]> git.neil.brown.name Git - history.git/commitdiff
Import 2.1.47 2.1.47
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:13:38 +0000 (15:13 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:13:38 +0000 (15:13 -0500)
79 files changed:
Documentation/Changes
Documentation/binfmt_misc.txt
Documentation/filesystems/romfs.txt
Documentation/filesystems/smbfs.txt
Makefile
arch/alpha/config.in
arch/i386/boot/setup.S
arch/i386/config.in
arch/i386/defconfig
arch/i386/kernel/setup.c
arch/mips/config.in
arch/sparc64/kernel/sys32.S
arch/sparc64/kernel/sys_sparc32.c
drivers/Makefile
drivers/char/Config.in
drivers/char/Makefile
drivers/char/lp.c
drivers/char/rocket.c [new file with mode: 0644]
drivers/char/rocket_int.h [new file with mode: 0644]
drivers/char/tty_io.c
drivers/misc/BUGS-parport [new file with mode: 0644]
drivers/misc/Makefile [new file with mode: 0644]
drivers/misc/TODO-parport [new file with mode: 0644]
drivers/misc/parport_arc.c [new file with mode: 0644]
drivers/misc/parport_ieee1284.c [new file with mode: 0644]
drivers/misc/parport_init.c [new file with mode: 0644]
drivers/misc/parport_pc.c [new file with mode: 0644]
drivers/misc/parport_procfs.c [new file with mode: 0644]
drivers/misc/parport_share.c [new file with mode: 0644]
drivers/net/eexpress.c
drivers/pnp/BUGS-parport [deleted file]
drivers/pnp/Config.in
drivers/pnp/Makefile
drivers/pnp/TODO-parport [deleted file]
drivers/pnp/parport_init.c [deleted file]
drivers/pnp/parport_ll_io.h [deleted file]
drivers/pnp/parport_probe.c
drivers/pnp/parport_procfs.c [deleted file]
drivers/pnp/parport_share.c [deleted file]
fs/dcache.c
fs/fat/inode.c
fs/fat/misc.c
fs/hpfs/hpfs_fs.c
fs/inode.c
fs/lockd/svcsubs.c
fs/minix/bitmap.c
fs/minix/dir.c
fs/minix/file.c
fs/minix/namei.c
fs/msdos/namei.c
fs/namei.c
fs/nfs/write.c
fs/nfsd/export.c
fs/nfsd/lockd.c
fs/nfsd/nfs3proc.c
fs/nfsd/nfsctl.c
fs/nfsd/nfsfh.c
fs/nfsd/nfsproc.c
fs/nfsd/nfsxdr.c
fs/nfsd/vfs.c
fs/open.c
fs/romfs/inode.c
fs/super.c
include/linux/dcache.h
include/linux/fs.h
include/linux/lp.h
include/linux/msdos_fs.h
include/linux/nfsd/export.h
include/linux/nfsd/nfsd.h
include/linux/nfsd/nfsfh.h
include/linux/nfsd/syscall.h
include/linux/nfsd/xdr.h
include/linux/parport.h
include/linux/rocket.h [new file with mode: 0644]
include/linux/timer.h
init/main.c
kernel/ksyms.c
mm/filemap.c
mm/vmscan.c

index 143d90eecc2934988e2e655b94245a1e94f0fa54..2ee5af046600e5465ced91a70320f5994016e773 100644 (file)
@@ -29,7 +29,7 @@ English-language HTML version.
    Also, don't forget http://www.linuxhq.com/ for all your Linux kernel
 needs.
 
-Last updated: May 31, 1997.
+Last updated: July 22. 1997
 Current Author: Chris Ricker (gt1355b@prism.gatech.edu).
 
 Current Minimal Requirements
@@ -39,11 +39,11 @@ Current Minimal Requirements
 encountered a bug!  If you're unsure what version you're currently
 running, the suggested command should tell you.
 
-- Kernel modules        modutils-2.1.34         ; insmod -v
+- Kernel modules        modutils-2.1.42         ; insmod -V
 - Gnu C                 2.7.2.1                 ; gcc --version
 - Binutils              2.8.1.0.1               ; ld -v
-- Linux C Library       5.4.23                  ; ls -l /lib/libc.so.*
-- Dynamic Linker (ld.so) 1.8.5                   ; ldd -v
+- Linux C Library       5.4.33                  ; ls -l /lib/libc.so.*
+- Dynamic Linker (ld.so) 1.9.2                   ; ldd -v
 - Linux C++ Library     2.7.2.1                 ; ls -l /usr/lib/libg++.so.*
 - Procps                1.01                    ; ps --version
 - Procinfo               0.11                    ; procinfo -v
@@ -51,8 +51,9 @@ running, the suggested command should tell you.
 - Net-tools              1.41                    ; hostname -V
 - Loadlin                1.6a
 - Sh-utils               1.16                    ; expr --v
-- Autofs                 0.3.3                   ; automount --version
+- Autofs                 0.3.7                   ; automount --version
 - NFS                    0.4.21                  ; showmount --version
+- Bash                   1.14.7                  ; bash -version
 
 Upgrade notes
 *************
@@ -78,12 +79,12 @@ accordingly.
    For modules to work, you need to be running libc-5.4.x or greater.
 Since updates to libc fix other problems as well (security flaws, for
 example) and since 5.4.7 is missing a few needed symbols, try to get
-the latest 5.4.x you can.  Currently, libc-5.4.23 is the latest public
+the latest 5.4.x you can.  Currently, libc-5.4.33 is the latest public
 release.
 
    If you upgrade to libc-5.4.x, you also have to upgrade your dynamic
-linker (ld.so) to at least 1.8.5, or all sorts of weirdness will
-happen.  Actually, ld.so-1.8.2 and later will work, but 1.8.5 is widely
+linker (ld.so) to at least 1.9.2, or all sorts of weirdness will
+happen.  Actually, ld.so-1.8.2 and later will work, but 1.9.2 is widely
 available, so if you need to upgrade, use it.  If you get a release
 later than 1.8.5, avoid 1.8.10 as it introduces a few bugs that are
 fixed in later releases.
@@ -94,7 +95,8 @@ you're using NIS.
 Modules
 =======
 
-   You need to upgrade to modutils-2.1.34 for kernels 2.1.34 and later.
+   You need to upgrade to modutils-2.1.42 for kernels 2.1.42 and later.
+This version will also work with 2.0.x kernels.
 
 Binutils
 ========
@@ -146,7 +148,8 @@ Memory
 many memory utils, which have to be upgraded.  Get the new procinfo and
 procps (which, AFAIK, is not yet available) to fix this.  Until you
 upgrade, programs which read /proc/meminfo will seg-fault or give an
-error.
+error.  There is an unofficial update to 1.12.2 available that fixes
+most problems.
 
 Mount and network file systems
 ==============================
@@ -167,7 +170,7 @@ DOSEMU
 ======
 
    A new "stable" version of DOSEMU is available for 2.1.x kernels.
-Upgrade to 0.66.1 or later.
+Upgrade to 0.66.7 or later.
 
 Loadlin
 =======
@@ -192,6 +195,13 @@ parallel port may no longer be where you expect it; for example, LPT1
 /dev/lp0 with the new Plug-and-Play driver.  If printing breaks with
 the new driver, try checking your lpd configuration.
 
+Bash
+====
+
+   Old versions of Bash fail to properly handle symlinks, which can
+cause problems when compiling modules.  Upgrade to at least 1.14 to fix
+this problem.
+
 Where to get the files
 **********************
 
@@ -218,12 +228,12 @@ ftp://sunsite.unc.edu/pub/Linux/GCC/release.gcc-2.7.2.1
 Linux C Library
 ===============
 
-The 5.4.23 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.23.bin.tar.gz
-ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.23.bin.tar.gz
-Installation notes for 5.4.23:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.23
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.23
+The 5.4.33 release:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.33.bin.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.33.bin.tar.gz
+Installation notes for 5.4.33:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.33
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.33
 
 Linux C++ Library
 =================
@@ -238,16 +248,16 @@ ftp://sunsite.unc.edu/pub/Linux/GCC/release.libg++-2.7.2.1
 Dynamic Linker
 ==============
 
-The 1.8.5 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/ld.so-1.8.5.tar.gz
-ftp://sunsite.unc.edu/pub/Linux/GCC/ld.so-1.8.5.tar.gz
+The 1.9.2 release:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/ld.so-1.9.2.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/GCC/ld.so-1.9.2.tar.gz
 
 Modules utilities
 =================
 
-The 2.1.34 release:
-ftp://ftp.redhat.com/pub/alphabits/modutils-2.1.34.tar.gz
-ftp://ftp.kernel.org/pub/linux/kernel/v2.1/modutils-2.1.34.tar.gz
+The 2.1.42 release:
+ftp://ftp.redhat.com/pub/alphabits/modutils-2.1.42.tar.gz
+ftp://ftp.kernel.org/pub/linux/kernel/v2.1/modutils-2.1.42.tar.gz
 
 Procps utilities
 ================
@@ -255,6 +265,8 @@ Procps utilities
 The 1.01 release:
 ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.01.tar.gz
 ftp://sunsite.unc.edu/pub/Linux/system/status/ps/procps-1.01.tgz
+The unofficial 1.12.2 release:
+ftp://ftp.debian.org/pub/debian/hamm/hamm/source/base/procps_1.12.2.tar.gz
 
 Procinfo utilities
 ==================
@@ -278,9 +290,9 @@ ftp://ftp.redhat.com/pub/redhat/redhat-4.0/updates/i386/rpm-devel-2.2.7-1.sparc.
 DOSEMU
 ======
 
-The 0.66.1 release:
-ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/dosemu0.66.1.tgz
-ftp://sunsite.unc.edu/pub/Linux/system/emulators/dosemu0.66.1.tgz
+The 0.66.7 release:
+ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/dosemu0.66.7.tgz
+ftp://sunsite.unc.edu/pub/Linux/system/emulators/dosemu0.66.7.tgz
 
 Loadlin
 =======
@@ -305,8 +317,8 @@ ftp://ftp.win.tue.nl/pub/linux/util/mount-2.6g.tar.gz
 Autofs
 ======
 
-The 0.3.3 release:
-ftp://ftp.kernel.org/pub/linux/daemons/autofs/autofs-0.3.3.tar.gz
+The 0.3.7 release:
+ftp://ftp.kernel.org/pub/linux/daemons/autofs/autofs-0.3.7.tar.gz
 
 NFS
 ===
@@ -328,6 +340,12 @@ Ypbind
 The 3.2 release:
 ftp://ftp.uni-paderborn.de/pub/linux/local/yp/ypbind-3.2.tar.gz
 
+Bash
+====
+
+The 1.14.7 release:
+ftp://prep.ai.mit.edu/pub/gnu/bash-1.14.7.tar.gz
+
 Other Info
 ==========
 
@@ -335,13 +353,20 @@ Other Info
 favorite local linux mirror.  If you can, please get them from a closer
 site before checking sunsite.
 
-   Also, for those of you running Red Hat (or RPM on a different
+   You may also want to check for updated versions of this software in a
+package format for the distribution you use.
+
+   For those of you running Red Hat (or RPM on a different
 distribution), most of these are available in RPM format.  Check around
 your favorite Red Hat mirror site before installing the non-RPM
 version.  Remember, you might need to use the -force option to get the
 upgrade to install.  ftp://ftp.redhat.com/pub/contrib/ will have almost
 everything you need.
 
+   Those of you running Debian (or a different distribution that
+supports .deb packages) can look in the "unstable" and
+"project/experimental" directories of your favorite Debian mirror.
+
    For others, David Bourgin has put together a package of everything
 necessary to quickly and easily upgrade to 2.1.x.  See
 ftp://ftp.wsc.com/pub/freeware/linux/update.linux/kernel-v2.1.x/ for
index aef3b271ae4a6df23d2264869eae14da943bdc5d..35eec12846ae48c9e83c73557f1b06e8100c9816 100644 (file)
@@ -69,20 +69,63 @@ or /proc/sys/fs/binfmt_misc/status.
 Emulating binfmt_java:
 ======================
 
-To emulate binfmt_java the following register-strings are necessary
-(the first two for byte-compiled Java binaries, the third for applets
-contained in a html-file). Register exactly in this order!
-  ":Java:M::\xca\xfe\xba\xbe::/usr/local/java/bin/java:"
-  ":JavaC:e::class::/usr/local/java/bin/java:"
+To emulate binfmt_java the following register-strings could be used: 
+for compiled Java programs use
+  ":Java:M::\xca\xfe\xba\xbe::/usr/local/java/bin/javawrapper:"
+for simple applet support use
   ":Applet:E::html::/usr/local/java/bin/appletviewer:"
+for more selective applet support (like binfmt_java) use
+  ":Applet:M::\<\!--applet::/usr/local/java/bin/appletviewer:"
+
+Note, that for the more selective applet support you have to modify
+existing html-files to contain <!--applet--> in the first line to
+let this work!
+
+For the compiled Java programs you need a wrapper script like the
+following (this is because Java is broken in case of the filename
+handling):
+
+====================== Cut here ===================
+#!/bin/bash
+# /usr/local/java/bin/javawrapper - the wrapper for binfmt_misc/java
+CLASS=$1
+
+# if classname is a link, we follow it (this could be done easier - how?)
+if [ -L "$1" ] ; then
+       CLASS=`ls --color=no -l $1 | tr -s '\t ' '  ' | cut -d ' ' -f 11` 
+fi
+CLASSN=`basename $CLASS | sed s/\.class$//`
+CLASSP=`dirname $CLASS`
+
+FOO=$PATH
+PATH=$CLASSPATH
+if [ -z "`type -p -a $CLASSN.class`" ] ; then
+       # class is not in CLASSPATH
+       if [ -e "$CLASSP/$CLASSN.class" ] ; then
+               # append dir of class to CLASSPATH
+               if [ -z "${CLASSPATH}" ] ; then
+                       export CLASSPATH=$CLASSP
+               else
+                       export CLASSPATH=$CLASSP:$CLASSPATH
+               fi
+       else
+               # uh! now we would have to create a symbolic link - really
+               # ugly, i.e. print a message that one has to change the setup
+               echo "Hey! This is not a good setup to run $1 !"
+               exit 1
+       fi
+fi
+PATH=$FOO
+
+shift
+/usr/local/java/bin/java $CLASSN $@
+====================== Cut here ===================
+
+To add a Java program to your path best put a symbolic link to the main
+.class file into /usr/bin (or another place you like) omitting the .class
+extension. The directory containing the original .class file will be
+added to your CLASSPATH during execution.
 
-To add a Java-executable to your path you can either make a symbolic
-link to the .class file elsewhere in your path (cut the .class-extension
-in the destination name for convenience) or add the directory of your
-.class files to your PATH environment. In both cases, ensure that the
-.class files are in your CLASSPATH environment!
-
-This is sort of ugly - Javas filename handling is just broken.
 
 
 HINTS:
index 5b8bafb465a41ac552eeedd1a7f544babc02f7d9..32a14e68bb378413a95f8911acb0d8446a8f7ad9 100644 (file)
@@ -8,19 +8,18 @@ similar feature, and even the possibility of a small kernel, with a
 file system which doesn't take up useful memory from the router
 functions in the basement of your office.
 
-For comparison, both the older minix and xiafs filesystems (compiled
-as module) need more than 20000 bytes, while romfs is less than a
-page, about 4000 bytes (assuming ix86 code).  Under the same
-conditions, the msdos filesystem would need about 30K (and does not
-support device nodes or symlinks), while the nfs module with nfsroot
-is about 57K.  Furthermore, as a bit unfair comparison, an actual
-rescue disk used up 3202 blocks with ext2, while with romfs, it needed
-3079 blocks.
+For comparison, both the older minix and xiafs (the latter is now
+defunct) filesystems, compiled as module need more than 20000 bytes,
+while romfs is less than a page, about 4000 bytes (assuming i586
+code).  Under the same conditions, the msdos filesystem would need
+about 30K (and does not support device nodes or symlinks), while the
+nfs module with nfsroot is about 57K.  Furthermore, as a bit unfair
+comparison, an actual rescue disk used up 3202 blocks with ext2, while
+with romfs, it needed 3079 blocks.
 
 To create such a file system, you'll need a user program named
-genromfs.  It is (or will be shortly) available via ftp on
-sunsite.unc.edu and its mirrors, in the /pub/Linux/system/Filesystems/
-directory.
+genromfs.  It is available via anonymous ftp on sunsite.unc.edu and
+its mirrors, in the /pub/Linux/system/recovery/ directory.
 
 As the name suggests, romfs could be also used (space-efficiently) on
 various read-only medias, like (E)EPROM disks if someone will have the
@@ -61,7 +60,7 @@ offset            content
        +---+---+---+---+
   0    | - | r | o | m |  \
        +---+---+---+---+       The ASCII representation of those bytes
-  4    | 1 | f | s | - |  /    (i.e. "-rom1fs-"
+  4    | 1 | f | s | - |  /    (i.e. "-rom1fs-")
        +---+---+---+---+
   8    |   full size   |       The number of accessible bytes in this fs.
        +---+---+---+---+
@@ -77,7 +76,7 @@ Every multi byte value (32 bit words, I'll use the longwords term from
 now on) must be in big endian order.
 
 The first eight bytes identify the filesystem, even for the casual
-reader.  After that in the 3rd longword, it contains the number of
+inspector.  After that, in the 3rd longword, it contains the number of
 bytes accessible from the start of this filesystem.  The 4th longword
 is the checksum of the first 512 bytes (or the number of bytes
 accessible, whichever is smallest).  The applied algorithm is the same
@@ -101,7 +100,7 @@ offset          content
  12    |   checksum    |       Covering the meta data, including the file
        +---+---+---+---+         name, and padding
  16    | file name     |       The zero terminated name of the file,
-       :               :       padded to 16 byte boundary.
+       :               :       padded to 16 byte boundary
        +---+---+---+---+
  xx    | file data     |
        :               :
@@ -112,9 +111,10 @@ bits are used for the mode information.  Bits 0..2 specify the type of
 the file; while bit 4 shows if the file is executable or not.  The
 permissions are assumed to be world readable, if this bit is not set,
 and world executable if it is; except the character and block devices,
-they are readable only for the owner.  The owner of every file is user
-and group 0, this should never be a problem for the intended use.  The
-mapping of the 8 possible values to file types is the following:
+they are never accessible for other than owner.  The owner of every
+file is user and group 0, this should never be a problem for the
+intended use.  The mapping of the 8 possible values to file types is
+the following:
 
          mapping               spec.info means
  0     hard link       link destination [file header]
@@ -128,14 +128,14 @@ mapping of the 8 possible values to file types is the following:
 
 Note that hard links are specifically marked in this filesystem, but
 they will behave as you can expect (i.e. share the inode number).
-Note also that your responsibility to not create hard link loops, and
-creating all the . and .. links for directories.  This is normally
-done correctly by the genromfs program.  Please refrain from using the
-executable bits on the socket and fifo special files, they may have
-other uses in the future.  Additionally, please remember that only
-regular files, and symlinks are supposed to have a nonzero size field;
-they contain the number of bytes available directly after the (padded)
-file name.
+Note also that it is your responsibility to not create hard link
+loops, and creating all the . and .. links for directories.  This is
+normally done correctly by the genromfs program.  Please refrain from
+using the executable bits for special purposes on the socket and fifo
+special files, they may have other uses in the future.  Additionally,
+please remember that only regular files, and symlinks are supposed to
+have a nonzero size field; they contain the number of bytes available
+directly after the (padded) file name.
 
 Another thing to note is that romfs works on file headers and data
 aligned to 16 byte boundaries, but most hardware devices and the block
@@ -145,8 +145,44 @@ padded to an 1024 byte boundary.
 
 If you have any problems or suggestions concerning this file system,
 please contact me.  However, think twice before wanting me to add
-features and code, because the primary advantage of this file system
-is the small code.
+features and code, because the primary and most important advantage of
+this file system is the small code.  On the other hand, don't be
+alarmed, I'm not getting that much romfs related mail.  Now I can
+understand why Avery wrote poems in the arcnet docs to get some more
+feedback. :)
+
+romfs has also a mailing list, and to date, it hasn't received any
+traffic, so you are welcome to join it to discuss your ideas. :)
+
+It's run by ezmlm, so you can subscribe to it by sending a message
+to romfs-subscribe@shadow.banki.hu, the content is irrelevant.
+
+Pending issues:
+
+- Permissions and owner information are pretty essential features of a
+Un*x like system, but romfs does not provide the full possibilities.
+I have never found this limiting, but others might.
+
+- The file system is read only, so it can be very small, but in case
+one would want to write _anything_ to a file system, he still needs
+a writable file system, thus negating the size advantages.  Possible
+solutions: implement write access as a compile-time option, or a new,
+similarly small writable filesystem for ram disks.
+
+- Since the files are only required to have alignment on a 16 byte
+boundary, it is currently possibly suboptimal to read or execute files
+from the filesystem.  It might be resolved by reordering file data to
+have most of it (i.e. except the start and the end) laying at "natural"
+boundaries, thus it would be possible to directly map a big portion of
+the file contents to the mm subsystem.
+
+- Compression might be an useful feature, but memory is quite a
+limiting factor in my eyes.
+
+- Where it is used?
+
+- Does it work on other architectures than intel and motorola?
+
 
 Have fun,
 Janos Farkas <chexum@shadow.banki.hu>
index ffef2d8141c9c16162240dee13083392c12fbbb1..b707757e2e9ea7228038423c3c6c5ea0666627aa 100644 (file)
@@ -9,5 +9,5 @@ netbios name or share.
 
 To use smbfs, you need a special mount program, which can be found in
 the ksmbfs package, found on
-sunsite.unc.edu:/pub/Linux/system/Filesystems/smbfs.
 
+       sunsite.unc.edu:/pub/Linux/system/filesystems/smbfs
index 4fcf1a91586c14086f5bf916e47e070fec634066..bc5a242251c604770575db59d1026bab5e9fc1cb 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 46
+SUBLEVEL = 47
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
@@ -119,7 +119,7 @@ FILESYSTEMS =fs/filesystems.a
 NETWORKS       =net/network.a
 DRIVERS                =drivers/block/block.a \
                 drivers/char/char.a \
-                drivers/pnp/pnp.a
+                drivers/misc/misc.a
 LIBS           =$(TOPDIR)/lib/lib.a
 SUBDIRS                =kernel drivers mm fs net ipc lib
 
@@ -149,6 +149,10 @@ ifdef CONFIG_SBUS
 DRIVERS := $(DRIVERS) drivers/sbus/sbus.a
 endif
 
+ifdef CONFIG_PNP
+DRIVERS := $(DRIVERS) drivers/pnp/pnp.a
+endif
+
 include arch/$(ARCH)/Makefile
 
 ifdef SMP
index 774686ebdac7e6d226d538e0769e5505e8217fa9..6ac472f084eb3433f214d3a0763030c14a3be065 100644 (file)
@@ -99,7 +99,10 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
   tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA
 fi
 tristate 'Kernel support for Linux/Intel ELF binaries' CONFIG_BINFMT_EM86
-tristate 'Parallel port support' CONFIG_PNP_PARPORT
+tristate 'Parallel port support' CONFIG_PARPORT
+if [ "$CONFIG_PARPORT" != "n" ]; then
+  dep_tristate '  PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
+fi
 endmenu
 
 source drivers/pnp/Config.in
index 32d852efceb81e35940c8119d7b2e234bde66a82..7553ce94316288b87c28655a8c018cb8b313a843 100644 (file)
 !
 ! Video handling moved to video.S by Martin Mares, March 1996
 ! <mj@k332.feld.cvut.cz>
+!
+! Extended memory detection scheme retwiddled by orc@pell.chi.il.us (david
+! parsons) to avoid loadlin confusion, July 1997
+!
 
 #define __ASSEMBLY__
 #include <linux/config.h>
@@ -241,53 +245,39 @@ loader_panic_mess:
 loader_ok:
 ! Get memory size (extended mem, kB)
 
-#ifdef STANDARD_MEMORY_BIOS_CALL
-       mov     ah,#0x88
-       int     0x15
-       mov     [2],ax
-#else
-       push    ax
-       push    cx
-       push    dx
-                               ! which bootloader ?
-       seg cs
-       mov     al,byte ptr type_of_loader
-       and     al,#0xf0
-       cmp     al,#0x10
-       jne     try_xe801       ! not Loadlin
-       seg cs
-       cmp     byte ptr type_of_loader,#0x16
-       jbe     oldstylemem     ! Loadlin <= 1.6 don't like that
-try_xe801:
-       mov     ax,#0xe801
-       int     0x15
-       jc      oldstylemem
+#ifndef STANDARD_MEMORY_BIOS_CALL
+       push    ebx
+
+        xor     ebx,ebx                ! preload new memory slot with 0k
+        mov    [0x1e0], ebx
+
+        mov     ax,#0xe801
+       int     0x15
+       jc      oldstylemem
 
-! memory size is (ax+(64*bx)) * 1024; we store bx+(ax/64)
+! memory size is in 1k chunksizes, to avoid confusing loadlin.
+! we store the 0xe801 memory size in a completely different place,
+! because it will most likely be longer than 16 bits.
+! (use 1e0 because that's what Larry Augustine uses in his
+! alternative new memory detection scheme, and it's sensible
+! to write everything into the same place.)
 
-       mov     [2],bx          ! store extended memory size
-       xor     dx,dx
-       mov     cx,#64          ! convert lower memory size from K into
-       div     cx              ! 64k chunks.
+       and     ebx, #0xffff    ! clear sign extend
+       shl     ebx, 6          ! and go from 64k to 1k chunks
+       mov     [0x1e0],ebx     ! store extended memory size
 
-       add     [2],ax          ! add lower memory into total size.
-       jmp     gotmem
+       and     eax, #0xffff    ! clear sign extend
+       add     [0x1e0],eax     ! and add lower memory into total size.
+  
+       ! and fall into the old memory detection code to populate the
+       ! compatability slot.
 
+       pop     ebx
 oldstylemem:
+#endif
        mov     ah,#0x88
        int     0x15
-       or      ax,ax           ! some BIOSes report ZERO for 64meg
-       mov     word ptr [2],#0x400
-       jz      gotmem
-       mov     cx,#64          ! got memory size in kbytes, so we need to
-       xor     dx,dx           ! adjust to 64k chunks for the system.
-       div     cx
        mov     [2],ax
-gotmem:
-       pop     dx
-       pop     cx
-       pop     ax
-#endif
 
 ! Set the keyboard repeat rate to the max
 
index 1612b614f193b6cdbd616c5a97ef92e7a6dc54dc..887407c6170aa0442e50488141186c077147fb47 100644 (file)
@@ -46,7 +46,10 @@ choice 'Processor type' \
         PPro           CONFIG_M686" Pentium
 bool 'Video mode selection support' CONFIG_VIDEO_SELECT
 
-tristate 'Parallel port support' CONFIG_PNP_PARPORT
+tristate 'Parallel port support' CONFIG_PARPORT
+if [ "$CONFIG_PARPORT" != "n" ]; then
+  dep_tristate '  PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
+fi
 
 endmenu
 
index 6e7700c9173725570e49a0f91718d2b14991c67d..430d6b971fa8e4964a918ea53cb00091eec60923 100644 (file)
@@ -31,7 +31,7 @@ CONFIG_BINFMT_MISC=y
 # CONFIG_M586 is not set
 CONFIG_M686=y
 # CONFIG_VIDEO_SELECT is not set
-# CONFIG_PNP_PARPORT is not set
+# CONFIG_PARPORT is not set
 
 #
 # Plug and Play support
index 4dd8edf76f0b244c864d7343c3cf3dffa83b5033..d4800f987d4584b186cc0d591420ac9542a74afa 100644 (file)
@@ -92,6 +92,9 @@ extern char empty_zero_page[PAGE_SIZE];
  */
 #define PARAM  empty_zero_page
 #define EXT_MEM_K (*(unsigned short *) (PARAM+2))
+#ifndef STANDARD_MEMORY_BIOS_CALL
+#define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0))
+#endif
 #ifdef CONFIG_APM
 #define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64))
 #endif
@@ -120,6 +123,7 @@ __initfunc(void setup_arch(char **cmdline_p,
        unsigned long * memory_start_p, unsigned long * memory_end_p))
 {
        unsigned long memory_start, memory_end;
+       unsigned long memory_alt_end;
        char c = ' ', *to = command_line, *from = COMMAND_LINE;
        int len = 0;
        static unsigned char smptrap=0;
@@ -143,10 +147,15 @@ __initfunc(void setup_arch(char **cmdline_p,
                BIOS_revision = SYS_DESC_TABLE.table[2];
        }
        aux_device_present = AUX_DEVICE_INFO;
-#ifdef STANDARD_MEMORY_BIOS_CALL
        memory_end = (1<<20) + (EXT_MEM_K<<10);
-#else
-       memory_end = (1<<20) + (EXT_MEM_K*64L*1024L);   /* 64kb chunks */
+#ifndef STANDARD_MEMORY_BIOS_CALL
+       memory_alt_end = (1<<20) + (ALT_MEM_K<<10);
+       if (memory_alt_end > memory_end) {
+           printk("Memory: sized by int13 0e801h\n");
+           memory_end = memory_alt_end;
+       }
+       else
+           printk("Memory: sized by int13 088h\n");
 #endif
        memory_end &= PAGE_MASK;
 #ifdef CONFIG_BLK_DEV_RAM
index f9f93542f69857c7837467bf7bb88b926c33b56f..78ab9c24538e702f82af49cf5a39619c345cad25 100644 (file)
@@ -80,7 +80,7 @@ bool 'System V IPC' CONFIG_SYSVIPC
 bool 'Sysctl support' CONFIG_SYSCTL
 
 if [ "$CONFIG_SGI" != "y" ]; then
-  tristate 'Parallel port support' CONFIG_PNP_PARPORT
+  tristate 'Parallel port support' CONFIG_PARPORT
 fi
 
 endmenu
index ed14462485caad805845df6c2fac3036a277c50d..8ee8d0a3509e90ac12c6c8c2518832c3dc4fc770 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: sys32.S,v 1.1 1997/06/29 03:38:56 davem Exp $
+/* $Id: sys32.S,v 1.2 1997/07/20 09:18:47 davem Exp $
  * sys32.S: I-cache tricks for 32-bit compatability layer simple
  *          conversions.
  *
@@ -306,7 +306,7 @@ sys32_settimeofday:
        .globl          sys32_sched_setparam, sys32_sched_getparam, sys32_signal
        .globl          sys32_reboot, sys32_acct, sys32_newuname, sys32_olduname
        .globl          sys32_sethostname, sys32_gethostname, sys32_setdomainname
-       .globl          sys32_time, sys32_swapoff, sys32_swapon, sys32_nfsservctl
+       .globl          sys32_time, sys32_swapoff, sys32_swapon
        .globl          sys32_create_module, sys32_init_module, sys32_delete_module
 sys32_bdflush:
        sra             %o1, 0, %o1
@@ -419,9 +419,3 @@ sys32_swapon:
        mov             %o7, %g1
        call            sys_swapon
         mov            %g1, %o7
-sys32_nfsservctl:
-       srl             %o1, 0, %o1
-       mov             %o7, %g1
-       srl             %o2, 0, %o2
-       call            sys_nfsservctl
-        mov            %g1, %o7
index 1f607da9815df1e5f257e7b7520165aaf86dfe9a..b6ca9448c28e6858b47f1c2d9a21794ae8869001 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.43 1997/07/17 02:20:45 davem Exp $
+/* $Id: sys_sparc32.c,v 1.44 1997/07/20 09:18:47 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
 #include <linux/quota.h>
 #include <linux/file.h>
 #include <linux/module.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr.h>
+#include <linux/nfsd/syscall.h>
+#include <linux/module.h>
 
 #include <asm/types.h>
 #include <asm/poll.h>
@@ -2519,3 +2525,271 @@ sys_get_kernel_syms(struct kernel_sym *table)
 }
 
 #endif  /* CONFIG_MODULES */
+
+/* Stuff for NFS server syscalls... */
+struct nfsctl_svc32 {
+       u16                     svc32_port;
+       s32                     svc32_nthreads;
+};
+
+struct nfsctl_client32 {
+       s8                      cl32_ident[NFSCLNT_IDMAX+1];
+       s32                     cl32_naddr;
+       struct in_addr          cl32_addrlist[NFSCLNT_ADDRMAX];
+       s32                     cl32_fhkeytype;
+       s32                     cl32_fhkeylen;
+       u8                      cl32_fhkey[NFSCLNT_KEYMAX];
+};
+
+struct nfsctl_export32 {
+       s8                      ex32_client[NFSCLNT_IDMAX+1];
+       s8                      ex32_path[NFS_MAXPATHLEN+1];
+       __kernel_dev_t32        ex32_dev;
+       __kernel_ino_t32        ex32_ino;
+       s32                     ex32_flags;
+       __kernel_uid_t32        ex32_anon_uid;
+       __kernel_gid_t32        ex32_anon_gid;
+};
+
+struct nfsctl_uidmap32 {
+       u32                     ug32_ident;   /* char * */
+       __kernel_uid_t32        ug32_uidbase;
+       s32                     ug32_uidlen;
+       u32                     ug32_udimap;  /* uid_t * */
+       __kernel_uid_t32        ug32_gidbase;
+       s32                     ug32_gidlen;
+       u32                     ug32_gdimap;  /* gid_t * */
+};
+
+struct nfsctl_fhparm32 {
+       struct sockaddr         gf32_addr;
+       __kernel_dev_t32        gf32_dev;
+       __kernel_ino_t32        gf32_ino;
+       s32                     gf32_version;
+};
+
+struct nfsctl_arg32 {
+       s32                     ca32_version;   /* safeguard */
+       union {
+               struct nfsctl_svc32     u32_svc;
+               struct nfsctl_client32  u32_client;
+               struct nfsctl_export32  u32_export;
+               struct nfsctl_uidmap32  u32_umap;
+               struct nfsctl_fhparm32  u32_getfh;
+               u32                     u32_debug;
+       } u;
+#define ca32_svc       u.u32_svc
+#define ca32_client    u.u32_client
+#define ca32_export    u.u32_export
+#define ca32_umap      u.u32_umap
+#define ca32_getfh     u.u32_getfh
+#define ca32_authd     u.u32_authd
+#define ca32_debug     u.u32_debug
+};
+
+union nfsctl_res32 {
+       struct knfs_fh          cr32_getfh;
+       u32                     cr32_debug;
+};
+
+static int nfs_svc32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+       if(__get_user(karg->ca_version, &arg32->ca32_version)                   ||
+          __get_user(karg->ca_svc.svc_port, &arg32->ca32_svc.svc32_port)       ||
+          __get_user(karg->ca_svc.svc_nthreads, &arg32->ca32_svc.svc32_nthreads))
+               return -EFAULT;
+       return 0;
+}
+
+static int nfs_clnt32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+       if(__get_user(karg->ca_version, &arg32->ca32_version)                   ||
+          copy_from_user(&karg->ca_client.cl_ident[0],
+                         &arg32->ca32_client.cl32_ident[0],
+                         NFSCLNT_IDMAX)                                        ||
+          __get_user(karg->ca_client.cl_naddr, &arg32->ca32_client.cl32_naddr) ||
+          copy_from_user(&karg->ca_client.cl_addrlist[0],
+                         &arg32->ca32_client.cl32_addrlist[0],
+                         (sizeof(struct in_addr) * NFSCLNT_ADDRMAX))           ||
+          __get_user(karg->ca_client.cl_fhkeytype,
+                     &arg32->ca32_client.cl32_fhkeytype)                       ||
+          __get_user(karg->ca_client.cl_fhkeylen,
+                     &arg32->ca32_client.cl32_fhkeylen)                        ||
+          copy_from_user(&karg->ca_client.cl_fhkey[0],
+                         &arg32->ca32_client.cl32_fhkey[0],
+                         NFSCLNT_KEYMAX))
+               return -EFAULT;
+       return 0;
+}
+
+static int nfs_exp32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+       if(__get_user(karg->ca_version, &arg32->ca32_version)                   ||
+          copy_from_user(&karg->ca_export.ex_client[0],
+                         &arg32->ca32_export.ex32_client[0],
+                         NFSCLNT_IDMAX)                                        ||
+          copy_from_user(&karg->ca_export.ex_path[0],
+                         &arg32->ca32_export.ex32_path[0],
+                         NFS_MAXPATHLEN)                                       ||
+          __get_user(karg->ca_export.ex_dev,
+                     &arg32->ca32_export.ex32_dev)                             ||
+          __get_user(karg->ca_export.ex_ino,
+                     &arg32->ca32_export.ex32_ino)                             ||
+          __get_user(karg->ca_export.ex_flags,
+                     &arg32->ca32_export.ex32_flags)                           ||
+          __get_user(karg->ca_export.ex_anon_uid,
+                     &arg32->ca32_export.ex32_anon_uid)                        ||
+          __get_user(karg->ca_export.ex_anon_gid,
+                     &arg32->ca32_export.ex32_anon_gid))
+               return -EFAULT;
+       return 0;
+}
+
+static int nfs_uud32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+       u32 uaddr;
+       int i;
+
+       memset(karg, 0, sizeof(*karg));
+       if(__get_user(karg->ca_version, &arg32->ca32_version))
+               return -EFAULT;
+       karg->ca_umap.ug_ident = (char *)get_free_page(GFP_USER);
+       if(!karg->ca_umap.ug_ident)
+               return -ENOMEM;
+       if(__get_user(uaddr, &arg32->ca32_umap.ug32_ident))
+               return -EFAULT;
+       if(strncpy_from_user(karg->ca_umap.ug_ident,
+                            (char *)A(uaddr), PAGE_SIZE) <= 0)
+               return -EFAULT;
+       if(__get_user(karg->ca_umap.ug_uidbase,
+                     &arg32->ca32_umap.ug32_uidbase)           ||
+          __get_user(karg->ca_umap.ug_uidlen,
+                     &arg32->ca32_umap.ug32_uidlen)            ||
+          __get_user(uaddr, &arg32->ca32_umap.ug32_udimap))
+               return -EFAULT;
+       karg->ca_umap.ug_udimap = kmalloc((sizeof(uid_t) * karg->ca_umap.ug_uidlen),
+                                         GFP_USER);
+       if(!karg->ca_umap.ug_udimap)
+               return -EFAULT;
+       for(i = 0; i < karg->ca_umap.ug_uidlen; i++)
+               if(__get_user(karg->ca_umap.ug_udimap[i],
+                             &(((__kernel_uid_t32 *)A(uaddr))[i])))
+                       return -EFAULT;
+       if(__get_user(karg->ca_umap.ug_gidbase,
+                     &arg32->ca32_umap.ug32_gidbase)           ||
+          __get_user(karg->ca_umap.ug_uidlen,
+                     &arg32->ca32_umap.ug32_gidlen)            ||
+          __get_user(uaddr, &arg32->ca32_umap.ug32_gdimap))
+               return -EFAULT;
+       karg->ca_umap.ug_gdimap = kmalloc((sizeof(gid_t) * karg->ca_umap.ug_uidlen),
+                                         GFP_USER);
+       if(!karg->ca_umap.ug_gdimap)
+               return -EFAULT;
+       for(i = 0; i < karg->ca_umap.ug_gidlen; i++)
+               if(__get_user(karg->ca_umap.ug_gdimap[i],
+                             &(((__kernel_gid_t32 *)A(uaddr))[i])))
+                       return -EFAULT;
+
+       /* Success! */
+       return 0;
+}
+
+static int nfs_getfh32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32)
+{
+       if(__get_user(karg->ca_version, &arg32->ca32_version)   ||
+          copy_from_user(&karg->ca_getfh.gf_addr,
+                         &arg32->ca32_getfh.gf32_addr,
+                         (sizeof(struct sockaddr)))            ||
+          __get_user(karg->ca_getfh.gf_dev,
+                     &arg32->ca32_getfh.gf32_dev)              ||
+          __get_user(karg->ca_getfh.gf_ino,
+                     &arg32->ca32_getfh.gf32_ino)              ||
+          __get_user(karg->ca_getfh.gf_version,
+                     &arg32->ca32_getfh.gf32_version))
+               return -EFAULT;
+       return 0;
+}
+
+static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res32)
+{
+       if(copy_to_user(&res32->cr32_getfh,
+                       &kres->cr_getfh,
+                       sizeof(res32->cr32_getfh))              ||
+          __put_user(kres->cr_debug, &res32->cr32_debug))
+               return -EFAULT;
+       return 0;
+}
+
+extern asmlinkage int sys_nfsservctl(int cmd,
+                                    struct nfsctl_arg *arg,
+                                    union nfsctl_res *resp);
+
+int asmlinkage sys32_nfsservctl(int cmd, u32 u_argp, u32 u_resp)
+{
+       struct nfsctl_arg32 *arg32 = (struct nfsctl_arg32 *)A(u_argp);
+       union nfsctl_res32 *res32 = (union nfsctl_res32 *)A(u_resp);
+       struct nfsctl_arg *karg = NULL;
+       union nfsctl_res *kres = NULL;
+       unsigned long oldfs;
+       int err;
+
+       karg = kmalloc(sizeof(*karg), GFP_USER);
+       if(!karg)
+               return -ENOMEM;
+       if(res32) {
+               kres = kmalloc(sizeof(*kres), GFP_USER);
+               if(!kres) {
+                       kfree(karg);
+                       return -ENOMEM;
+               }
+       }
+       switch(cmd) {
+       case NFSCTL_SVC:
+               err = nfs_svc32_trans(karg, arg32);
+               break;
+       case NFSCTL_ADDCLIENT:
+               err = nfs_clnt32_trans(karg, arg32);
+               break;
+       case NFSCTL_DELCLIENT:
+               err = nfs_clnt32_trans(karg, arg32);
+               break;
+       case NFSCTL_EXPORT:
+               err = nfs_exp32_trans(karg, arg32);
+               break;
+       /* This one is unimplemented, be we're ready for it. */
+       case NFSCTL_UGIDUPDATE:
+               err = nfs_uud32_trans(karg, arg32);
+               break;
+       case NFSCTL_GETFH:
+               err = nfs_getfh32_trans(karg, arg32);
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+       if(err)
+               goto done;
+       oldfs = get_fs();
+       set_fs(KERNEL_DS);
+       err = sys_nfsservctl(cmd, karg, kres);
+       set_fs(oldfs);
+
+       if(!err && cmd == NFSCTL_GETFH)
+               err = nfs_getfh32_res_trans(kres, res32);
+
+done:
+       if(karg) {
+               if(cmd == NFSCTL_UGIDUPDATE) {
+                       if(karg->ca_umap.ug_ident)
+                               kfree(karg->ca_umap.ug_ident);
+                       if(karg->ca_umap.ug_udimap)
+                               kfree(karg->ca_umap.ug_udimap);
+                       if(karg->ca_umap.ug_gdimap)
+                               kfree(karg->ca_umap.ug_gdimap);
+               }
+               kfree(karg);
+       }
+       if(kres)
+               kfree(kres);
+       return err;
+}
index 69299d7cde20d884ccf86c7835a8780491fbe177..34b5662d40670300cd03264816fbccf03a0473f9 100644 (file)
@@ -7,9 +7,9 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
-SUB_DIRS     := block char net pnp #streams
+SUB_DIRS     := block char net misc #streams
 MOD_SUB_DIRS := $(SUB_DIRS) sbus
-ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn pnp
+ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus sound cdrom isdn misc pnp
 
 ifdef CONFIG_PCI
 SUB_DIRS += pci
@@ -38,6 +38,15 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_PNP),y)
+SUB_DIRS += pnp
+MOD_SUB_DIRS += pnp
+else
+  ifeq ($(CONFIG_PNP),m)
+  MOD_SUB_DIRS += pnp
+  endif
+endif
+
 ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR),)
 SUB_DIRS += cdrom
 MOD_SUB_DIRS += cdrom
index c02b376e8a852d81577c61ba35bbc5b45a3524f5..0ef43d10144d4a498d1d3c9d64bcf4278aaa1e6b 100644 (file)
@@ -19,6 +19,7 @@ if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then
 fi
 bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD
 if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
+   tristate 'Comtrol Rocketport support' CONFIG_ROCKETPORT
    tristate 'Digiboard Intelligent Async Support' CONFIG_DIGIEPCA
    if [ "$CONFIG_DIGIEPCA" = "n" ]; then
       tristate 'Digiboard PC/Xx Support' CONFIG_DIGI
@@ -36,8 +37,8 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
      int '  FIFO trigger level' CONFIG_ESPSERIAL_TRIGGER_LEVEL 768
    fi
 fi
-if [ "$CONFIG_PNP_PARPORT" != "n" ]; then
-  dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PNP_PARPORT
+if [ "$CONFIG_PARPORT" != "n" ]; then
+  dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT
   if [ "$CONFIG_PRINTER" != "n" ]; then
     bool '  Support IEEE1284 status readback' CONFIG_PRINTER_READBACK
   fi
index 3e69d37588040df9641215539f00bb1729a1853d..bdc6ff892e221ec82fbc21a2d8548e641ee26c22 100644 (file)
@@ -59,6 +59,14 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_ROCKETPORT),y)
+L_OBJS += rocket.o
+else
+  ifeq ($(CONFIG_ROCKETPORT),m)
+  M_OBJS += rocket.o
+  endif
+endif
+
 ifeq ($(CONFIG_DIGI),y)
 L_OBJS += pcxx.o
 else
index 9f9e5a99276ed13ddceb385ce3b11f47c2b097df..caf5b5ccfa5656cf0307a7b5c3b9595054077425 100644 (file)
  * "lp=" command line parameters added by Grant Guenther, grant@torque.net
  * lp_read (Status readback) support added by Carsten Gross,
  *                                             carsten@sol.wohnheim.uni-ulm.de
+ * Support for parport by Philip Blundell <Philip.Blundell@pobox.com>
  */
 
+/* This driver is about due for a rewrite. */
+
 #include <linux/module.h>
 
 #include <linux/config.h>
@@ -23,7 +26,6 @@
 #include <linux/ioport.h>
 #include <linux/fcntl.h>
 #include <linux/delay.h>
-#include <linux/init.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <linux/parport.h>
 #include <linux/lp.h>
 
-/* the BIOS manuals say there can be up to 4 lpt devices
- * but I have not seen a board where the 4th address is listed
- * if you have different hardware change the table below
- * please let me know if you have different equipment
- * if you have more than 3 printers, remember to increase LP_NO
- */
+/* if you have more than 3 printers, remember to increase LP_NO */
 struct lp_struct lp_table[] =
 {
  {NULL, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0,
@@ -164,7 +161,7 @@ static inline int lp_char_interrupt(char lpchar, int minor)
 static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
        struct parport *pb = (struct parport *) dev_id;
-       struct ppd *pd = pb->cad;
+       struct pardevice *pd = pb->cad;
        struct lp_struct *lp_dev = (struct lp_struct *) pd->private;
 
        if (lp_dev->lp_wait_q)
@@ -501,8 +498,9 @@ static int lp_open(struct inode * inode, struct file * file)
 static int lp_release(struct inode * inode, struct file * file)
 {
        unsigned int minor = MINOR(inode->i_rdev);
+       unsigned int irq;
 
-       if (LP_IRQ(minor) > 0) {
+       if ((irq = LP_IRQ(minor))) {
                kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
                lp_table[minor].lp_buffer = NULL;
        }
@@ -630,7 +628,7 @@ MODULE_PARM(parport, "1-" __MODULE_STRING(LP_NO) "i");
 
 static int parport_ptr = 0;
 
-__initfunc(void lp_setup(char *str, int *ints))
+void lp_setup(char *str, int *ints)
 {
        /* Ugh. */
        if (!strncmp(str, "parport", 7)) {
@@ -678,23 +676,18 @@ static int inline lp_searchfor(int list[], int a)
        return 0;
 }
 
-__initfunc(int lp_init(void))
+int lp_init(void)
 {
        int count = 0;
        struct parport *pb;
   
-       if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
-               printk("lp: unable to get major %d\n", LP_MAJOR);
-               return -EIO;
-       }
-
        if (parport[0] == -2) return 0;
 
        pb = parport_enumerate();
 
        while (pb) {
                /* We only understand PC-style ports. */
-               if (pb->modes & PARPORT_MODE_SPP) {
+               if (pb->modes & PARPORT_MODE_PCSPP) {
                        if (parport[0] == -1 || lp_searchfor(parport, count) ||
                            (parport[0] == -3 &&
                             pb->probe_info.class == PARPORT_CLASS_PRINTER)) {
@@ -704,12 +697,8 @@ __initfunc(int lp_init(void))
                                                lp_interrupt, PARPORT_DEV_TRAN,
                                                (void *) &lp_table[count]);
                                lp_table[count].flags |= LP_EXIST;
-                               printk(KERN_INFO "lp%d: using %s at 0x%x, ", 
-                                      count, pb->name, pb->base);
-                               if (pb->irq == PARPORT_IRQ_NONE)
-                                       printk("polling.\n");
-                               else
-                                       printk("irq %d.\n", pb->irq);
+                               printk(KERN_INFO "lp%d: using %s (%s).\n", 
+                                      count, pb->name, (pb->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven");
                        }
                        if (++count == LP_NO)
                                break;
@@ -720,9 +709,14 @@ __initfunc(int lp_init(void))
        /* Successful specified devices increase count
         * Unsuccessful specified devices increase failed
         */
-       if (count)
-               return 0;
-  
+       if (count) {
+               if (register_chrdev(LP_MAJOR, "lp", &lp_fops)) {
+                       printk("lp: unable to get major %d\n", LP_MAJOR);
+                       return -EIO;
+               }
+               return 0;
+       }
+
        printk(KERN_INFO "lp: driver loaded but no devices found\n");
 #ifdef MODULE
        return 0;
diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c
new file mode 100644 (file)
index 0000000..59d2666
--- /dev/null
@@ -0,0 +1,2978 @@
+/*
+ * Rocketport device driver for Linux
+ *
+ * Written by Theodore Ts'o, 1995, 1996, 1997.
+ * 
+ * Copyright (C) 1995, 1996, 1997 by Comtrol, Inc.
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Minor number schema:
+ *
+ * +-------------------------------+
+ * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * +---+-------+-------+-----------+
+ * | C | Board |  AIOP | Port #    |
+ * +---+-------+-------+-----------+
+ *
+ * C=0 implements normal POSIX tty.
+ * C=1 is reserved for the callout device.
+ * 
+ * Normally, the user won't have to worry about the AIOP; as far as
+ * the user is concerned, the lower 5 bits of the minor number address
+ * the ports on a particular board (from 0 up to 32).
+ */
+
+/* Kernel includes */
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#if (defined(CONFIG_PCI) && (LINUX_VERSION_CODE >= 131072))
+#define ENABLE_PCI
+#endif
+
+#if (LINUX_VERSION_CODE > 66304)
+#define NEW_MODULES
+#endif
+
+#ifdef NEW_MODULES
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+#include <linux/config.h>
+#else /* !NEW_MODULES */
+#ifdef MODVERSIONS
+#define MODULE
+#endif
+#include <linux/config.h>
+#include <linux/module.h>
+#endif /* NEW_MODULES */
+
+#include <linux/errno.h>
+#include <linux/major.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/major.h>
+#include <linux/ioport.h>
+#ifdef ENABLE_PCI
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#endif
+#if (LINUX_VERSION_CODE >= 131343) /* 2.1.15 -- XX get correct version */
+#include <linux/init.h>
+#else
+#define __initfunc(x)  x
+#endif
+       
+#include "rocket_int.h"
+#include <linux/rocket.h>
+
+#define ROCKET_VERSION "1.14a"
+#define ROCKET_DATE "19-Jul-97"
+
+#define ROCKET_PARANOIA_CHECK
+
+#undef ROCKET_DEBUG_OPEN
+#undef ROCKET_DEBUG_INTR
+#undef ROCKET_DEBUG_WRITE
+#undef ROCKET_DEBUG_FLOW
+#undef ROCKET_DEBUG_THROTTLE
+#undef ROCKET_DEBUG_WAIT_UNTIL_SENT
+#undef ROCKET_DEBUG_RECEIVE
+#undef ROCKET_DEBUG_HANGUP
+       
+
+/*   CAUTION!!!!!  The TIME_STAT Function relies on the Pentium 64 bit
+ *    register.  For various reasons related to 1.2.13, the test for this
+ *    register is omitted from this driver.  If you are going to enable
+ *    this option, make sure you are running a Pentium CPU and that a
+ *    cat of /proc/cpuinfo shows ability TS Counters as Yes.  Warning part
+ *    done, don't cry to me if you enable this options and things won't
+ *    work.  If it gives you any problems, then disable the option.  The code
+ *    in this function is pretty straight forward, if it breaks on your
+ *    CPU, there is probably something funny about your CPU.
+ */
+
+#undef TIME_STAT       /* For performing timing statistics on driver. */
+                       /* Produces printks, one every TIME_COUNTER loops, eats */
+                       /* some of your CPU time.  Good for testing or */
+                       /* other checking, otherwise, leave it undefed */
+                       /* Doug Ledford */
+#define TIME_STAT_CPU 100      /* This needs to be set to your processor speed */
+                               /* For example, 100Mhz CPU, set this to 100 */
+#define TIME_COUNTER 180000    /* This is how many iterations to run before */
+                             /* performing the printk statements.   */
+                             /* 6000 = 1 minute, 360000 = 1 hour, etc. */
+                             /* Since time_stat is long long, this */
+                             /* Can be really high if you want :)  */
+#undef TIME_STAT_VERBOSE   /* Undef this if you want a terse log message. */
+
+#define _INLINE_ inline
+
+/*
+ * Until we get a formal timer assignment
+ */
+#ifndef COMTROL_TIMER
+#define COMTROL_TIMER 26
+#endif
+
+#ifndef NEW_MODULES
+/*
+ * NB. we must include the kernel idenfication string in to install the module.
+ */
+#include <linux/version.h>
+/*static*/ char kernel_version[] = UTS_RELEASE;
+#endif
+
+static struct r_port *rp_table[MAX_RP_PORTS];
+static struct tty_struct *rocket_table[MAX_RP_PORTS];
+static unsigned int xmit_flags[NUM_BOARDS];
+static struct termios *rocket_termios[MAX_RP_PORTS];
+static struct termios *rocket_termios_locked[MAX_RP_PORTS];
+static void rp_wait_until_sent(struct tty_struct *tty, int timeout);
+static void rp_flush_buffer(struct tty_struct *tty);
+
+static struct tty_driver rocket_driver, callout_driver;
+static int rocket_refcount = 0;
+
+static int rp_num_ports_open = 0;
+
+unsigned long board1 = 0;
+unsigned long board2 = 0;
+unsigned long board3 = 0;
+unsigned long board4 = 0;
+unsigned long controller = 0;
+unsigned long support_low_speed = 0;
+int rp_baud_base = 460800;
+static unsigned long rcktpt_io_addr[NUM_BOARDS];
+static int max_board;
+#ifdef TIME_STAT
+static unsigned long long time_stat = 0;
+static unsigned long time_stat_short = 0;
+static unsigned long time_stat_long = 0;
+static unsigned long time_counter = 0;
+#endif
+
+/*
+ * Provide backwards compatibility for kernels prior to 2.1.8.
+ */
+#if (LINUX_VERSION_CODE < 131336)
+int copy_from_user(void *to, const void *from_user, unsigned long len)
+{
+       int     error;
+
+       error = verify_area(VERIFY_READ, from_user, len);
+       if (error)
+               return len;
+       memcpy_fromfs(to, from_user, len);
+       return 0;
+}
+
+int copy_to_user(void *to_user, const void *from, unsigned long len)
+{
+       int     error;
+       
+       error = verify_area(VERIFY_WRITE, to_user, len);
+       if (error)
+               return len;
+       memcpy_tofs(to_user, from, len);
+       return 0;
+}
+#else
+#include <asm/uaccess.h>
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by rp_write.  We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static void rp_start(struct tty_struct *tty);
+
+static inline int rocket_paranoia_check(struct r_port *info,
+                                       dev_t device, const char *routine)
+{
+#ifdef ROCKET_PARANOIA_CHECK
+       static const char *badmagic =
+               "Warning: bad magic number for rocketport struct (%d, %d) in %s\n";
+       if (!info)
+               return 1;
+       if (info->magic != RPORT_MAGIC) {
+               printk(badmagic, MAJOR(device), MINOR(device), routine);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+/*
+ * Here begins the interrupt/polling routine for the Rocketport!
+ */
+static _INLINE_ void rp_do_receive(struct r_port *info, struct tty_struct *tty,
+                                  CHANNEL_t *cp, unsigned int ChanStatus)
+{
+       unsigned int CharNStat;
+       int ToRecv, wRecv, space, count;
+       unsigned char   *cbuf;
+       char            *fbuf;
+       
+       ToRecv= sGetRxCnt(cp);
+       space = 2*TTY_FLIPBUF_SIZE;
+       cbuf = tty->flip.char_buf;
+       fbuf = tty->flip.flag_buf;
+       count = 0;
+#ifdef ROCKET_DEBUG_INTR
+       printk("rp_do_receive(%d, %d)...", ToRecv, space);
+#endif
+       if (ToRecv == 0 || (space <= 0))
+               return;
+       
+       /*
+        * determine how many we can actually read in.  If we can't
+        * read any in then we have a software overrun condition.
+        */
+       if (ToRecv > space)
+               ToRecv = space;
+       
+       /*
+        * if status indicates there are errored characters in the
+        * FIFO, then enter status mode (a word in FIFO holds
+        * character and status).
+        */
+       if (ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) {
+               if (!(ChanStatus & STATMODE)) {
+#ifdef ROCKET_DEBUG_RECEIVE
+                       printk("Entering STATMODE...");
+#endif
+                       ChanStatus |= STATMODE;
+                       sEnRxStatusMode(cp);
+               }
+       }
+
+       /* 
+        * if we previously entered status mode, then read down the
+        * FIFO one word at a time, pulling apart the character and
+        * the status.  Update error counters depending on status
+        */
+       if (ChanStatus & STATMODE) {
+#ifdef ROCKET_DEBUG_RECEIVE
+               printk("Ignore %x, read %x...", info->ignore_status_mask,
+                      info->read_status_mask);
+#endif
+               while (ToRecv) {
+                       CharNStat= sInW(sGetTxRxDataIO(cp));
+
+#ifdef ROCKET_DEBUG_RECEIVE
+                       printk("%x...", CharNStat);
+#endif
+
+                       if (CharNStat & STMBREAKH)
+                               CharNStat &= ~(STMFRAMEH | STMPARITYH);
+                       if (CharNStat & info->ignore_status_mask) {
+                               ToRecv--;
+                               continue;
+                       }
+                       CharNStat &= info->read_status_mask;
+                       if (CharNStat & STMBREAKH) {
+                               *fbuf++ = TTY_BREAK;
+#if 0
+                               if (info->flags & ROCKET_SAK)
+                                       do_SAK(tty);
+#endif
+                       } else if (CharNStat & STMPARITYH)
+                               *fbuf++ = TTY_PARITY;
+                       else if (CharNStat & STMFRAMEH)
+                               *fbuf++ = TTY_FRAME;
+                       else if (CharNStat & STMRCVROVRH)
+                               *fbuf++ =TTY_OVERRUN;
+                       else
+                               *fbuf++ = 0;
+                       *cbuf++ = CharNStat & 0xff;
+                       count++;
+                       ToRecv--;
+               }
+
+               /*
+                * after we've emptied the FIFO in status mode, turn
+                * status mode back off
+                */
+               if (sGetRxCnt(cp) == 0) {
+#ifdef ROCKET_DEBUG_RECEIVE
+                       printk("Status mode off.\n");
+#endif
+                       sDisRxStatusMode(cp);
+               }
+       } else {
+               /*
+                * we aren't in status mode, so read down the FIFO two
+                * characters at time by doing repeated word IO
+                * transfer.
+                */
+               wRecv= ToRecv >> 1;
+               if (wRecv)
+                       sInStrW(sGetTxRxDataIO(cp), cbuf,
+                               wRecv);
+               if (ToRecv & 1)
+                       cbuf[ToRecv-1] = sInB(sGetTxRxDataIO(cp));
+               memset(fbuf, 0, ToRecv);
+               cbuf += ToRecv;
+               fbuf += ToRecv;
+               count += ToRecv;
+       }
+       tty->ldisc.receive_buf(tty, tty->flip.char_buf,
+                              tty->flip.flag_buf, count);
+}
+
+/*
+ * This routine is called when a transmit interrupt is found.  It's
+ * responsible for pushing data found in the transmit buffer out to
+ * the serial card.
+ */
+static _INLINE_ void rp_do_transmit(struct r_port *info)
+{
+       int     c;
+       CHANNEL_t *cp = &info->channel;
+       struct tty_struct *tty;
+       
+#ifdef ROCKET_DEBUG_INTR
+       printk("rp_do_transmit ");
+#endif
+       if (!info)
+               return;
+       if (!info->tty) {
+               printk("rp: WARNING rp_do_transmit called with info->tty==NULL\n");
+               xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f));
+               return;
+       }
+       tty = info->tty;
+       info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
+       while (1) {
+               if (tty->stopped || tty->hw_stopped)
+                       break;
+               c = MIN(info->xmit_fifo_room,
+                       MIN(info->xmit_cnt,
+                           XMIT_BUF_SIZE - info->xmit_tail));
+               if (c <= 0 || info->xmit_fifo_room <= 0)
+                       break;
+               sOutStrW(sGetTxRxDataIO(cp),
+                        info->xmit_buf + info->xmit_tail, c/2);
+               if (c & 1)
+                       sOutB(sGetTxRxDataIO(cp),
+                             info->xmit_buf[info->xmit_tail + c -
+                                            1]);
+               info->xmit_tail += c;
+               info->xmit_tail &= XMIT_BUF_SIZE-1;
+               info->xmit_cnt -= c;
+               info->xmit_fifo_room -= c;
+#ifdef ROCKET_DEBUG_INTR
+               printk("tx %d chars...", c);
+#endif
+       }
+       if (info->xmit_cnt == 0)
+               xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f));
+       if (info->xmit_cnt < WAKEUP_CHARS) {
+               if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+                   tty->ldisc.write_wakeup)
+                       (tty->ldisc.write_wakeup)(tty);
+               wake_up_interruptible(&tty->write_wait);
+       }
+#ifdef ROCKET_DEBUG_INTR
+       printk("(%d,%d,%d,%d)...", info->xmit_cnt, info->xmit_head,
+              info->xmit_tail, info->xmit_fifo_room);
+#endif
+}
+
+/*
+ * This function is called for each port which has signalled an
+ * interrupt.  It checks what interrupts are pending and services
+ * them. 
+ */
+static _INLINE_ void rp_handle_port(struct r_port *info)
+{
+       CHANNEL_t *cp;
+       struct tty_struct *tty;
+       unsigned int IntMask, ChanStatus;
+
+       if (!info)
+               return;
+       if ( (info->flags & ROCKET_INITIALIZED) == 0 ) {
+               printk("rp: WARNING: rp_handle_port called with info->flags & NOT_INIT\n");
+               return;
+       }
+       if (!info->tty) {
+               printk("rp: WARNING: rp_handle_port called with info->tty==NULL\n");
+               return;
+       }
+       cp = &info->channel;
+       tty = info->tty;
+
+       IntMask = sGetChanIntID(cp) & info->intmask;
+#ifdef ROCKET_DEBUG_INTR
+       printk("rp_interrupt %02x...", IntMask);
+#endif
+       ChanStatus= sGetChanStatus(cp);
+       if (IntMask & RXF_TRIG) {       /* Rx FIFO trigger level */
+               rp_do_receive(info, tty, cp, ChanStatus);
+       }
+#if 0
+       if (IntMask & SRC_INT) {        /* Special receive condition */
+       }
+#endif
+       if (IntMask & DELTA_CD) {       /* CD change  */
+#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || \
+     defined(ROCKET_DEBUG_HANGUP))
+               printk("ttyR%d CD now %s...", info->line,
+                      (ChanStatus & CD_ACT) ? "on" : "off");
+#endif
+               if (!(ChanStatus & CD_ACT) &&
+                   info->cd_status &&
+                   !((info->flags & ROCKET_CALLOUT_ACTIVE) &&
+                     (info->flags & ROCKET_CALLOUT_NOHUP))) {
+#ifdef ROCKET_DEBUG_HANGUP
+                       printk("CD drop, calling hangup.\n");
+#endif
+                       tty_hangup(tty);
+               }
+               info->cd_status = (ChanStatus & CD_ACT) ? 1 : 0;
+               wake_up_interruptible(&info->open_wait);
+       }
+#ifdef ROCKET_DEBUG_INTR
+       if (IntMask & DELTA_CTS) {      /* CTS change */
+               printk("CTS change...\n");
+       }
+       if (IntMask & DELTA_DSR) {      /* DSR change */
+               printk("DSR change...\n");
+       }
+#endif
+}
+
+/*
+ * The top level polling routine.
+ */
+static void rp_do_poll(void)
+{
+       CONTROLLER_t *ctlp;
+       int ctrl, aiop, ch, line;
+       unsigned int xmitmask;
+       unsigned char CtlMask, AiopMask;
+
+#ifdef TIME_STAT
+       unsigned long low=0, high=0, loop_time;
+       unsigned long long time_stat_tmp=0, time_stat_tmp2=0;
+
+       __asm__(".byte 0x0f,0x31"
+               :"=a" (low), "=d" (high));
+       time_stat_tmp = high;
+       time_stat_tmp <<= 32;
+       time_stat_tmp += low;
+#endif /* TIME_STAT */
+
+       for (ctrl=0; ctrl < max_board; ctrl++) {
+               if (rcktpt_io_addr[ctrl] <= 0)
+                       continue;
+               ctlp= sCtlNumToCtlPtr(ctrl);
+
+#ifdef ENABLE_PCI
+               if(ctlp->BusType == isPCI)
+                       CtlMask= sPCIGetControllerIntStatus(ctlp);
+               else
+#endif
+                       CtlMask= sGetControllerIntStatus(ctlp);
+               for (aiop=0; CtlMask; CtlMask >>= 1, aiop++) {
+                       if (CtlMask & 1) {
+                               AiopMask= sGetAiopIntStatus(ctlp, aiop);
+                               for (ch=0; AiopMask; AiopMask >>= 1, ch++) {
+                                       if (AiopMask & 1) {
+                                               line = (ctrl << 5) | 
+                                                       (aiop << 3) | ch;
+                                               rp_handle_port(rp_table[line]);
+                                       }
+                               }
+                       }
+               }
+               xmitmask = xmit_flags[ctrl];
+               for (line = ctrl << 5; xmitmask; xmitmask >>= 1, line++) {
+                       if (xmitmask & 1)
+                               rp_do_transmit(rp_table[line]);
+               }
+       }
+
+       /*
+        * Reset the timer so we get called at the next clock tick.
+        */
+       if (rp_num_ports_open) {
+               timer_active |= 1 << COMTROL_TIMER;
+       }
+#ifdef TIME_STAT
+       __asm__(".byte 0x0f,0x31"
+               :"=a" (low), "=d" (high));
+       time_stat_tmp2 = high;
+       time_stat_tmp2 <<= 32;
+       time_stat_tmp2 += low;
+       time_stat_tmp2 -= time_stat_tmp;
+       time_stat += time_stat_tmp2;
+       if (time_counter == 0) 
+               time_stat_short = time_stat_long = time_stat_tmp2;
+       else {
+               if ( time_stat_tmp2 < time_stat_short )
+                       time_stat_short = time_stat_tmp2;
+               else if ( time_stat_tmp2 > time_stat_long )
+                       time_stat_long = time_stat_tmp2;
+       }
+       if ( ++time_counter == TIME_COUNTER ) {
+               loop_time = (unsigned long) ( ((unsigned long)(time_stat >> 32) * ( (unsigned long)(0xffffffff)/(TIME_STAT_CPU * TIME_COUNTER) ) ) + ((unsigned long)time_stat/(TIME_STAT_CPU*TIME_COUNTER)));
+#ifdef TIME_STAT_VERBOSE
+               printk("rp_do_poll: Interrupt Timings\n");
+               printk("     %5ld iterations; %ld us min,\n",
+                      (long)TIME_COUNTER, (time_stat_short/TIME_STAT_CPU));
+               printk("     %5ld us max, %ld us average per iteration.\n",
+                      (time_stat_long/TIME_STAT_CPU), loop_time);
+               printk("We want to use < 5,000 us for an iteration.\n");
+#else /* TIME_STAT_VERBOSE */
+               printk("rp: %ld loops: %ld min, %ld max, %ld us/loop.\n",
+                      (long)TIME_COUNTER, (time_stat_short/TIME_STAT_CPU),
+                      (time_stat_long/TIME_STAT_CPU), loop_time);
+#endif /* TIME_STAT_VERBOSE */
+               time_counter = time_stat = 0;
+               time_stat_short = time_stat_long = 0;
+       }
+#endif /* TIME_STAT */
+}
+/*
+ * Here ends the interrupt/polling routine.
+ */
+
+
+/*
+ * This function initializes the r_port structure, as well as enabling
+ * the port on the RocketPort board.
+ */
+static void init_r_port(int board, int aiop, int chan)
+{
+       struct r_port *info;
+       int line;
+       CONTROLLER_T *ctlp;
+       CHANNEL_t       *cp;
+       
+       line = (board << 5) | (aiop << 3) | chan;
+
+       ctlp= sCtlNumToCtlPtr(board);
+
+       info = kmalloc(sizeof(struct r_port), GFP_KERNEL);
+       if (!info) {
+               printk("Couldn't allocate info struct for line #%d\n", line);
+               return;
+       }
+       memset(info, 0, sizeof(struct r_port));
+       
+       info->magic = RPORT_MAGIC;
+       info->line = line;
+       info->ctlp = ctlp;
+       info->board = board;
+       info->aiop = aiop;
+       info->chan = chan;
+       info->closing_wait = 3000;
+       info->close_delay = 50;
+       info->callout_termios =callout_driver.init_termios;
+       info->normal_termios = rocket_driver.init_termios;
+
+       info->intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD |
+               DELTA_CTS | DELTA_DSR;
+       if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) {
+               printk("Rocketport sInitChan(%d, %d, %d) failed!\n",
+                      board, aiop, chan);
+               kfree(info);
+               return;
+       }
+       cp = &info->channel;
+       rp_table[line] = info;
+}
+
+static int baud_table[] = {
+       0, 50, 75, 110, 134, 150, 200, 300,
+       600, 1200, 1800, 2400, 4800, 9600, 19200,
+       38400, 57600, 115200, 230400, 460800, 0 };
+
+/*
+ * This routine configures a rocketport port so according to its
+ * termio settings.
+ */
+static void configure_r_port(struct r_port *info)
+{
+       unsigned cflag;
+       unsigned long   flags;
+       int     i, bits, baud;
+       CHANNEL_t       *cp;
+       
+       if (!info->tty || !info->tty->termios)
+               return;
+       cp = &info->channel;
+       cflag = info->tty->termios->c_cflag;
+
+       /* Byte size and parity */
+       if ((cflag & CSIZE) == CS8) {
+               sSetData8(cp);
+               bits = 10;
+       } else {
+               sSetData7(cp);
+               bits = 9;
+       }
+        if (cflag & CSTOPB) {
+               sSetStop2(cp);
+               bits++;
+       } else {
+               sSetStop1(cp);
+       }
+       
+       if (cflag & PARENB) {
+               sEnParity(cp);
+               bits++;
+               if (cflag & PARODD) {
+                       sSetOddParity(cp);
+               } else {
+                       sSetEvenParity(cp);
+               }
+       } else {
+               sDisParity(cp);
+       }
+       
+       /* baud rate */
+       i = cflag & CBAUD;
+       if (i & CBAUDEX) {
+               i &= ~CBAUDEX;
+               if (i < 1 || i > 4) 
+                       info->tty->termios->c_cflag &= ~CBAUDEX;
+               else
+                       i += 15;
+       }
+       if (i == 15) {
+               if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
+                       i += 1;
+               if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
+                       i += 2;
+               if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
+                       i += 3;
+               if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
+                       i += 4;
+       }
+       baud = baud_table[i] ? baud_table[i] : 9600;
+       info->cps = baud / bits;
+       sSetBaud(cp, (rp_baud_base/baud) - 1);
+       
+       if (cflag & CRTSCTS) {
+               info->intmask |= DELTA_CTS;
+               sEnCTSFlowCtl(cp);
+       } else {
+               info->intmask &= ~DELTA_CTS;
+               sDisCTSFlowCtl(cp);
+       }
+       sSetRTS(&info->channel);
+       if (cflag & CLOCAL)
+               info->intmask &= ~DELTA_CD;
+       else {
+               save_flags(flags); cli();
+               if (sGetChanStatus(cp) & CD_ACT)
+                       info->cd_status = 1;
+               else
+                       info->cd_status = 0;
+               info->intmask |= DELTA_CD;
+               restore_flags(flags);
+       }
+       /*
+        * Set up ignore/read mask words
+        */
+       info->read_status_mask = STMRCVROVRH | 0xFF;
+       if (I_INPCK(info->tty))
+               info->read_status_mask |= STMFRAMEH | STMPARITYH;
+       if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+               info->read_status_mask |= STMBREAKH;
+       
+       /*
+        * Characters to ignore
+        */
+       info->ignore_status_mask = 0;
+       if (I_IGNPAR(info->tty))
+               info->ignore_status_mask |= STMFRAMEH | STMPARITYH;
+       if (I_IGNBRK(info->tty)) {
+               info->ignore_status_mask |= STMBREAKH;
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns too.  (For real raw support).
+                */
+               if (I_IGNPAR(info->tty))
+                       info->ignore_status_mask |= STMRCVROVRH;
+       }
+}
+
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+                          struct r_port *info)
+{
+       struct wait_queue wait = { current, NULL };
+       int             retval;
+       int             do_clocal = 0;
+       unsigned long   flags;
+
+       /*
+        * If the device is in the middle of being closed, then block
+        * until it's done, and then try again.
+        */
+       if (tty_hung_up_p(filp))
+               return ((info->flags & ROCKET_HUP_NOTIFY) ? 
+                       -EAGAIN : -ERESTARTSYS);
+       if (info->flags & ROCKET_CLOSING) {
+               interruptible_sleep_on(&info->close_wait);
+               return ((info->flags & ROCKET_HUP_NOTIFY) ? 
+                       -EAGAIN : -ERESTARTSYS);
+       }
+
+       /*
+        * If this is a callout device, then just make sure the normal
+        * device isn't being used.
+        */
+       if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+               if (info->flags & ROCKET_NORMAL_ACTIVE)
+                       return -EBUSY;
+               if ((info->flags & ROCKET_CALLOUT_ACTIVE) &&
+                   (info->flags & ROCKET_SESSION_LOCKOUT) &&
+                   (info->session != current->session))
+                   return -EBUSY;
+               if ((info->flags & ROCKET_CALLOUT_ACTIVE) &&
+                   (info->flags & ROCKET_PGRP_LOCKOUT) &&
+                   (info->pgrp != current->pgrp))
+                   return -EBUSY;
+               info->flags |= ROCKET_CALLOUT_ACTIVE;
+               return 0;
+       }
+       
+       /*
+        * If non-blocking mode is set, or the port is not enabled,
+        * then make the check up front and then exit.
+        */
+       if ((filp->f_flags & O_NONBLOCK) ||
+           (tty->flags & (1 << TTY_IO_ERROR))) {
+               if (info->flags & ROCKET_CALLOUT_ACTIVE)
+                       return -EBUSY;
+               info->flags |= ROCKET_NORMAL_ACTIVE;
+               return 0;
+       }
+
+       if (info->flags & ROCKET_CALLOUT_ACTIVE) {
+               if (info->normal_termios.c_cflag & CLOCAL)
+                       do_clocal = 1;
+       } else {
+               if (tty->termios->c_cflag & CLOCAL)
+                       do_clocal = 1;
+       }
+       
+       /*
+        * Block waiting for the carrier detect and the line to become
+        * free (i.e., not in use by the callout).  While we are in
+        * this loop, info->count is dropped by one, so that
+        * rp_close() knows when to free things.  We restore it upon
+        * exit, either normal or abnormal.
+        */
+       retval = 0;
+       add_wait_queue(&info->open_wait, &wait);
+#ifdef ROCKET_DEBUG_OPEN
+       printk("block_til_ready before block: ttyR%d, count = %d\n",
+              info->line, info->count);
+#endif
+       save_flags(flags); cli();
+       if (!tty_hung_up_p(filp))
+               info->count--;
+       restore_flags(flags);
+       info->blocked_open++;
+       while (1) {
+               if (!(info->flags & ROCKET_CALLOUT_ACTIVE) &&
+                   (tty->termios->c_cflag & CBAUD)) {
+                       sSetDTR(&info->channel);
+                       sSetRTS(&info->channel);
+               }
+               current->state = TASK_INTERRUPTIBLE;
+               if (tty_hung_up_p(filp) ||
+                   !(info->flags & ROCKET_INITIALIZED)) {
+                       if (info->flags & ROCKET_HUP_NOTIFY)
+                               retval = -EAGAIN;
+                       else
+                               retval = -ERESTARTSYS;  
+                       break;
+               }
+               if (!(info->flags & ROCKET_CALLOUT_ACTIVE) &&
+                   !(info->flags & ROCKET_CLOSING) &&
+                   (do_clocal || (sGetChanStatusLo(&info->channel) &
+                                  CD_ACT)))
+                       break;
+               if (current->signal & ~current->blocked) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+#ifdef ROCKET_DEBUG_OPEN
+               printk("block_til_ready blocking: ttyR%d, count = %d, flags=0x%0x\n",
+                      info->line, info->count, info->flags);
+#endif
+               schedule();
+       }
+       current->state = TASK_RUNNING;
+       remove_wait_queue(&info->open_wait, &wait);
+       cli();
+       if (!tty_hung_up_p(filp))
+               info->count++;
+       restore_flags(flags);
+       info->blocked_open--;
+#ifdef ROCKET_DEBUG_OPEN
+       printk("block_til_ready after blocking: ttyR%d, count = %d\n",
+              info->line, info->count);
+#endif
+       if (retval)
+               return retval;
+       info->flags |= ROCKET_NORMAL_ACTIVE;
+       return 0;
+}      
+
+/*
+ * This routine is called whenever a rocketport board is opened.
+ */
+static int rp_open(struct tty_struct *tty, struct file * filp)
+{
+       struct r_port *info;
+       int     line, retval;
+       CHANNEL_t       *cp;
+       unsigned long page;
+       
+       line = MINOR(tty->device) - tty->driver.minor_start;
+       if ((line < 0) || (line >= MAX_RP_PORTS))
+               return -ENODEV;
+       if (!tmp_buf) {
+               page = get_free_page(GFP_KERNEL);
+               if (!page)
+                       return -ENOMEM;
+               if (tmp_buf)
+                       free_page(page);
+               else
+                       tmp_buf = (unsigned char *) page;
+       }
+       page = get_free_page(GFP_KERNEL);
+       if (!page)
+               return -ENOMEM;
+
+       tty->driver_data = info = rp_table[line];
+       
+       if (info->flags & ROCKET_CLOSING) {
+               interruptible_sleep_on(&info->close_wait);
+               free_page(page);
+               return ((info->flags & ROCKET_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS);
+       }
+       
+       /*
+        * We must not sleep from here until the port is marked fully
+        * in use.
+        */
+       if (rp_table[line] == NULL) {
+               tty->flags = (1 << TTY_IO_ERROR);
+               free_page(page);
+               return 0;
+       }
+       if (!info) {
+               printk("rp_open: rp_table[%d] is NULL!\n", line);
+               free_page(page);
+               return -EIO;
+       }
+       if (info->xmit_buf)
+               free_page(page);
+       else
+               info->xmit_buf = (unsigned char *) page;
+       info->tty = tty;
+
+       if (info->flags & ROCKET_CLOSING) {
+               interruptible_sleep_on(&info->close_wait);
+               return ((info->flags & ROCKET_HUP_NOTIFY) ? 
+                       -EAGAIN : -ERESTARTSYS);
+       }
+
+       if (info->count++ == 0) {
+#ifdef MODULE
+               MOD_INC_USE_COUNT;
+#endif
+               rp_num_ports_open++;
+#ifdef ROCKET_DEBUG_OPEN
+               printk("rocket mod++ = %d...", rp_num_ports_open);
+#endif
+       }
+#ifdef ROCKET_DEBUG_OPEN
+       printk("rp_open ttyR%d, count=%d\n", info->line, info->count);
+#endif
+       /*
+        * Info->count is now 1; so it's safe to sleep now.
+        */
+       info->session = current->session;
+       info->pgrp = current->pgrp;
+       
+       cp = &info->channel;
+       sSetRxTrigger(cp, TRIG_1);
+       if (sGetChanStatus(cp) & CD_ACT)
+               info->cd_status = 1;
+       else
+               info->cd_status = 0;
+       sDisRxStatusMode(cp);
+       sFlushRxFIFO(cp);       
+       sFlushTxFIFO(cp);       
+
+       sEnInterrupts(cp, (TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN));
+       sSetRxTrigger(cp, TRIG_1);
+
+       sGetChanStatus(cp);
+       sDisRxStatusMode(cp);
+       sClrTxXOFF(cp);
+
+       sDisCTSFlowCtl(cp);
+       sDisTxSoftFlowCtl(cp);
+
+       sEnRxFIFO(cp);
+       sEnTransmit(cp);
+
+       info->flags |= ROCKET_INITIALIZED;
+       
+       configure_r_port(info);
+       if (tty->termios->c_cflag & CBAUD) {
+               sSetDTR(cp);
+               sSetRTS(cp);
+       }
+       
+       timer_active |= 1 << COMTROL_TIMER;
+
+       retval = block_til_ready(tty, filp, info);
+       if (retval) {
+#ifdef ROCKET_DEBUG_OPEN
+               printk("rp_open returning after block_til_ready with %d\n",
+                      retval);
+#endif
+               return retval;
+       }
+
+       if ((info->count == 1) && (info->flags & ROCKET_SPLIT_TERMIOS)) {
+               if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+                       *tty->termios = info->normal_termios;
+               else 
+                       *tty->termios = info->callout_termios;
+               configure_r_port(info);
+       }
+
+       return 0;
+}
+
+static void rp_close(struct tty_struct *tty, struct file * filp)
+{
+       struct r_port * info = (struct r_port *)tty->driver_data;
+       unsigned long flags;
+       int timeout;
+       CHANNEL_t       *cp;
+
+       if (rocket_paranoia_check(info, tty->device, "rp_close"))
+               return;
+
+#ifdef ROCKET_DEBUG_OPEN
+       printk("rp_close ttyR%d, count = %d\n", info->line, info->count);
+#endif
+       
+       save_flags(flags); cli();
+       
+       if (tty_hung_up_p(filp)) {
+               restore_flags(flags);
+               return;
+       }
+       if ((tty->count == 1) && (info->count != 1)) {
+               /*
+                * Uh, oh.  tty->count is 1, which means that the tty
+                * structure will be freed.  Info->count should always
+                * be one in these conditions.  If it's greater than
+                * one, we've got real problems, since it means the
+                * serial port won't be shutdown.
+                */
+               printk("rp_close: bad serial port count; tty->count is 1, "
+                      "info->count is %d\n", info->count);
+               info->count = 1;
+       }
+       if (--info->count < 0) {
+               printk("rp_close: bad serial port count for ttyR%d: %d\n",
+                      info->line, info->count);
+               info->count = 0;
+       }
+       if (info->count) {
+               restore_flags(flags);
+               return;
+       }
+       info->flags |= ROCKET_CLOSING;
+       /*
+        * Save the termios structure, since this port may have
+        * separate termios for callout and dialin.
+        */
+       if (info->flags & ROCKET_NORMAL_ACTIVE)
+               info->normal_termios = *tty->termios;
+       if (info->flags & ROCKET_CALLOUT_ACTIVE)
+               info->callout_termios = *tty->termios;
+       
+       cp = &info->channel;
+
+       /*
+        * Notify the line discpline to only process XON/XOFF characters
+        */
+       tty->closing = 1;
+
+       /*
+        * If transmission was throttled by the application request,
+        * just flush the xmit buffer.
+        */
+#if (LINUX_VERSION_CODE >= 131343)
+       if (tty->flow_stopped)
+               rp_flush_buffer(tty);
+#endif
+
+       /*
+        * Wait for the transmit buffer to clear
+        */
+       if (info->closing_wait != ROCKET_CLOSING_WAIT_NONE)
+               tty_wait_until_sent(tty, info->closing_wait);
+       /*
+        * Before we drop DTR, make sure the UART transmitter
+        * has completely drained; this is especially
+        * important if there is a transmit FIFO!
+        */
+       timeout = (sGetTxCnt(cp)+1) * HZ / info->cps;
+       if (timeout == 0)
+               timeout = 1;
+       rp_wait_until_sent(tty, timeout);
+       
+       xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f));
+       sDisTransmit(cp);
+       sDisInterrupts(cp, (TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN));
+       sDisCTSFlowCtl(cp);
+       sDisTxSoftFlowCtl(cp);
+       sClrTxXOFF(cp);
+       sFlushRxFIFO(cp);       
+       sFlushTxFIFO(cp);
+       sClrRTS(cp);
+       if (C_HUPCL(tty)) {
+               sClrDTR(cp);
+       }
+       if (tty->driver.flush_buffer)
+               tty->driver.flush_buffer(tty);
+       if (tty->ldisc.flush_buffer)
+               tty->ldisc.flush_buffer(tty);
+
+       xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f));
+       if (info->blocked_open) {
+               if (info->close_delay) {
+                       current->state = TASK_INTERRUPTIBLE;
+                       current->timeout = jiffies + info->close_delay;
+                       schedule();
+               }
+               wake_up_interruptible(&info->open_wait);
+       } else {
+               if (info->xmit_buf) {
+                       free_page((unsigned long) info->xmit_buf);
+                       info->xmit_buf = 0;
+               }
+       }
+       info->flags &= ~(ROCKET_INITIALIZED | ROCKET_CLOSING |
+                        ROCKET_CALLOUT_ACTIVE | ROCKET_NORMAL_ACTIVE);
+       tty->closing = 0;
+       wake_up_interruptible(&info->close_wait);
+       
+#ifdef MODULE
+       MOD_DEC_USE_COUNT;
+#endif
+       rp_num_ports_open--;
+#ifdef ROCKET_DEBUG_OPEN
+       printk("rocket mod-- = %d...", rp_num_ports_open);
+#endif
+       restore_flags(flags);
+       
+#ifdef ROCKET_DEBUG_OPEN
+       printk("rp_close ttyR%d complete shutdown\n", info->line);
+#endif
+       
+}
+
+static void rp_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+       struct r_port * info = (struct r_port *)tty->driver_data;
+       CHANNEL_t *cp;
+       unsigned cflag;
+       
+
+       if (rocket_paranoia_check(info, tty->device, "rp_set_termios"))
+               return;
+
+       cflag = tty->termios->c_cflag;
+
+       if (cflag == old_termios->c_cflag)
+               return;
+
+       /*
+        * This driver doesn't support CS5 or CS6
+        */
+       if (((cflag & CSIZE) == CS5) ||
+           ((cflag & CSIZE) == CS6))
+               tty->termios->c_cflag = ((cflag & ~CSIZE) |
+                                        (old_termios->c_cflag & CSIZE));
+
+       configure_r_port(info);
+
+       cp = &info->channel;
+
+       /* Handle transition to B0 status */
+       if ((old_termios->c_cflag & CBAUD) &&
+           !(tty->termios->c_cflag & CBAUD)) {
+               sClrDTR(cp);
+               sClrRTS(cp);
+       }
+       
+       /* Handle transition away from B0 status */
+       if (!(old_termios->c_cflag & CBAUD) &&
+           (tty->termios->c_cflag & CBAUD)) {
+               if (!tty->hw_stopped ||
+                   !(tty->termios->c_cflag & CRTSCTS)) {
+                       sSetRTS(cp);
+               }
+               sSetDTR(cp);
+       }
+       
+       if ((old_termios->c_cflag & CRTSCTS) &&
+           !(tty->termios->c_cflag & CRTSCTS)) {
+               tty->hw_stopped = 0;
+               rp_start(tty);
+       }
+}
+
+/*
+ * Here are the routines used by rp_ioctl
+ */
+
+static void send_break(        struct r_port * info, int duration)
+{
+       current->state = TASK_INTERRUPTIBLE;
+       current->timeout = jiffies + duration;
+       cli();
+       sSendBreak(&info->channel);
+       schedule();
+       sClrBreak(&info->channel);
+       sti();
+}
+
+static int get_modem_info(struct r_port * info, unsigned int *value)
+{
+       unsigned int control, result, ChanStatus;
+
+       ChanStatus = sGetChanStatusLo(&info->channel);
+       
+       control = info->channel.TxControl[3];
+       result =  ((control & SET_RTS) ? TIOCM_RTS : 0)
+               | ((control & SET_DTR) ? TIOCM_DTR : 0)
+               | ((ChanStatus  & CD_ACT) ? TIOCM_CAR : 0)
+                       /* TIOCM_RNG not supported */
+               | ((ChanStatus  & DSR_ACT) ? TIOCM_DSR : 0)
+               | ((ChanStatus  & CTS_ACT) ? TIOCM_CTS : 0);
+
+       if (copy_to_user(value, &result, sizeof(int)))
+               return -EFAULT;
+       return 0;
+}
+
+static int set_modem_info(struct r_port * info, unsigned int cmd,
+                         unsigned int *value)
+{
+       unsigned int arg;
+
+       if (copy_from_user(&arg, value, sizeof(int)))
+               return -EFAULT;
+
+       switch (cmd) {
+       case TIOCMBIS: 
+               if (arg & TIOCM_RTS)
+                       info->channel.TxControl[3] |= SET_RTS;
+               if (arg & TIOCM_DTR)
+                       info->channel.TxControl[3] |= SET_DTR;
+               break;
+       case TIOCMBIC:
+               if (arg & TIOCM_RTS)
+                       info->channel.TxControl[3] &= ~SET_RTS;
+               if (arg & TIOCM_DTR)
+                       info->channel.TxControl[3] &= ~SET_DTR;
+               break;
+       case TIOCMSET:
+               info->channel.TxControl[3] =
+                       ((info->channel.TxControl[3] & ~(SET_RTS | SET_DTR))
+                        | ((arg & TIOCM_RTS) ? SET_RTS : 0)
+                        | ((arg & TIOCM_DTR) ? SET_DTR : 0));
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       sOutDW(info->channel.IndexAddr,
+              *(DWord_t *) &(info->channel.TxControl[0]));
+       
+       return 0;
+}
+
+static int get_config(struct r_port * info, struct rocket_config * retinfo)
+{
+       struct rocket_config tmp;
+  
+       if (!retinfo)
+               return -EFAULT;
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.line = info->line;
+       tmp.flags = info->flags;
+       tmp.close_delay = info->close_delay;
+       tmp.closing_wait = info->closing_wait;
+       tmp.port = rcktpt_io_addr[(info->line >> 5) & 3];
+       
+       if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+               return -EFAULT;
+       return 0;
+}
+
+static int set_config(struct r_port * info, struct rocket_config * new_info)
+{
+       struct rocket_config new_serial;
+
+       if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
+               return -EFAULT;
+
+       if (!suser()) {
+               if ((new_serial.flags & ~ROCKET_USR_MASK) !=
+                   (info->flags & ~ROCKET_USR_MASK))
+                       return -EPERM;
+               info->flags = ((info->flags & ~ROCKET_USR_MASK) |
+                              (new_serial.flags & ROCKET_USR_MASK));
+               configure_r_port(info);
+               return 0;
+       }
+       
+       info->flags = ((info->flags & ~ROCKET_FLAGS) |
+                       (new_serial.flags & ROCKET_FLAGS));
+       info->close_delay = new_serial.close_delay;
+       info->closing_wait = new_serial.closing_wait;
+       configure_r_port(info);
+       
+       return 0;
+}
+
+static int get_ports(struct r_port * info, struct rocket_ports * retports)
+{
+       struct rocket_ports tmp;
+       int     board, port, index;
+  
+       if (!retports)
+               return -EFAULT;
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.tty_major = rocket_driver.major;
+       tmp.callout_major = callout_driver.major;
+       for (board = 0; board < 4; board++) {
+               index = board << 5;
+               for (port = 0; port < 32; port++, index++) {
+                       if (rp_table[index])
+                               tmp.port_bitmap[board] |= 1 << port;
+               }
+       }
+       if (copy_to_user(retports,&tmp,sizeof(*retports)))
+               return -EFAULT;
+       return 0;
+}
+
+static int rp_ioctl(struct tty_struct *tty, struct file * file,
+                   unsigned int cmd, unsigned long arg)
+{
+       int tmp;
+       struct r_port * info = (struct r_port *)tty->driver_data;
+       int retval;
+
+       if (cmd != RCKP_GET_PORTS &&
+           rocket_paranoia_check(info, tty->device, "rp_ioctl"))
+               return -ENODEV;
+
+       switch (cmd) {
+               case TCSBRK:    /* SVID version: non-zero arg --> no break */
+                       retval = tty_check_change(tty);
+                       if (retval)
+                               return retval;
+                       tty_wait_until_sent(tty, 0);
+                       if (current->signal & ~current->blocked)
+                               return -EINTR;
+                       if (!arg) {
+                               send_break(info, HZ/4); /* 1/4 second */
+                               if (current->signal & ~current->blocked)
+                                       return -EINTR;
+                       }
+                       return 0;
+               case TCSBRKP:   /* support for POSIX tcsendbreak() */
+                       retval = tty_check_change(tty);
+                       if (retval)
+                               return retval;
+                       tty_wait_until_sent(tty, 0);
+                       if (current->signal & ~current->blocked)
+                               return -EINTR;
+                       send_break(info, arg ? arg*(HZ/10) : HZ/4);
+                       if (current->signal & ~current->blocked)
+                               return -EINTR;
+                       return 0;
+               case TIOCGSOFTCAR:
+                       tmp = C_CLOCAL(tty) ? 1 : 0;
+                       if (copy_to_user((void *)arg, &tmp, sizeof(int)))
+                               return -EFAULT;
+                       return 0;
+               case TIOCSSOFTCAR:
+                       if (copy_from_user(&tmp, (void *)arg, sizeof(int)))
+                               return -EFAULT;
+
+                       tty->termios->c_cflag =
+                               ((tty->termios->c_cflag & ~CLOCAL) |
+                                (tmp ? CLOCAL : 0));
+                       return 0;
+               case TIOCMGET:
+                       return get_modem_info(info, (unsigned int *) arg);
+               case TIOCMBIS:
+               case TIOCMBIC:
+               case TIOCMSET:
+                       return set_modem_info(info, cmd, (unsigned int *) arg);
+               case RCKP_GET_STRUCT:
+                       if (copy_to_user((void *) arg, info,
+                                        sizeof(struct r_port)))
+                               return -EFAULT;
+                       return 0;
+
+               case RCKP_GET_CONFIG:
+                       return get_config(info, (struct rocket_config *) arg);
+               case RCKP_SET_CONFIG:
+                       return set_config(info, (struct rocket_config *) arg);
+                       
+               case RCKP_GET_PORTS:
+                       return get_ports(info, (struct rocket_ports *) arg);
+               default:
+                       return -ENOIOCTLCMD;
+               }
+       return 0;
+}
+
+#if (defined(ROCKET_DEBUG_FLOW) || defined(ROCKET_DEBUG_THROTTLE))
+static char *rp_tty_name(struct tty_struct *tty, char *buf)
+{
+       if (tty)
+               sprintf(buf, "%s%d", tty->driver.name,
+                       MINOR(tty->device) - tty->driver.minor_start +
+                       tty->driver.name_base);
+       else
+               strcpy(buf, "NULL tty");
+       return buf;
+}
+#endif
+
+static void rp_send_xchar(struct tty_struct *tty, char ch)
+{
+       struct r_port *info = (struct r_port *)tty->driver_data;
+       CHANNEL_t *cp;
+
+       if (rocket_paranoia_check(info, tty->device, "rp_send_xchar"))
+               return;
+
+       cp = &info->channel;
+       if (sGetTxCnt(cp)) 
+               sWriteTxPrioByte(cp, ch);
+       else
+               sWriteTxByte(sGetTxRxDataIO(cp), ch);
+}
+
+static void rp_throttle(struct tty_struct * tty)
+{
+       struct r_port *info = (struct r_port *)tty->driver_data;
+       CHANNEL_t *cp;
+#ifdef ROCKET_DEBUG_THROTTLE
+       char    buf[64];
+       
+       printk("throttle %s: %d....\n", rp_tty_name(tty, buf),
+              tty->ldisc.chars_in_buffer(tty));
+#endif
+
+       if (rocket_paranoia_check(info, tty->device, "rp_throttle"))
+               return;
+
+       cp = &info->channel;
+       if (I_IXOFF(tty))
+               rp_send_xchar(tty, STOP_CHAR(tty));
+       
+       sClrRTS(&info->channel);
+}
+
+static void rp_unthrottle(struct tty_struct * tty)
+{
+       struct r_port *info = (struct r_port *)tty->driver_data;
+       CHANNEL_t *cp;
+#ifdef ROCKET_DEBUG_THROTTLE
+       char    buf[64];
+       
+       printk("unthrottle %s: %d....\n", rp_tty_name(tty, buf),
+              tty->ldisc.chars_in_buffer(tty));
+#endif
+
+       if (rocket_paranoia_check(info, tty->device, "rp_throttle"))
+               return;
+
+       cp = &info->channel;
+       if (I_IXOFF(tty))
+               rp_send_xchar(tty, START_CHAR(tty));
+
+       sSetRTS(&info->channel);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rp_stop() and rp_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rp_stop(struct tty_struct *tty)
+{
+       struct r_port * info = (struct r_port *)tty->driver_data;
+#ifdef ROCKET_DEBUG_FLOW
+       char    buf[64];
+       
+       printk("stop %s: %d %d....\n", rp_tty_name(tty, buf),
+              info->xmit_cnt, info->xmit_fifo_room);
+#endif
+
+       if (rocket_paranoia_check(info, tty->device, "rp_stop"))
+               return;
+
+       if (sGetTxCnt(&info->channel))
+               sDisTransmit(&info->channel);
+}
+
+static void rp_start(struct tty_struct *tty)
+{
+       struct r_port * info = (struct r_port *)tty->driver_data;
+#ifdef ROCKET_DEBUG_FLOW
+       char    buf[64];
+       
+       printk("start %s: %d %d....\n", rp_tty_name(tty, buf),
+              info->xmit_cnt, info->xmit_fifo_room);
+#endif
+
+       if (rocket_paranoia_check(info, tty->device, "rp_stop"))
+               return;
+
+       sEnTransmit(&info->channel);
+       xmit_flags[info->line >> 5] |= (1 << (info->line & 0x1f));
+}
+
+/*
+ * rp_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rp_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       struct r_port *info = (struct r_port *)tty->driver_data;
+       CHANNEL_t *cp;
+       unsigned long orig_jiffies;
+       int check_time, exit_time;
+       int txcnt;
+       
+       if (rocket_paranoia_check(info, tty->device, "rp_wait_until_sent"))
+               return;
+
+       cp = &info->channel;
+
+       orig_jiffies = jiffies;
+#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
+       printk("In RP_wait_until_sent(%d) (jiff=%lu)...", timeout, jiffies);
+       printk("cps=%d...", info->cps);
+#endif
+       while (1) {
+               txcnt = sGetTxCnt(cp);
+               if (!txcnt) {
+                       if (sGetChanStatusLo(cp) & TXSHRMT)
+                               break;
+                       check_time = (HZ / info->cps) / 5;
+               } else
+                       check_time = HZ * txcnt / info->cps;
+               if (timeout) {
+                       exit_time = orig_jiffies + timeout - jiffies;
+                       if (exit_time <= 0)
+                               break;
+                       if (exit_time < check_time)
+                               check_time = exit_time;
+               }
+               if (check_time == 0)
+                       check_time = 1;
+#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
+               printk("txcnt = %d (jiff=%lu,check=%d)...", txcnt,
+                      jiffies, check_time);
+#endif
+               current->state = TASK_INTERRUPTIBLE;
+               current->counter = 0;   /* make us low-priority */
+               current->timeout = jiffies + check_time;
+               schedule();
+               if (current->signal & ~current->blocked)
+                       break;
+       }
+       current->state = TASK_RUNNING;
+#ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT
+       printk("txcnt = %d (jiff=%lu)...done\n", txcnt, jiffies);
+#endif
+}
+
+/*
+ * rp_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void rp_hangup(struct tty_struct *tty)
+{
+       CHANNEL_t       *cp;
+       struct r_port * info = (struct r_port *)tty->driver_data;
+       
+       if (rocket_paranoia_check(info, tty->device, "rp_hangup"))
+               return;
+
+#if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_HANGUP))
+       printk("rp_hangup of ttyR%d...", info->line);
+#endif
+       /*
+        * If the port is in the process of being closed, just force
+        * the transmit buffer to be empty, and let rp_close handle
+        * the clean up.
+        */
+       if (info->flags & ROCKET_CLOSING) {
+               cli();
+               info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+               sti();
+               wake_up_interruptible(&tty->write_wait);
+               return;
+       }
+       if (info->count) {
+#ifdef MODULE
+               MOD_DEC_USE_COUNT;
+#endif
+               rp_num_ports_open--;
+       }
+       
+       xmit_flags[info->line >> 5] &= ~(1 << (info->line & 0x1f));
+       info->count = 0;
+       info->flags &= ~(ROCKET_NORMAL_ACTIVE|ROCKET_CALLOUT_ACTIVE);
+       info->tty = 0;
+
+       cp = &info->channel;
+       sDisRxFIFO(cp);
+       sDisTransmit(cp);
+       sDisInterrupts(cp, (TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN));
+       sDisCTSFlowCtl(cp);
+       sDisTxSoftFlowCtl(cp);
+       sClrTxXOFF(cp);
+       info->flags &= ~ROCKET_INITIALIZED;
+       
+       wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * The Rocketport write routines.  The Rocketport driver uses a
+ * double-buffering strategy, with the twist that if the in-memory CPU
+ * buffer is empty, and there's space in the transmit FIFO, the
+ * writing routines will write directly to transmit FIFO.
+ *
+ * This gets a little tricky, but I'm pretty sure I got it all right.
+ */
+static void rp_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct r_port * info = (struct r_port *)tty->driver_data;
+       CHANNEL_t       *cp;
+
+       if (rocket_paranoia_check(info, tty->device, "rp_put_char"))
+               return;
+
+#ifdef ROCKET_DEBUG_WRITE
+       printk("rp_put_char %c...", ch);
+#endif
+       
+       cp = &info->channel;
+
+       if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room == 0)
+               info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
+
+       if (tty->stopped || tty->hw_stopped ||
+           info->xmit_fifo_room == 0 || info->xmit_cnt != 0) {
+               info->xmit_buf[info->xmit_head++] = ch;
+               info->xmit_head &= XMIT_BUF_SIZE-1;
+               info->xmit_cnt++;
+               xmit_flags[info->line >> 5] |= (1 << (info->line & 0x1f));
+       } else {
+               sOutB(sGetTxRxDataIO(cp), ch);
+               info->xmit_fifo_room--;
+       }
+}
+
+#if (LINUX_VERSION_CODE > 66304)
+static int rp_write(struct tty_struct * tty, int from_user,
+                   const unsigned char *buf, int count)
+#else
+static int rp_write(struct tty_struct * tty, int from_user,
+                   unsigned char *buf, int count)
+#endif 
+{
+       struct r_port * info = (struct r_port *)tty->driver_data;
+       CHANNEL_t       *cp;
+       const unsigned char     *b;
+       int             c, retval = 0;
+       unsigned long   flags;
+
+       if (count <= 0 || rocket_paranoia_check(info, tty->device, "rp_write"))
+               return 0;
+
+#ifdef ROCKET_DEBUG_WRITE
+       printk("rp_write %d chars...", count);
+#endif
+       cp = &info->channel;
+
+       if (!tty->stopped && !tty->hw_stopped && info->xmit_fifo_room == 0)
+               info->xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp);
+
+       if (!tty->stopped && !tty->hw_stopped && info->xmit_cnt == 0
+           && info->xmit_fifo_room >= 0) {
+               c = MIN(count, info->xmit_fifo_room);
+               b = buf;
+               if (from_user) {
+                       down(&tmp_buf_sem);
+                       c -= copy_from_user(tmp_buf, buf, c);
+                       b = tmp_buf;
+                       up(&tmp_buf_sem);
+                       /* In case we got pre-empted */
+                       if (!c) {
+                               retval = -EFAULT;
+                               goto end;
+                       }
+                       if (info->tty == 0)
+                               goto end;
+                       c = MIN(c, info->xmit_fifo_room);
+               }
+               sOutStrW(sGetTxRxDataIO(cp), b, c/2);
+               if (c & 1)
+                       sOutB(sGetTxRxDataIO(cp), b[c-1]);
+               retval += c;
+               buf += c;
+               count -= c;
+               info->xmit_fifo_room -= c;
+       }
+       if (!count)
+               goto end;
+       
+       save_flags(flags);
+       while (1) {
+               cli();
+               if (info->tty == 0) {
+                       restore_flags(flags);
+                       goto end;
+               }
+               c = MIN(count, MIN(XMIT_BUF_SIZE - info->xmit_cnt - 1,
+                                  XMIT_BUF_SIZE - info->xmit_head));
+               if (c <= 0)
+                       break;
+
+               b = buf;
+               if (from_user) {
+                       down(&tmp_buf_sem);
+                       c -= copy_from_user(tmp_buf, buf, c);
+                       b = tmp_buf;
+                       up(&tmp_buf_sem);
+                       if (!c) {
+                               if (retval == 0)
+                                       retval = -EFAULT;
+                               goto end_intr;
+                       }
+                       /* In case we got pre-empted */
+                       if (info->tty == 0)
+                               goto end_intr;
+                       c = MIN(c, MIN(XMIT_BUF_SIZE - info->xmit_cnt - 1,
+                                      XMIT_BUF_SIZE - info->xmit_head));
+                               
+               }
+               memcpy(info->xmit_buf + info->xmit_head, b, c);
+               info->xmit_head = (info->xmit_head + c) & (XMIT_BUF_SIZE-1);
+               info->xmit_cnt += c;
+               restore_flags(flags);
+               buf += c;
+               count -= c;
+               retval += c;
+       }
+end_intr:
+       if ((retval > 0) && !tty->stopped && !tty->hw_stopped)
+               xmit_flags[info->line >> 5] |= (1 << (info->line & 0x1f));
+       restore_flags(flags);
+end:
+       if (info->xmit_cnt < WAKEUP_CHARS) {
+               if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+                   tty->ldisc.write_wakeup)
+                       (tty->ldisc.write_wakeup)(tty);
+               wake_up_interruptible(&tty->write_wait);
+       }
+       return retval;
+}
+
+/*
+ * Return the number of characters that can be sent.  We estimate
+ * only using the in-memory transmit buffer only, and ignore the
+ * potential space in the transmit FIFO.
+ */
+static int rp_write_room(struct tty_struct *tty)
+{
+       struct r_port * info = (struct r_port *)tty->driver_data;
+       int     ret;
+
+       if (rocket_paranoia_check(info, tty->device, "rp_write_room"))
+               return 0;
+
+       ret = XMIT_BUF_SIZE - info->xmit_cnt - 1;
+       if (ret < 0)
+               ret = 0;
+#ifdef ROCKET_DEBUG_WRITE
+       printk("rp_write_room returns %d...", ret);
+#endif
+       return ret;
+}
+
+/*
+ * Return the number of characters in the buffer.  Again, this only
+ * counts those characters in the in-memory transmit buffer.
+ */
+static int rp_chars_in_buffer(struct tty_struct *tty)
+{
+       struct r_port * info = (struct r_port *)tty->driver_data;
+       CHANNEL_t       *cp;
+
+       if (rocket_paranoia_check(info, tty->device, "rp_chars_in_buffer"))
+               return 0;
+
+       cp = &info->channel;
+
+#ifdef ROCKET_DEBUG_WRITE
+       printk("rp_chars_in_buffer returns %d...", info->xmit_cnt);
+#endif
+       return info->xmit_cnt;
+}
+
+static void rp_flush_buffer(struct tty_struct *tty)
+{
+       struct r_port * info = (struct r_port *)tty->driver_data;
+       CHANNEL_t       *cp;
+
+       if (rocket_paranoia_check(info, tty->device, "rp_flush_buffer"))
+               return;
+
+       cli();
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       sti();
+       wake_up_interruptible(&tty->write_wait);
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+           tty->ldisc.write_wakeup)
+               (tty->ldisc.write_wakeup)(tty);
+       
+       cp = &info->channel;
+       
+       sFlushTxFIFO(cp);
+}
+
+#ifdef ENABLE_PCI
+__initfunc(int register_PCI(int i, char bus, char device_fn))
+{
+       int     num_aiops, aiop, max_num_aiops, num_chan, chan;
+       unsigned int    aiopio[MAX_AIOPS_PER_BOARD];
+       char *str;
+       CONTROLLER_t    *ctlp;
+       unsigned short  vendor_id, device_id;
+       int     ret, error;
+       unsigned int port;
+
+       error = pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID,
+               &vendor_id);
+       ret = pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID,
+               &device_id);
+       if (error == 0)
+               error = ret;
+       ret = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,
+               &port);
+       rcktpt_io_addr[i] = (unsigned long) port;
+       if (error == 0)
+               error = ret;
+
+       if (error) {
+               printk("PCI RocketPort error: %s not initializing due to error"
+                      "reading configuration space\n",
+                      pcibios_strerror(error));
+               return(0);
+       }
+
+       --rcktpt_io_addr[i];
+       switch(device_id) {
+       case PCI_DEVICE_ID_RP4QUAD:
+               str = "Quadcable";
+               max_num_aiops = 1;
+               break;
+       case PCI_DEVICE_ID_RP8OCTA:
+               str = "Octacable";
+               max_num_aiops = 1;
+               break;
+       case PCI_DEVICE_ID_RP8INTF:
+               str = "8";
+               max_num_aiops = 1;
+               break;
+       case PCI_DEVICE_ID_RP8J:
+               str = "8J";
+               max_num_aiops = 1;
+               break;
+       case PCI_DEVICE_ID_RP16INTF:
+               str = "16";
+               max_num_aiops = 2;
+               break;
+       case PCI_DEVICE_ID_RP32INTF:
+               str = "32";
+               max_num_aiops = 4;
+               break;
+       default:
+               str = "(unknown/unsupported)";
+               max_num_aiops = 0;
+               break;
+       }
+       for(aiop=0;aiop < max_num_aiops;aiop++)
+               aiopio[aiop] = rcktpt_io_addr[i] + (aiop * 0x40);
+       ctlp = sCtlNumToCtlPtr(i);
+       num_aiops = sPCIInitController(ctlp, i,
+                                       aiopio, max_num_aiops, 0,
+                                       FREQ_DIS, 0);
+       printk("Rocketport controller #%d found at %d:%d, "
+              "%d AIOP(s) (PCI Rocketport %s)\n", i, bus, device_fn,
+              num_aiops, str);
+       if(num_aiops <= 0) {
+               rcktpt_io_addr[i] = 0;
+               return(0);
+       }
+       for(aiop = 0;aiop < num_aiops; aiop++) {
+               sResetAiopByNum(ctlp, aiop);
+               sEnAiop(ctlp, aiop);
+               num_chan = sGetAiopNumChan(ctlp, aiop);
+               for(chan=0;chan < num_chan; chan++)
+                       init_r_port(i, aiop, chan);
+       }
+       return(1);
+}
+
+__initfunc(static int init_PCI(int boards_found))
+{
+       unsigned char   bus, device_fn;
+       int     i, count = 0;
+
+       for(i=0; i < (NUM_BOARDS - boards_found); i++) {
+               if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8OCTA,
+                       i, &bus, &device_fn)) 
+                       if(register_PCI(count+boards_found, bus, device_fn))
+                               count++;
+               if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP8INTF,
+                       i, &bus, &device_fn)) 
+                       if(register_PCI(count+boards_found, bus, device_fn))
+                               count++;
+               if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP16INTF,
+                       i, &bus, &device_fn)) 
+                       if(register_PCI(count+boards_found, bus, device_fn))
+                               count++;
+               if(!pcibios_find_device(PCI_VENDOR_ID_RP, PCI_DEVICE_ID_RP32INTF,
+                       i, &bus, &device_fn)) 
+                       if(register_PCI(count+boards_found, bus, device_fn))
+                               count++;
+       }
+       return(count);
+}
+#endif
+
+__initfunc(static int init_ISA(int i, int *reserved_controller))
+{
+       int     num_aiops, num_chan;
+       int     aiop, chan;
+       unsigned int    aiopio[MAX_AIOPS_PER_BOARD];    
+       CONTROLLER_t    *ctlp;
+
+       if (rcktpt_io_addr[i] == 0)
+               return(0);
+
+       if (check_region(rcktpt_io_addr[i],64)) {
+               printk("RocketPort board address 0x%lx in use...\n",
+                       rcktpt_io_addr[i]);
+               rcktpt_io_addr[i] = 0;
+               return(0);
+       }
+       
+       for (aiop=0; aiop<MAX_AIOPS_PER_BOARD; aiop++)
+               aiopio[aiop]= rcktpt_io_addr[i] + (aiop * 0x400);
+       ctlp= sCtlNumToCtlPtr(i);
+       num_aiops = sInitController(ctlp, i, controller + (i*0x400),
+                                   aiopio, MAX_AIOPS_PER_BOARD, 0,
+                                   FREQ_DIS, 0);
+       if (num_aiops <= 0) {
+               rcktpt_io_addr[i] = 0;
+               return(0);
+       }
+       for (aiop = 0; aiop < num_aiops; aiop++) {
+               sResetAiopByNum(ctlp, aiop);
+               sEnAiop(ctlp, aiop);
+               num_chan = sGetAiopNumChan(ctlp,aiop);
+               for (chan=0; chan < num_chan; chan++)
+                       init_r_port(i, aiop, chan);
+       }
+       printk("Rocketport controller #%d found at 0x%lx, "
+              "%d AIOPs\n", i, rcktpt_io_addr[i],
+              num_aiops);
+       if (rcktpt_io_addr[i] + 0x40 == controller) {
+               *reserved_controller = 1;
+               request_region(rcktpt_io_addr[i], 68,
+                                      "Comtrol Rocketport");
+       } else {
+               request_region(rcktpt_io_addr[i], 64,
+                              "Comtrol Rocketport");
+       }
+       return(1);
+}
+
+
+/*
+ * The module "startup" routine; it's run when the module is loaded.
+ */
+__initfunc(int rp_init(void))
+{
+       int i, retval, pci_boards_found, isa_boards_found;
+       int     reserved_controller = 0;
+
+       printk("Rocketport device driver module, version %s, %s\n",
+              ROCKET_VERSION, ROCKET_DATE);
+
+       /*
+        * Set up the timer channel.  If it is already in use by
+        * some other driver, give up.
+        */
+       if (timer_table[COMTROL_TIMER].fn) {
+               printk("rocket.o: Timer channel %d already in use!\n",
+                      COMTROL_TIMER);
+               return -EBUSY;
+       }
+       timer_table[COMTROL_TIMER].fn = rp_do_poll;
+       timer_table[COMTROL_TIMER].expires = 0;
+       
+       /*
+        * Initialize the array of pointers to our own internal state
+        * structures.
+        */
+       memset(rp_table, 0, sizeof(rp_table));
+       memset(xmit_flags, 0, sizeof(xmit_flags));
+
+       if (board1 == 0)
+               board1 = 0x180;
+       if (controller == 0)
+               controller = board1 + 0x40;
+
+       if (check_region(controller, 4)) {
+               printk("Controller IO addresses in use, unloading driver.\n");
+               return -EBUSY;
+       }
+       
+       rcktpt_io_addr[0] = board1;
+       rcktpt_io_addr[1] = board2;
+       rcktpt_io_addr[2] = board3;
+       rcktpt_io_addr[3] = board4;
+
+       /*
+        * If support_low_speed is set, use the slow clock prescale,
+        * which supports 50 bps
+        */
+       if (support_low_speed) {
+               sClockPrescale = 0x19;  /* mod 9 (divide by 10) prescale */
+               rp_baud_base = 230400;
+       } else {
+               sClockPrescale = 0x14; /* mod 4 (devide by 5) prescale */
+               rp_baud_base = 460800;
+       }
+       
+       /*
+        * OK, let's probe each of the controllers looking for boards.
+        */
+       isa_boards_found = 0;
+       pci_boards_found = 0;
+       for (i=0; i < NUM_BOARDS; i++) {
+               if(init_ISA(i, &reserved_controller))
+                       isa_boards_found++;
+       }
+#ifdef ENABLE_PCI
+       if (pcibios_present()) {
+               if(isa_boards_found < NUM_BOARDS)
+                       pci_boards_found = init_PCI(isa_boards_found);
+       } else {
+               printk("No PCI BIOS found\n");
+       }
+#endif
+       max_board = pci_boards_found + isa_boards_found;
+       
+       if (max_board == 0) {
+               printk("No rocketport ports found; unloading driver.\n");
+               timer_table[COMTROL_TIMER].fn = 0;
+               return -ENODEV;
+       }
+
+       if (reserved_controller == 0)
+               request_region(controller, 4, "Comtrol Rocketport");
+
+       /*
+        * Set up the tty driver structure and then register this
+        * driver with the tty layer.
+        */
+       memset(&rocket_driver, 0, sizeof(struct tty_driver));
+       rocket_driver.magic = TTY_DRIVER_MAGIC;
+       rocket_driver.name = "ttyR";
+       rocket_driver.major = TTY_ROCKET_MAJOR;
+       rocket_driver.minor_start = 0;
+       rocket_driver.num = MAX_RP_PORTS;
+       rocket_driver.type = TTY_DRIVER_TYPE_SERIAL;
+       rocket_driver.subtype = SERIAL_TYPE_NORMAL;
+       rocket_driver.init_termios = tty_std_termios;
+       rocket_driver.init_termios.c_cflag =
+               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       rocket_driver.flags = TTY_DRIVER_REAL_RAW;
+       rocket_driver.refcount = &rocket_refcount;
+       rocket_driver.table = rocket_table;
+       rocket_driver.termios = rocket_termios;
+       rocket_driver.termios_locked = rocket_termios_locked;
+
+       rocket_driver.open = rp_open;
+       rocket_driver.close = rp_close;
+       rocket_driver.write = rp_write;
+       rocket_driver.put_char = rp_put_char;
+       rocket_driver.write_room = rp_write_room;
+       rocket_driver.chars_in_buffer = rp_chars_in_buffer;
+       rocket_driver.flush_buffer = rp_flush_buffer;
+       rocket_driver.ioctl = rp_ioctl;
+       rocket_driver.throttle = rp_throttle;
+       rocket_driver.unthrottle = rp_unthrottle;
+       rocket_driver.set_termios = rp_set_termios;
+       rocket_driver.stop = rp_stop;
+       rocket_driver.start = rp_start;
+       rocket_driver.hangup = rp_hangup;
+#if (LINUX_VERSION_CODE >= 131343)
+       rocket_driver.send_xchar = rp_send_xchar;
+       rocket_driver.wait_until_sent = rp_wait_until_sent;
+#endif
+
+       /*
+        * The callout device is just like normal device except for
+        * the minor number and the subtype code.
+        */
+       callout_driver = rocket_driver;
+       callout_driver.name = "cur";
+       callout_driver.major = CUA_ROCKET_MAJOR;
+       callout_driver.minor_start = 0;
+       callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+       
+       retval = tty_register_driver(&callout_driver);
+       if (retval < 0) {
+               printk("Couldn't install Rocketport callout driver "
+                      "(error %d)\n", -retval);
+               return -1;
+       }
+
+       retval = tty_register_driver(&rocket_driver);
+       if (retval < 0) {
+               printk("Couldn't install tty Rocketport driver "
+                      "(error %d)\n", -retval);
+               return -1;
+       }
+#ifdef ROCKET_DEBUG_OPEN
+       printk("Rocketport driver is major %d, callout is %d\n",
+              rocket_driver.major, callout_driver.major);
+#endif
+
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       return rp_init();
+}
+
+void
+cleanup_module( void) {
+       int     retval;
+       int     i;
+       int     released_controller = 0;
+       
+       retval = tty_unregister_driver(&callout_driver);
+       if (retval) {
+               printk("Error %d while trying to unregister "
+                      "rocketport callout driver\n", -retval);
+       }
+       retval = tty_unregister_driver(&rocket_driver);
+       if (retval) {
+               printk("Error %d while trying to unregister "
+                      "rocketport driver\n", -retval);
+       }
+       for (i = 0; i < MAX_RP_PORTS; i++) {
+               if (rp_table[i])
+                       kfree(rp_table[i]);
+       }
+       for (i=0; i < NUM_BOARDS; i++) {
+               if (rcktpt_io_addr[i] <= 0)
+                       continue;
+               if (rcktpt_io_addr[i] + 0x40 == controller) {
+                       released_controller++;
+                       release_region(rcktpt_io_addr[i], 68);
+               } else
+                       release_region(rcktpt_io_addr[i], 64);
+               if (released_controller == 0)
+                       release_region(controller, 4);
+       }
+       if (tmp_buf)
+               free_page((unsigned long) tmp_buf);
+       timer_table[COMTROL_TIMER].fn = 0;
+}
+#endif
+
+/***********************************************************************
+               Copyright 1994 Comtrol Corporation.
+                       All Rights Reserved.
+
+The following source code is subject to Comtrol Corporation's
+Developer's License Agreement.
+
+This source code is protected by United States copyright law and 
+international copyright treaties.
+
+This source code may only be used to develop software products that
+will operate with Comtrol brand hardware.
+
+You may not reproduce nor distribute this source code in its original
+form but must produce a derivative work which includes portions of
+this source code only.
+
+The portions of this source code which you use in your derivative
+work must bear Comtrol's copyright notice:
+
+               Copyright 1994 Comtrol Corporation.
+
+***********************************************************************/
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+static Byte_t RData[RDATASIZE] =
+{
+   0x00, 0x09, 0xf6, 0x82,
+   0x02, 0x09, 0x86, 0xfb,
+   0x04, 0x09, 0x00, 0x0a,
+   0x06, 0x09, 0x01, 0x0a,
+   0x08, 0x09, 0x8a, 0x13,
+   0x0a, 0x09, 0xc5, 0x11,
+   0x0c, 0x09, 0x86, 0x85,
+   0x0e, 0x09, 0x20, 0x0a,
+   0x10, 0x09, 0x21, 0x0a,
+   0x12, 0x09, 0x41, 0xff,
+   0x14, 0x09, 0x82, 0x00,
+   0x16, 0x09, 0x82, 0x7b,
+   0x18, 0x09, 0x8a, 0x7d,
+   0x1a, 0x09, 0x88, 0x81,
+   0x1c, 0x09, 0x86, 0x7a,
+   0x1e, 0x09, 0x84, 0x81,
+   0x20, 0x09, 0x82, 0x7c,
+   0x22, 0x09, 0x0a, 0x0a 
+};
+
+static Byte_t RRegData[RREGDATASIZE]=
+{
+   0x00, 0x09, 0xf6, 0x82,             /* 00: Stop Rx processor */
+   0x08, 0x09, 0x8a, 0x13,             /* 04: Tx software flow control */
+   0x0a, 0x09, 0xc5, 0x11,             /* 08: XON char */
+   0x0c, 0x09, 0x86, 0x85,             /* 0c: XANY */
+   0x12, 0x09, 0x41, 0xff,             /* 10: Rx mask char */
+   0x14, 0x09, 0x82, 0x00,             /* 14: Compare/Ignore #0 */
+   0x16, 0x09, 0x82, 0x7b,             /* 18: Compare #1 */
+   0x18, 0x09, 0x8a, 0x7d,             /* 1c: Compare #2 */
+   0x1a, 0x09, 0x88, 0x81,             /* 20: Interrupt #1 */
+   0x1c, 0x09, 0x86, 0x7a,             /* 24: Ignore/Replace #1 */
+   0x1e, 0x09, 0x84, 0x81,             /* 28: Interrupt #2 */
+   0x20, 0x09, 0x82, 0x7c,             /* 2c: Ignore/Replace #2 */
+   0x22, 0x09, 0x0a, 0x0a              /* 30: Rx FIFO Enable */
+};
+
+CONTROLLER_T sController[CTL_SIZE] =
+{
+   {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}},
+   {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}},
+   {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}},
+   {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}
+};
+
+#if 0
+/* IRQ number to MUDBAC register 2 mapping */
+Byte_t sIRQMap[16] =
+{
+   0,0,0,0x10,0x20,0x30,0,0,0,0x40,0x50,0x60,0x70,0,0,0x80
+};
+#endif
+
+Byte_t sBitMapClrTbl[8] =
+{
+   0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f
+};
+
+Byte_t sBitMapSetTbl[8] =
+{
+   0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
+};
+
+int sClockPrescale = 0x14;
+
+/***************************************************************************
+Function: sInitController
+Purpose:  Initialization of controller global registers and controller
+          structure.
+Call:     sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize,
+                          IRQNum,Frequency,PeriodicOnly)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int CtlNum; Controller number
+          ByteIO_t MudbacIO; Mudbac base I/O address.
+          ByteIO_t *AiopIOList; List of I/O addresses for each AIOP.
+             This list must be in the order the AIOPs will be found on the
+             controller.  Once an AIOP in the list is not found, it is
+             assumed that there are no more AIOPs on the controller.
+          int AiopIOListSize; Number of addresses in AiopIOList
+          int IRQNum; Interrupt Request number.  Can be any of the following:
+                         0: Disable global interrupts
+                         3: IRQ 3
+                         4: IRQ 4
+                         5: IRQ 5
+                         9: IRQ 9
+                         10: IRQ 10
+                         11: IRQ 11
+                         12: IRQ 12
+                         15: IRQ 15
+          Byte_t Frequency: A flag identifying the frequency
+                   of the periodic interrupt, can be any one of the following:
+                      FREQ_DIS - periodic interrupt disabled
+                      FREQ_137HZ - 137 Hertz
+                      FREQ_69HZ - 69 Hertz
+                      FREQ_34HZ - 34 Hertz
+                      FREQ_17HZ - 17 Hertz
+                      FREQ_9HZ - 9 Hertz
+                      FREQ_4HZ - 4 Hertz
+                   If IRQNum is set to 0 the Frequency parameter is
+                   overidden, it is forced to a value of FREQ_DIS.
+          int PeriodicOnly: TRUE if all interrupts except the periodic
+                               interrupt are to be blocked.
+                            FALSE is both the periodic interrupt and
+                               other channel interrupts are allowed.
+                            If IRQNum is set to 0 the PeriodicOnly parameter is
+                               overidden, it is forced to a value of FALSE.
+Return:   int: Number of AIOPs on the controller, or CTLID_NULL if controller
+               initialization failed.
+
+Comments:
+          If periodic interrupts are to be disabled but AIOP interrupts
+          are allowed, set Frequency to FREQ_DIS and PeriodicOnly to FALSE.
+
+          If interrupts are to be completely disabled set IRQNum to 0.
+
+          Setting Frequency to FREQ_DIS and PeriodicOnly to TRUE is an
+          invalid combination.
+
+          This function performs initialization of global interrupt modes,
+          but it does not actually enable global interrupts.  To enable
+          and disable global interrupts use functions sEnGlobalInt() and
+          sDisGlobalInt().  Enabling of global interrupts is normally not
+          done until all other initializations are complete.
+
+          Even if interrupts are globally enabled, they must also be
+          individually enabled for each channel that is to generate
+          interrupts.
+
+Warnings: No range checking on any of the parameters is done.
+
+          No context switches are allowed while executing this function.
+
+          After this function all AIOPs on the controller are disabled,
+          they can be enabled with sEnAiop().
+*/
+int sInitController(   CONTROLLER_T *CtlP,
+                       int CtlNum,
+                       ByteIO_t MudbacIO,
+                       ByteIO_t *AiopIOList,
+                       int AiopIOListSize,
+                       int IRQNum,
+                       Byte_t Frequency,
+                       int PeriodicOnly)
+{
+       int             i;
+       ByteIO_t        io;
+
+   CtlP->CtlNum = CtlNum;
+   CtlP->CtlID = CTLID_0001;        /* controller release 1 */
+   CtlP->BusType = isISA;     
+   CtlP->MBaseIO = MudbacIO;
+   CtlP->MReg1IO = MudbacIO + 1;
+   CtlP->MReg2IO = MudbacIO + 2;
+   CtlP->MReg3IO = MudbacIO + 3;
+#if 1
+   CtlP->MReg2 = 0;                 /* interrupt disable */
+   CtlP->MReg3 = 0;                 /* no periodic interrupts */
+#else
+   if(sIRQMap[IRQNum] == 0)            /* interrupts globally disabled */
+   {
+      CtlP->MReg2 = 0;                 /* interrupt disable */
+      CtlP->MReg3 = 0;                 /* no periodic interrupts */
+   }
+   else
+   {
+      CtlP->MReg2 = sIRQMap[IRQNum];   /* set IRQ number */
+      CtlP->MReg3 = Frequency;         /* set frequency */
+      if(PeriodicOnly)                 /* periodic interrupt only */
+      {
+         CtlP->MReg3 |= PERIODIC_ONLY;
+      }
+   }
+#endif
+   sOutB(CtlP->MReg2IO,CtlP->MReg2);
+   sOutB(CtlP->MReg3IO,CtlP->MReg3);
+   sControllerEOI(CtlP);               /* clear EOI if warm init */
+   /* Init AIOPs */
+   CtlP->NumAiop = 0;
+   for(i=0; i < AiopIOListSize; i++)
+   {
+      io = AiopIOList[i];
+      CtlP->AiopIO[i] = (WordIO_t)io;
+      CtlP->AiopIntChanIO[i] = io + _INT_CHAN;
+      sOutB(CtlP->MReg2IO,CtlP->MReg2 | (i & 0x03)); /* AIOP index */
+      sOutB(MudbacIO,(Byte_t)(io >> 6));       /* set up AIOP I/O in MUDBAC */
+      sEnAiop(CtlP,i);                         /* enable the AIOP */
+
+      CtlP->AiopID[i] = sReadAiopID(io);       /* read AIOP ID */
+      if(CtlP->AiopID[i] == AIOPID_NULL)       /* if AIOP does not exist */
+      {
+         sDisAiop(CtlP,i);                     /* disable AIOP */
+         break;                                /* done looking for AIOPs */
+      }
+
+      CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t)io); /* num channels in AIOP */
+      sOutW((WordIO_t)io + _INDX_ADDR,_CLK_PRE);      /* clock prescaler */
+      sOutB(io + _INDX_DATA,sClockPrescale);
+      CtlP->NumAiop++;                         /* bump count of AIOPs */
+      sDisAiop(CtlP,i);                        /* disable AIOP */
+   }
+
+   if(CtlP->NumAiop == 0)
+      return(-1);
+   else
+      return(CtlP->NumAiop);
+}
+
+/***************************************************************************
+Function: sPCIInitController
+Purpose:  Initialization of controller global registers and controller
+          structure.
+Call:     sPCIInitController(CtlP,CtlNum,AiopIOList,AiopIOListSize,
+                          IRQNum,Frequency,PeriodicOnly)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int CtlNum; Controller number
+          ByteIO_t *AiopIOList; List of I/O addresses for each AIOP.
+             This list must be in the order the AIOPs will be found on the
+             controller.  Once an AIOP in the list is not found, it is
+             assumed that there are no more AIOPs on the controller.
+          int AiopIOListSize; Number of addresses in AiopIOList
+          int IRQNum; Interrupt Request number.  Can be any of the following:
+                         0: Disable global interrupts
+                         3: IRQ 3
+                         4: IRQ 4
+                         5: IRQ 5
+                         9: IRQ 9
+                         10: IRQ 10
+                         11: IRQ 11
+                         12: IRQ 12
+                         15: IRQ 15
+          Byte_t Frequency: A flag identifying the frequency
+                   of the periodic interrupt, can be any one of the following:
+                      FREQ_DIS - periodic interrupt disabled
+                      FREQ_137HZ - 137 Hertz
+                      FREQ_69HZ - 69 Hertz
+                      FREQ_34HZ - 34 Hertz
+                      FREQ_17HZ - 17 Hertz
+                      FREQ_9HZ - 9 Hertz
+                      FREQ_4HZ - 4 Hertz
+                   If IRQNum is set to 0 the Frequency parameter is
+                   overidden, it is forced to a value of FREQ_DIS.
+          int PeriodicOnly: TRUE if all interrupts except the periodic
+                               interrupt are to be blocked.
+                            FALSE is both the periodic interrupt and
+                               other channel interrupts are allowed.
+                            If IRQNum is set to 0 the PeriodicOnly parameter is
+                               overidden, it is forced to a value of FALSE.
+Return:   int: Number of AIOPs on the controller, or CTLID_NULL if controller
+               initialization failed.
+
+Comments:
+          If periodic interrupts are to be disabled but AIOP interrupts
+          are allowed, set Frequency to FREQ_DIS and PeriodicOnly to FALSE.
+
+          If interrupts are to be completely disabled set IRQNum to 0.
+
+          Setting Frequency to FREQ_DIS and PeriodicOnly to TRUE is an
+          invalid combination.
+
+          This function performs initialization of global interrupt modes,
+          but it does not actually enable global interrupts.  To enable
+          and disable global interrupts use functions sEnGlobalInt() and
+          sDisGlobalInt().  Enabling of global interrupts is normally not
+          done until all other initializations are complete.
+
+          Even if interrupts are globally enabled, they must also be
+          individually enabled for each channel that is to generate
+          interrupts.
+
+Warnings: No range checking on any of the parameters is done.
+
+          No context switches are allowed while executing this function.
+
+          After this function all AIOPs on the controller are disabled,
+          they can be enabled with sEnAiop().
+*/
+int sPCIInitController(        CONTROLLER_T *CtlP,
+                       int CtlNum,
+                       ByteIO_t *AiopIOList,
+                       int AiopIOListSize,
+                       int IRQNum,
+                       Byte_t Frequency,
+                       int PeriodicOnly)
+{
+       int             i;
+       ByteIO_t        io;
+
+   CtlP->CtlNum = CtlNum;
+   CtlP->CtlID = CTLID_0001;        /* controller release 1 */
+   CtlP->BusType = isPCI;        /* controller release 1 */
+
+   CtlP->PCIIO = (WordIO_t)((ByteIO_t)AiopIOList[0] + _PCI_INT_FUNC);
+
+   sPCIControllerEOI(CtlP);               /* clear EOI if warm init */
+   /* Init AIOPs */
+   CtlP->NumAiop = 0;
+   for(i=0; i < AiopIOListSize; i++)
+   {
+      io = AiopIOList[i];
+      CtlP->AiopIO[i] = (WordIO_t)io;
+      CtlP->AiopIntChanIO[i] = io + _INT_CHAN;
+
+      CtlP->AiopID[i] = sReadAiopID(io);       /* read AIOP ID */
+      if(CtlP->AiopID[i] == AIOPID_NULL)       /* if AIOP does not exist */
+         break;                                /* done looking for AIOPs */
+
+      CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t)io); /* num channels in AIOP */
+      sOutW((WordIO_t)io + _INDX_ADDR,_CLK_PRE);      /* clock prescaler */
+      sOutB(io + _INDX_DATA,sClockPrescale);
+      CtlP->NumAiop++;                         /* bump count of AIOPs */
+   }
+
+   if(CtlP->NumAiop == 0)
+      return(-1);
+   else
+      return(CtlP->NumAiop);
+}
+
+/***************************************************************************
+Function: sReadAiopID
+Purpose:  Read the AIOP idenfication number directly from an AIOP.
+Call:     sReadAiopID(io)
+          ByteIO_t io: AIOP base I/O address
+Return:   int: Flag AIOPID_XXXX if a valid AIOP is found, where X
+                 is replace by an identifying number.
+          Flag AIOPID_NULL if no valid AIOP is found
+Warnings: No context switches are allowed while executing this function.
+
+*/
+int sReadAiopID(ByteIO_t io)
+{
+   Byte_t AiopID;               /* ID byte from AIOP */
+
+   sOutB(io + _CMD_REG,RESET_ALL);     /* reset AIOP */
+   sOutB(io + _CMD_REG,0x0);
+   AiopID = sInB(io + _CHN_STAT0) & 0x07;
+   if(AiopID == 0x06)
+      return(1);
+   else                                /* AIOP does not exist */
+      return(-1);
+}
+
+/***************************************************************************
+Function: sReadAiopNumChan
+Purpose:  Read the number of channels available in an AIOP directly from
+          an AIOP.
+Call:     sReadAiopNumChan(io)
+          WordIO_t io: AIOP base I/O address
+Return:   int: The number of channels available
+Comments: The number of channels is determined by write/reads from identical
+          offsets within the SRAM address spaces for channels 0 and 4.
+          If the channel 4 space is mirrored to channel 0 it is a 4 channel
+          AIOP, otherwise it is an 8 channel.
+Warnings: No context switches are allowed while executing this function.
+*/
+int sReadAiopNumChan(WordIO_t io)
+{
+   Word_t x;
+
+   sOutDW((DWordIO_t)io + _INDX_ADDR,0x12340000L); /* write to chan 0 SRAM */
+   sOutW(io + _INDX_ADDR,0);       /* read from SRAM, chan 0 */
+   x = sInW(io + _INDX_DATA);
+   sOutW(io + _INDX_ADDR,0x4000);  /* read from SRAM, chan 4 */
+   if(x != sInW(io + _INDX_DATA))  /* if different must be 8 chan */
+      return(8);
+   else
+      return(4);
+}
+
+/***************************************************************************
+Function: sInitChan
+Purpose:  Initialization of a channel and channel structure
+Call:     sInitChan(CtlP,ChP,AiopNum,ChanNum)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          CHANNEL_T *ChP; Ptr to channel structure
+          int AiopNum; AIOP number within controller
+          int ChanNum; Channel number within AIOP
+Return:   int: TRUE if initialization succeeded, FALSE if it fails because channel
+               number exceeds number of channels available in AIOP.
+Comments: This function must be called before a channel can be used.
+Warnings: No range checking on any of the parameters is done.
+
+          No context switches are allowed while executing this function.
+*/
+int sInitChan( CONTROLLER_T *CtlP,
+               CHANNEL_T *ChP,
+               int AiopNum,
+               int ChanNum)
+{
+   int i;
+   WordIO_t AiopIO;
+   WordIO_t ChIOOff;
+   Byte_t *ChR;
+   Word_t ChOff;
+   static Byte_t R[4];
+   int brd9600;
+
+   if(ChanNum >= CtlP->AiopNumChan[AiopNum])
+      return(FALSE);                   /* exceeds num chans in AIOP */
+
+   /* Channel, AIOP, and controller identifiers */
+   ChP->CtlP = CtlP;
+   ChP->ChanID = CtlP->AiopID[AiopNum];
+   ChP->AiopNum = AiopNum;
+   ChP->ChanNum = ChanNum;
+
+   /* Global direct addresses */
+   AiopIO = CtlP->AiopIO[AiopNum];
+   ChP->Cmd = (ByteIO_t)AiopIO + _CMD_REG;
+   ChP->IntChan = (ByteIO_t)AiopIO + _INT_CHAN;
+   ChP->IntMask = (ByteIO_t)AiopIO + _INT_MASK;
+   ChP->IndexAddr = (DWordIO_t)AiopIO + _INDX_ADDR;
+   ChP->IndexData = AiopIO + _INDX_DATA;
+
+   /* Channel direct addresses */
+   ChIOOff = AiopIO + ChP->ChanNum * 2;
+   ChP->TxRxData = ChIOOff + _TD0;
+   ChP->ChanStat = ChIOOff + _CHN_STAT0;
+   ChP->TxRxCount = ChIOOff + _FIFO_CNT0;
+   ChP->IntID = (ByteIO_t)AiopIO + ChP->ChanNum + _INT_ID0;
+
+   /* Initialize the channel from the RData array */
+   for(i=0; i < RDATASIZE; i+=4)
+   {
+      R[0] = RData[i];
+      R[1] = RData[i+1] + 0x10 * ChanNum;
+      R[2] = RData[i+2];
+      R[3] = RData[i+3];
+      sOutDW(ChP->IndexAddr,*((DWord_t *)&R[0]));
+   }
+
+   ChR = ChP->R;
+   for(i=0; i < RREGDATASIZE; i+=4)
+   {
+      ChR[i] = RRegData[i];
+      ChR[i+1] = RRegData[i+1] + 0x10 * ChanNum;
+      ChR[i+2] = RRegData[i+2];
+      ChR[i+3] = RRegData[i+3];
+   }
+
+   /* Indexed registers */
+   ChOff = (Word_t)ChanNum * 0x1000;
+
+   if (sClockPrescale == 0x14)
+          brd9600 = 47;
+   else
+          brd9600 = 23;
+
+   ChP->BaudDiv[0] = (Byte_t)(ChOff + _BAUD);
+   ChP->BaudDiv[1] = (Byte_t)((ChOff + _BAUD) >> 8);
+   ChP->BaudDiv[2] = (Byte_t)brd9600;
+   ChP->BaudDiv[3] = (Byte_t)(brd9600 >> 8);
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->BaudDiv[0]);
+
+   ChP->TxControl[0] = (Byte_t)(ChOff + _TX_CTRL);
+   ChP->TxControl[1] = (Byte_t)((ChOff + _TX_CTRL) >> 8);
+   ChP->TxControl[2] = 0;
+   ChP->TxControl[3] = 0;
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]);
+
+   ChP->RxControl[0] = (Byte_t)(ChOff + _RX_CTRL);
+   ChP->RxControl[1] = (Byte_t)((ChOff + _RX_CTRL) >> 8);
+   ChP->RxControl[2] = 0;
+   ChP->RxControl[3] = 0;
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]);
+
+   ChP->TxEnables[0] = (Byte_t)(ChOff + _TX_ENBLS);
+   ChP->TxEnables[1] = (Byte_t)((ChOff + _TX_ENBLS) >> 8);
+   ChP->TxEnables[2] = 0;
+   ChP->TxEnables[3] = 0;
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxEnables[0]);
+
+   ChP->TxCompare[0] = (Byte_t)(ChOff + _TXCMP1);
+   ChP->TxCompare[1] = (Byte_t)((ChOff + _TXCMP1) >> 8);
+   ChP->TxCompare[2] = 0;
+   ChP->TxCompare[3] = 0;
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxCompare[0]);
+
+   ChP->TxReplace1[0] = (Byte_t)(ChOff + _TXREP1B1);
+   ChP->TxReplace1[1] = (Byte_t)((ChOff + _TXREP1B1) >> 8);
+   ChP->TxReplace1[2] = 0;
+   ChP->TxReplace1[3] = 0;
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxReplace1[0]);
+
+   ChP->TxReplace2[0] = (Byte_t)(ChOff + _TXREP2);
+   ChP->TxReplace2[1] = (Byte_t)((ChOff + _TXREP2) >> 8);
+   ChP->TxReplace2[2] = 0;
+   ChP->TxReplace2[3] = 0;
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxReplace2[0]);
+
+   ChP->TxFIFOPtrs = ChOff + _TXF_OUTP;
+   ChP->TxFIFO = ChOff + _TX_FIFO;
+
+   sOutB(ChP->Cmd,(Byte_t)ChanNum | RESTXFCNT); /* apply reset Tx FIFO count */
+   sOutB(ChP->Cmd,(Byte_t)ChanNum);  /* remove reset Tx FIFO count */
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */
+   sOutW(ChP->IndexData,0);
+   ChP->RxFIFOPtrs = ChOff + _RXF_OUTP;
+   ChP->RxFIFO = ChOff + _RX_FIFO;
+
+   sOutB(ChP->Cmd,(Byte_t)ChanNum | RESRXFCNT); /* apply reset Rx FIFO count */
+   sOutB(ChP->Cmd,(Byte_t)ChanNum);  /* remove reset Rx FIFO count */
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs); /* clear Rx out ptr */
+   sOutW(ChP->IndexData,0);
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */
+   sOutW(ChP->IndexData,0);
+   ChP->TxPrioCnt = ChOff + _TXP_CNT;
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->TxPrioCnt);
+   sOutB(ChP->IndexData,0);
+   ChP->TxPrioPtr = ChOff + _TXP_PNTR;
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->TxPrioPtr);
+   sOutB(ChP->IndexData,0);
+   ChP->TxPrioBuf = ChOff + _TXP_BUF;
+   sEnRxProcessor(ChP);                /* start the Rx processor */
+
+   return(TRUE);
+}
+
+/***************************************************************************
+Function: sStopRxProcessor
+Purpose:  Stop the receive processor from processing a channel.
+Call:     sStopRxProcessor(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+
+Comments: The receive processor can be started again with sStartRxProcessor().
+          This function causes the receive processor to skip over the
+          stopped channel.  It does not stop it from processing other channels.
+
+Warnings: No context switches are allowed while executing this function.
+
+          Do not leave the receive processor stopped for more than one
+          character time.
+
+          After calling this function a delay of 4 uS is required to ensure
+          that the receive processor is no longer processing this channel.
+*/
+void sStopRxProcessor(CHANNEL_T *ChP)
+{
+   Byte_t R[4];
+
+   R[0] = ChP->R[0];
+   R[1] = ChP->R[1];
+   R[2] = 0x0a;
+   R[3] = ChP->R[3];
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&R[0]);
+}
+
+/***************************************************************************
+Function: sFlushRxFIFO
+Purpose:  Flush the Rx FIFO
+Call:     sFlushRxFIFO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   void
+Comments: To prevent data from being enqueued or dequeued in the Tx FIFO
+          while it is being flushed the receive processor is stopped
+          and the transmitter is disabled.  After these operations a
+          4 uS delay is done before clearing the pointers to allow
+          the receive processor to stop.  These items are handled inside
+          this function.
+Warnings: No context switches are allowed while executing this function.
+*/
+void sFlushRxFIFO(CHANNEL_T *ChP)
+{
+   int i;
+   Byte_t Ch;                   /* channel number within AIOP */
+   int RxFIFOEnabled;                  /* TRUE if Rx FIFO enabled */
+
+   if(sGetRxCnt(ChP) == 0)             /* Rx FIFO empty */
+      return;                          /* don't need to flush */
+
+   RxFIFOEnabled = FALSE;
+   if(ChP->R[0x32] == 0x08) /* Rx FIFO is enabled */
+   {
+      RxFIFOEnabled = TRUE;
+      sDisRxFIFO(ChP);                 /* disable it */
+      for(i=0; i < 2000/200; i++)      /* delay 2 uS to allow proc to disable FIFO*/
+         sInB(ChP->IntChan);           /* depends on bus i/o timing */
+   }
+   sGetChanStatus(ChP);          /* clear any pending Rx errors in chan stat */
+   Ch = (Byte_t)sGetChanNum(ChP);
+   sOutB(ChP->Cmd,Ch | RESRXFCNT);     /* apply reset Rx FIFO count */
+   sOutB(ChP->Cmd,Ch);                 /* remove reset Rx FIFO count */
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs); /* clear Rx out ptr */
+   sOutW(ChP->IndexData,0);
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */
+   sOutW(ChP->IndexData,0);
+   if(RxFIFOEnabled)
+      sEnRxFIFO(ChP);                  /* enable Rx FIFO */
+}
+
+/***************************************************************************
+Function: sFlushTxFIFO
+Purpose:  Flush the Tx FIFO
+Call:     sFlushTxFIFO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   void
+Comments: To prevent data from being enqueued or dequeued in the Tx FIFO
+          while it is being flushed the receive processor is stopped
+          and the transmitter is disabled.  After these operations a
+          4 uS delay is done before clearing the pointers to allow
+          the receive processor to stop.  These items are handled inside
+          this function.
+Warnings: No context switches are allowed while executing this function.
+*/
+void sFlushTxFIFO(CHANNEL_T *ChP)
+{
+   int i;
+   Byte_t Ch;                   /* channel number within AIOP */
+   int TxEnabled;                      /* TRUE if transmitter enabled */
+
+   if(sGetTxCnt(ChP) == 0)             /* Tx FIFO empty */
+      return;                          /* don't need to flush */
+
+   TxEnabled = FALSE;
+   if(ChP->TxControl[3] & TX_ENABLE)
+   {
+      TxEnabled = TRUE;
+      sDisTransmit(ChP);               /* disable transmitter */
+   }
+   sStopRxProcessor(ChP);              /* stop Rx processor */
+   for(i = 0; i < 4000/200; i++)         /* delay 4 uS to allow proc to stop */
+      sInB(ChP->IntChan);      /* depends on bus i/o timing */
+   Ch = (Byte_t)sGetChanNum(ChP);
+   sOutB(ChP->Cmd,Ch | RESTXFCNT);     /* apply reset Tx FIFO count */
+   sOutB(ChP->Cmd,Ch);                 /* remove reset Tx FIFO count */
+   sOutW((WordIO_t)ChP->IndexAddr,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */
+   sOutW(ChP->IndexData,0);
+   if(TxEnabled)
+      sEnTransmit(ChP);                /* enable transmitter */
+   sStartRxProcessor(ChP);             /* restart Rx processor */
+}
+
+/***************************************************************************
+Function: sWriteTxPrioByte
+Purpose:  Write a byte of priority transmit data to a channel
+Call:     sWriteTxPrioByte(ChP,Data)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Byte_t Data; The transmit data byte
+
+Return:   int: 1 if the bytes is successfully written, otherwise 0.
+
+Comments: The priority byte is transmitted before any data in the Tx FIFO.
+
+Warnings: No context switches are allowed while executing this function.
+*/
+int sWriteTxPrioByte(CHANNEL_T *ChP, Byte_t Data)
+{
+   Byte_t DWBuf[4];             /* buffer for double word writes */
+   Word_t *WordPtr;          /* must be far because Win SS != DS */
+   register DWordIO_t IndexAddr;
+
+   if(sGetTxCnt(ChP) > 1)              /* write it to Tx priority buffer */
+   {
+      IndexAddr = ChP->IndexAddr;
+      sOutW((WordIO_t)IndexAddr,ChP->TxPrioCnt); /* get priority buffer status */
+      if(sInB((ByteIO_t)ChP->IndexData) & PRI_PEND) /* priority buffer busy */
+         return(0);                    /* nothing sent */
+
+      WordPtr = (Word_t *)(&DWBuf[0]);
+      *WordPtr = ChP->TxPrioBuf;       /* data byte address */
+
+      DWBuf[2] = Data;                 /* data byte value */
+      sOutDW(IndexAddr,*((DWord_t *)(&DWBuf[0]))); /* write it out */
+
+      *WordPtr = ChP->TxPrioCnt;       /* Tx priority count address */
+
+      DWBuf[2] = PRI_PEND + 1;         /* indicate 1 byte pending */
+      DWBuf[3] = 0;                    /* priority buffer pointer */
+      sOutDW(IndexAddr,*((DWord_t *)(&DWBuf[0]))); /* write it out */
+   }
+   else                                /* write it to Tx FIFO */
+   {
+      sWriteTxByte(sGetTxRxDataIO(ChP),Data);
+   }
+   return(1);                          /* 1 byte sent */
+}
+
+/***************************************************************************
+Function: sEnInterrupts
+Purpose:  Enable one or more interrupts for a channel
+Call:     sEnInterrupts(ChP,Flags)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Word_t Flags: Interrupt enable flags, can be any combination
+             of the following flags:
+                TXINT_EN:   Interrupt on Tx FIFO empty
+                RXINT_EN:   Interrupt on Rx FIFO at trigger level (see
+                            sSetRxTrigger())
+                SRCINT_EN:  Interrupt on SRC (Special Rx Condition)
+                MCINT_EN:   Interrupt on modem input change
+                CHANINT_EN: Allow channel interrupt signal to the AIOP's
+                            Interrupt Channel Register.
+Return:   void
+Comments: If an interrupt enable flag is set in Flags, that interrupt will be
+          enabled.  If an interrupt enable flag is not set in Flags, that
+          interrupt will not be changed.  Interrupts can be disabled with
+          function sDisInterrupts().
+
+          This function sets the appropriate bit for the channel in the AIOP's
+          Interrupt Mask Register if the CHANINT_EN flag is set.  This allows
+          this channel's bit to be set in the AIOP's Interrupt Channel Register.
+
+          Interrupts must also be globally enabled before channel interrupts
+          will be passed on to the host.  This is done with function
+          sEnGlobalInt().
+
+          In some cases it may be desirable to disable interrupts globally but
+          enable channel interrupts.  This would allow the global interrupt
+          status register to be used to determine which AIOPs need service.
+*/
+void sEnInterrupts(CHANNEL_T *ChP,Word_t Flags)
+{
+   Byte_t Mask;                 /* Interrupt Mask Register */
+
+   ChP->RxControl[2] |=
+      ((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN));
+
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]);
+
+   ChP->TxControl[2] |= ((Byte_t)Flags & TXINT_EN);
+
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]);
+
+   if(Flags & CHANINT_EN)
+   {
+      Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum];
+      sOutB(ChP->IntMask,Mask);
+   }
+}
+
+/***************************************************************************
+Function: sDisInterrupts
+Purpose:  Disable one or more interrupts for a channel
+Call:     sDisInterrupts(ChP,Flags)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Word_t Flags: Interrupt flags, can be any combination
+             of the following flags:
+                TXINT_EN:   Interrupt on Tx FIFO empty
+                RXINT_EN:   Interrupt on Rx FIFO at trigger level (see
+                            sSetRxTrigger())
+                SRCINT_EN:  Interrupt on SRC (Special Rx Condition)
+                MCINT_EN:   Interrupt on modem input change
+                CHANINT_EN: Disable channel interrupt signal to the
+                            AIOP's Interrupt Channel Register.
+Return:   void
+Comments: If an interrupt flag is set in Flags, that interrupt will be
+          disabled.  If an interrupt flag is not set in Flags, that
+          interrupt will not be changed.  Interrupts can be enabled with
+          function sEnInterrupts().
+
+          This function clears the appropriate bit for the channel in the AIOP's
+          Interrupt Mask Register if the CHANINT_EN flag is set.  This blocks
+          this channel's bit from being set in the AIOP's Interrupt Channel
+          Register.
+*/
+void sDisInterrupts(CHANNEL_T *ChP,Word_t Flags)
+{
+   Byte_t Mask;                 /* Interrupt Mask Register */
+
+   ChP->RxControl[2] &=
+         ~((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN));
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]);
+   ChP->TxControl[2] &= ~((Byte_t)Flags & TXINT_EN);
+   sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]);
+
+   if(Flags & CHANINT_EN)
+   {
+      Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum];
+      sOutB(ChP->IntMask,Mask);
+   }
+}
diff --git a/drivers/char/rocket_int.h b/drivers/char/rocket_int.h
new file mode 100644 (file)
index 0000000..8b2ea22
--- /dev/null
@@ -0,0 +1,1149 @@
+/*
+ * rocket_int.h --- internal header file for rocket.c
+ *
+ * Written by Theodore Ts'o, Copyright 1997.
+ *
+ * Portions of this file are....
+ * 
+ * Copyright 1994 Comtrol Corporation.    All Rights Reserved.
+ * 
+ * The following source code is subject to Comtrol Corporation's
+ * Developer's License Agreement.
+ * 
+ * This source code is protected by United States copyright law and 
+ * international copyright treaties.
+ * 
+ * This source code may only be used to develop software products that
+ * will operate with Comtrol brand hardware.
+ * 
+ * You may not reproduce nor distribute this source code in its original
+ * form but must produce a derivative work which includes portions of
+ * this source code only.
+ * 
+ * The portions of this source code which you use in your derivative
+ * work must bear Comtrol's copyright notice:
+ * 
+ *             Copyright 1994 Comtrol Corporation.
+ * 
+ */
+
+/*
+ * Begin Comtrol-provided headers, et. al.
+ */
+
+/*
+       user definitions for Rocket Toolkit
+
+       The following typedefs and defines must be established
+       depending on the platform the toolkit is being used
+       with.
+
+*/
+
+/************************************************************
+The following sets up the world for use with Linux
+************************************************************/
+
+#include <asm/io.h>
+
+typedef unsigned char Byte_t;
+typedef unsigned int ByteIO_t;
+
+typedef unsigned int Word_t;
+typedef unsigned int WordIO_t;
+
+typedef unsigned long DWord_t;
+typedef unsigned int DWordIO_t;
+
+#define sOutB(a, b) outb_p(b, a)
+#define sOutW(a, b) outw_p(b, a)
+#define sOutDW(a, b) outl_p(b, a)
+#define sInB(a) (inb_p(a))
+#define sInW(a) (inw_p(a))
+
+#define sOutStrW(port, addr, count) outsw(port, addr, count)
+
+#define sInStrW(port, addr, count) insw(port, addr, count)
+       
+#define CTL_SIZE 4
+#define AIOP_CTL_SIZE 4
+#define CHAN_AIOP_SIZE 8
+#define MAX_PORTS_PER_AIOP 8
+#define MAX_AIOPS_PER_BOARD 4
+#define MAX_PORTS_PER_BOARD 32
+
+/* Bus type ID */
+#define        isISA   0
+#define        isPCI   1
+#define        isMC    2
+
+/* Controller ID numbers */
+#define CTLID_NULL  -1              /* no controller exists */
+#define CTLID_0001  0x0001          /* controller release 1 */
+
+/* AIOP ID numbers, identifies AIOP type implementing channel */
+#define AIOPID_NULL -1              /* no AIOP or channel exists */
+#define AIOPID_0001 0x0001          /* AIOP release 1 */
+
+#define NULLDEV -1                  /* identifies non-existant device */
+#define NULLCTL -1                  /* identifies non-existant controller */
+#define NULLCTLPTR (CONTROLLER_T *)0 /* identifies non-existant controller */
+#define NULLAIOP -1                 /* identifies non-existant AIOP */
+#define NULLCHAN -1                 /* identifies non-existant channel */
+
+/************************************************************************
+ Global Register Offsets - Direct Access - Fixed values
+************************************************************************/
+
+#define _CMD_REG   0x38   /* Command Register            8    Write */
+#define _INT_CHAN  0x39   /* Interrupt Channel Register  8    Read */
+#define _INT_MASK  0x3A   /* Interrupt Mask Register     8    Read / Write */
+#define _UNUSED    0x3B   /* Unused                      8 */
+#define _INDX_ADDR 0x3C   /* Index Register Address      16   Write */
+#define _INDX_DATA 0x3E   /* Index Register Data         8/16 Read / Write */
+
+/************************************************************************
+ Channel Register Offsets for 1st channel in AIOP - Direct Access
+************************************************************************/
+#define _TD0       0x00  /* Transmit Data               16   Write */
+#define _RD0       0x00  /* Receive Data                16   Read */
+#define _CHN_STAT0 0x20  /* Channel Status              8/16 Read / Write */
+#define _FIFO_CNT0 0x10  /* Transmit/Receive FIFO Count 16   Read */
+#define _INT_ID0   0x30  /* Interrupt Identification    8    Read */
+
+/************************************************************************
+ Tx Control Register Offsets - Indexed - External - Fixed
+************************************************************************/
+#define _TX_ENBLS  0x980    /* Tx Processor Enables Register 8 Read / Write */
+#define _TXCMP1    0x988    /* Transmit Compare Value #1     8 Read / Write */
+#define _TXCMP2    0x989    /* Transmit Compare Value #2     8 Read / Write */
+#define _TXREP1B1  0x98A    /* Tx Replace Value #1 - Byte 1  8 Read / Write */
+#define _TXREP1B2  0x98B    /* Tx Replace Value #1 - Byte 2  8 Read / Write */
+#define _TXREP2    0x98C    /* Transmit Replace Value #2     8 Read / Write */
+
+/************************************************************************
+Memory Controller Register Offsets - Indexed - External - Fixed
+************************************************************************/
+#define _RX_FIFO    0x000    /* Rx FIFO */
+#define _TX_FIFO    0x800    /* Tx FIFO */
+#define _RXF_OUTP   0x990    /* Rx FIFO OUT pointer        16 Read / Write */
+#define _RXF_INP    0x992    /* Rx FIFO IN pointer         16 Read / Write */
+#define _TXF_OUTP   0x994    /* Tx FIFO OUT pointer        8  Read / Write */
+#define _TXF_INP    0x995    /* Tx FIFO IN pointer         8  Read / Write */
+#define _TXP_CNT    0x996    /* Tx Priority Count          8  Read / Write */
+#define _TXP_PNTR   0x997    /* Tx Priority Pointer        8  Read / Write */
+
+#define PRI_PEND    0x80     /* Priority data pending (bit7, Tx pri cnt) */
+#define TXFIFO_SIZE 255      /* size of Tx FIFO */
+#define RXFIFO_SIZE 1023     /* size of Rx FIFO */
+
+/************************************************************************
+Tx Priority Buffer - Indexed - External - Fixed
+************************************************************************/
+#define _TXP_BUF    0x9C0    /* Tx Priority Buffer  32  Bytes   Read / Write */
+#define TXP_SIZE    0x20     /* 32 bytes */
+
+/************************************************************************
+Channel Register Offsets - Indexed - Internal - Fixed
+************************************************************************/
+
+#define _TX_CTRL    0xFF0    /* Transmit Control               16  Write */
+#define _RX_CTRL    0xFF2    /* Receive Control                 8  Write */
+#define _BAUD       0xFF4    /* Baud Rate                      16  Write */
+#define _CLK_PRE    0xFF6    /* Clock Prescaler                 8  Write */
+
+#if 0
+#define CLOCK_PRESC 0x14          /* ?????? new mod 4 (divide by 5) prescale */
+
+#define BRD50             9215
+#define BRD75             6143  
+#define BRD110            4188
+#define BRD134            3438
+#define BRD150            3071
+#define BRD200            2303
+#define BRD300            1535
+#define BRD600            767
+#define BRD1200           383
+#define BRD1800           255
+#define BRD2000           229
+#define BRD2400           191
+#define BRD3600           127
+#define BRD4800           95
+#define BRD7200           63
+#define BRD9600           47
+#define BRD14400          31 
+#define BRD19200          23
+#define BRD38400          11
+#define BRD57600          7
+#define BRD76800          5
+#define BRD115200         3
+#define BRD230400         1
+#define BRD460800        0
+#endif
+
+#if 0
+
+/* Old clock prescale definition and baud rates associated with it */
+
+#define CLOCK_PRESC 0x19  */        /* mod 9 (divide by 10) prescale */
+#define BRD50             4607
+#define BRD75             3071
+#define BRD110            2094
+#define BRD134            1712
+#define BRD150            1535
+#define BRD200            1151
+#define BRD300            767
+#define BRD600            383
+#define BRD1200           191
+#define BRD1800           127
+#define BRD2000           114
+#define BRD2400           95
+#define BRD3600           64
+#define BRD4800           47
+#define BRD7200           31
+#define BRD9600           23
+#define BRD14400          15
+#define BRD19200          11
+#define BRD38400          5
+#define BRD57600          3
+#define BRD76800          2
+#define BRD115200         1
+#define BRD230400         0
+
+#endif
+
+#define STMBREAK   0x08        /* BREAK */
+#define STMFRAME   0x04        /* framing error */
+#define STMRCVROVR 0x02        /* receiver over run error */
+#define STMPARITY  0x01        /* parity error */
+#define STMERROR   (STMBREAK | STMFRAME | STMPARITY)
+#define STMBREAKH   0x800      /* BREAK */
+#define STMFRAMEH   0x400      /* framing error */
+#define STMRCVROVRH 0x200      /* receiver over run error */
+#define STMPARITYH  0x100      /* parity error */
+#define STMERRORH   (STMBREAKH | STMFRAMEH | STMPARITYH)
+
+#define CTS_ACT   0x20        /* CTS input asserted */
+#define DSR_ACT   0x10        /* DSR input asserted */
+#define CD_ACT    0x08        /* CD input asserted */
+#define TXFIFOMT  0x04        /* Tx FIFO is empty */
+#define TXSHRMT   0x02        /* Tx shift register is empty */
+#define RDA       0x01        /* Rx data available */
+#define DRAINED (TXFIFOMT | TXSHRMT)  /* indicates Tx is drained */
+
+#define STATMODE  0x8000      /* status mode enable bit */
+#define RXFOVERFL 0x2000      /* receive FIFO overflow */
+#define RX2MATCH  0x1000      /* receive compare byte 2 match */
+#define RX1MATCH  0x0800      /* receive compare byte 1 match */
+#define RXBREAK   0x0400      /* received BREAK */
+#define RXFRAME   0x0200      /* received framing error */
+#define RXPARITY  0x0100      /* received parity error */
+#define STATERROR (RXBREAK | RXFRAME | RXPARITY)
+
+#define CTSFC_EN  0x80        /* CTS flow control enable bit */
+#define RTSTOG_EN 0x40        /* RTS toggle enable bit */
+#define TXINT_EN  0x10        /* transmit interrupt enable */
+#define STOP2     0x08        /* enable 2 stop bits (0 = 1 stop) */
+#define PARITY_EN 0x04        /* enable parity (0 = no parity) */
+#define EVEN_PAR  0x02        /* even parity (0 = odd parity) */
+#define DATA8BIT  0x01        /* 8 bit data (0 = 7 bit data) */
+
+#define SETBREAK  0x10        /* send break condition (must clear) */
+#define LOCALLOOP 0x08        /* local loopback set for test */
+#define SET_DTR   0x04        /* assert DTR */
+#define SET_RTS   0x02        /* assert RTS */
+#define TX_ENABLE 0x01        /* enable transmitter */
+
+#define RTSFC_EN  0x40        /* RTS flow control enable */
+#define RXPROC_EN 0x20        /* receive processor enable */
+#define TRIG_NO   0x00        /* Rx FIFO trigger level 0 (no trigger) */
+#define TRIG_1    0x08        /* trigger level 1 char */
+#define TRIG_1_2  0x10        /* trigger level 1/2 */
+#define TRIG_7_8  0x18        /* trigger level 7/8 */
+#define TRIG_MASK 0x18        /* trigger level mask */
+#define SRCINT_EN 0x04        /* special Rx condition interrupt enable */
+#define RXINT_EN  0x02        /* Rx interrupt enable */
+#define MCINT_EN  0x01        /* modem change interrupt enable */
+
+#define RXF_TRIG  0x20        /* Rx FIFO trigger level interrupt */
+#define TXFIFO_MT 0x10        /* Tx FIFO empty interrupt */
+#define SRC_INT   0x08        /* special receive condition interrupt */
+#define DELTA_CD  0x04        /* CD change interrupt */
+#define DELTA_CTS 0x02        /* CTS change interrupt */
+#define DELTA_DSR 0x01        /* DSR change interrupt */
+
+#define REP1W2_EN 0x10        /* replace byte 1 with 2 bytes enable */
+#define IGN2_EN   0x08        /* ignore byte 2 enable */
+#define IGN1_EN   0x04        /* ignore byte 1 enable */
+#define COMP2_EN  0x02        /* compare byte 2 enable */
+#define COMP1_EN  0x01        /* compare byte 1 enable */
+
+#define RESET_ALL 0x80        /* reset AIOP (all channels) */
+#define TXOVERIDE 0x40        /* Transmit software off override */
+#define RESETUART 0x20        /* reset channel's UART */
+#define RESTXFCNT 0x10        /* reset channel's Tx FIFO count register */
+#define RESRXFCNT 0x08        /* reset channel's Rx FIFO count register */
+
+#define INTSTAT0  0x01        /* AIOP 0 interrupt status */
+#define INTSTAT1  0x02        /* AIOP 1 interrupt status */
+#define INTSTAT2  0x04        /* AIOP 2 interrupt status */
+#define INTSTAT3  0x08        /* AIOP 3 interrupt status */
+
+#define INTR_EN   0x08        /* allow interrupts to host */
+#define INT_STROB 0x04        /* strobe and clear interrupt line (EOI) */
+
+/**************************************************************************
+ MUDBAC remapped for PCI
+**************************************************************************/
+
+#define _CFG_INT_PCI  0x40
+#define _PCI_INT_FUNC 0x3A
+
+#define PCI_STROB 0x2000        /* bit 13 of int aiop register */
+#define INTR_EN_PCI   0x0010        /* allow interrupts to host */
+
+
+#define CHAN3_EN  0x08        /* enable AIOP 3 */
+#define CHAN2_EN  0x04        /* enable AIOP 2 */
+#define CHAN1_EN  0x02        /* enable AIOP 1 */
+#define CHAN0_EN  0x01        /* enable AIOP 0 */
+#define FREQ_DIS  0x00
+#define FREQ_274HZ 0x60
+#define FREQ_137HZ 0x50
+#define FREQ_69HZ  0x40
+#define FREQ_34HZ  0x30
+#define FREQ_17HZ  0x20
+#define FREQ_9HZ   0x10
+#define PERIODIC_ONLY 0x80    /* only PERIODIC interrupt */
+
+#define CHANINT_EN 0x0100           /* flags to enable/disable channel ints */
+
+#define RDATASIZE 72
+#define RREGDATASIZE 52
+
+/* Controller level information structure */
+typedef struct
+{
+       int             CtlID;
+       int             CtlNum;
+       int             BusType;
+       WordIO_t        PCIIO;
+       ByteIO_t        MBaseIO;
+       ByteIO_t        MReg1IO;
+       ByteIO_t        MReg2IO;
+       ByteIO_t        MReg3IO;
+       Byte_t          MReg2;
+       Byte_t          MReg3;
+       int             NumAiop;
+       WordIO_t        AiopIO[AIOP_CTL_SIZE];
+       ByteIO_t        AiopIntChanIO[AIOP_CTL_SIZE];
+       int             AiopID[AIOP_CTL_SIZE];
+       int             AiopNumChan[AIOP_CTL_SIZE];
+} CONTROLLER_T;
+
+typedef CONTROLLER_T CONTROLLER_t;
+
+/* Channel level information structure */
+typedef struct
+{
+       CONTROLLER_T    *CtlP;
+       int             AiopNum;
+       int             ChanID;
+       int             ChanNum;
+
+       ByteIO_t        Cmd;
+       ByteIO_t        IntChan;
+       ByteIO_t        IntMask;
+       DWordIO_t       IndexAddr;
+       WordIO_t        IndexData;
+
+       WordIO_t        TxRxData;
+       WordIO_t        ChanStat;
+       WordIO_t        TxRxCount;
+       ByteIO_t        IntID;
+
+       Word_t          TxFIFO;
+       Word_t          TxFIFOPtrs;
+       Word_t          RxFIFO;
+       Word_t          RxFIFOPtrs;
+       Word_t          TxPrioCnt;
+       Word_t          TxPrioPtr;
+       Word_t          TxPrioBuf;
+
+       Byte_t          R[RREGDATASIZE];
+
+       Byte_t          BaudDiv[4];
+       Byte_t          TxControl[4];
+       Byte_t          RxControl[4];
+       Byte_t          TxEnables[4];
+       Byte_t          TxCompare[4];
+       Byte_t          TxReplace1[4];
+       Byte_t          TxReplace2[4];
+} CHANNEL_T;
+
+typedef CHANNEL_T CHANNEL_t;
+typedef CHANNEL_T * CHANPTR_T;
+
+/***************************************************************************
+Function: sClrBreak
+Purpose:  Stop sending a transmit BREAK signal
+Call:     sClrBreak(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sClrBreak(ChP) \
+{ \
+   (ChP)->TxControl[3] &= ~SETBREAK; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sClrDTR
+Purpose:  Clr the DTR output
+Call:     sClrDTR(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sClrDTR(ChP) \
+{ \
+   (ChP)->TxControl[3] &= ~SET_DTR; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sClrRTS
+Purpose:  Clr the RTS output
+Call:     sClrRTS(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sClrRTS(ChP) \
+{ \
+   (ChP)->TxControl[3] &= ~SET_RTS; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sClrTxXOFF
+Purpose:  Clear any existing transmit software flow control off condition
+Call:     sClrTxXOFF(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sClrTxXOFF(ChP) \
+{ \
+   sOutB((ChP)->Cmd,TXOVERIDE | (Byte_t)(ChP)->ChanNum); \
+   sOutB((ChP)->Cmd,(Byte_t)(ChP)->ChanNum); \
+}
+
+/***************************************************************************
+Function: sCtlNumToCtlPtr
+Purpose:  Convert a controller number to controller structure pointer
+Call:     sCtlNumToCtlPtr(CtlNum)
+          int CtlNum; Controller number
+Return:   CONTROLLER_T *: Ptr to controller structure
+*/
+#define sCtlNumToCtlPtr(CTLNUM) &sController[CTLNUM]
+
+/***************************************************************************
+Function: sControllerEOI
+Purpose:  Strobe the MUDBAC's End Of Interrupt bit.
+Call:     sControllerEOI(CtlP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+*/
+#define sControllerEOI(CTLP) sOutB((CTLP)->MReg2IO,(CTLP)->MReg2 | INT_STROB)
+
+/***************************************************************************
+Function: sPCIControllerEOI
+Purpose:  Strobe the PCI End Of Interrupt bit.
+Call:     sPCIControllerEOI(CtlP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+*/
+#define sPCIControllerEOI(CTLP) sOutW((CTLP)->PCIIO, PCI_STROB)
+
+/***************************************************************************
+Function: sDisAiop
+Purpose:  Disable I/O access to an AIOP
+Call:     sDisAiop(CltP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int AiopNum; Number of AIOP on controller
+*/
+#define sDisAiop(CTLP,AIOPNUM) \
+{ \
+   (CTLP)->MReg3 &= sBitMapClrTbl[AIOPNUM]; \
+   sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \
+}
+
+/***************************************************************************
+Function: sDisCTSFlowCtl
+Purpose:  Disable output flow control using CTS
+Call:     sDisCTSFlowCtl(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sDisCTSFlowCtl(ChP) \
+{ \
+   (ChP)->TxControl[2] &= ~CTSFC_EN; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: DisParity
+Purpose:  Disable parity
+Call:     sDisParity(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: Function sSetParity() can be used in place of functions sEnParity(),
+          sDisParity(), sSetOddParity(), and sSetEvenParity().
+*/
+#define sDisParity(ChP) \
+{ \
+   (ChP)->TxControl[2] &= ~PARITY_EN; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sDisRxFIFO
+Purpose:  Disable Rx FIFO
+Call:     sDisRxFIFO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sDisRxFIFO(ChP) \
+{ \
+   (ChP)->R[0x32] = 0x0a; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x30]); \
+}
+
+/***************************************************************************
+Function: sDisRxStatusMode
+Purpose:  Disable the Rx status mode
+Call:     sDisRxStatusMode(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This takes the channel out of the receive status mode.  All
+          subsequent reads of receive data using sReadRxWord() will return
+          two data bytes.
+*/
+#define sDisRxStatusMode(ChP) sOutW((ChP)->ChanStat,0)
+
+/***************************************************************************
+Function: sDisTransmit
+Purpose:  Disable transmit
+Call:     sDisTransmit(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+          This disables movement of Tx data from the Tx FIFO into the 1 byte
+          Tx buffer.  Therefore there could be up to a 2 byte latency
+          between the time sDisTransmit() is called and the transmit buffer
+          and transmit shift register going completely empty.
+*/
+#define sDisTransmit(ChP) \
+{ \
+   (ChP)->TxControl[3] &= ~TX_ENABLE; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sDisTxSoftFlowCtl
+Purpose:  Disable Tx Software Flow Control
+Call:     sDisTxSoftFlowCtl(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sDisTxSoftFlowCtl(ChP) \
+{ \
+   (ChP)->R[0x06] = 0x8a; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x04]); \
+}
+
+/***************************************************************************
+Function: sEnAiop
+Purpose:  Enable I/O access to an AIOP
+Call:     sEnAiop(CltP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int AiopNum; Number of AIOP on controller
+*/
+#define sEnAiop(CTLP,AIOPNUM) \
+{ \
+   (CTLP)->MReg3 |= sBitMapSetTbl[AIOPNUM]; \
+   sOutB((CTLP)->MReg3IO,(CTLP)->MReg3); \
+}
+
+/***************************************************************************
+Function: sEnCTSFlowCtl
+Purpose:  Enable output flow control using CTS
+Call:     sEnCTSFlowCtl(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnCTSFlowCtl(ChP) \
+{ \
+   (ChP)->TxControl[2] |= CTSFC_EN; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: EnParity
+Purpose:  Enable parity
+Call:     sEnParity(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: Function sSetParity() can be used in place of functions sEnParity(),
+          sDisParity(), sSetOddParity(), and sSetEvenParity().
+
+Warnings: Before enabling parity odd or even parity should be chosen using
+          functions sSetOddParity() or sSetEvenParity().
+*/
+#define sEnParity(ChP) \
+{ \
+   (ChP)->TxControl[2] |= PARITY_EN; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sEnRxFIFO
+Purpose:  Enable Rx FIFO
+Call:     sEnRxFIFO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnRxFIFO(ChP) \
+{ \
+   (ChP)->R[0x32] = 0x08; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0x30]); \
+}
+
+/***************************************************************************
+Function: sEnRxProcessor
+Purpose:  Enable the receive processor
+Call:     sEnRxProcessor(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This function is used to start the receive processor.  When
+          the channel is in the reset state the receive processor is not
+          running.  This is done to prevent the receive processor from
+          executing invalid microcode instructions prior to the
+          downloading of the microcode.
+
+Warnings: This function must be called after valid microcode has been
+          downloaded to the AIOP, and it must not be called before the
+          microcode has been downloaded.
+*/
+#define sEnRxProcessor(ChP) \
+{ \
+   (ChP)->RxControl[2] |= RXPROC_EN; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->RxControl[0]); \
+}
+
+/***************************************************************************
+Function: sEnRxStatusMode
+Purpose:  Enable the Rx status mode
+Call:     sEnRxStatusMode(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This places the channel in the receive status mode.  All subsequent
+          reads of receive data using sReadRxWord() will return a data byte
+          in the low word and a status byte in the high word.
+
+*/
+#define sEnRxStatusMode(ChP) sOutW((ChP)->ChanStat,STATMODE)
+
+/***************************************************************************
+Function: sEnTransmit
+Purpose:  Enable transmit
+Call:     sEnTransmit(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sEnTransmit(ChP) \
+{ \
+   (ChP)->TxControl[3] |= TX_ENABLE; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sGetAiopIntStatus
+Purpose:  Get the AIOP interrupt status
+Call:     sGetAiopIntStatus(CtlP,AiopNum)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int AiopNum; AIOP number
+Return:   Byte_t: The AIOP interrupt status.  Bits 0 through 7
+                         represent channels 0 through 7 respectively.  If a
+                         bit is set that channel is interrupting.
+*/
+#define sGetAiopIntStatus(CTLP,AIOPNUM) sInB((CTLP)->AiopIntChanIO[AIOPNUM])
+
+/***************************************************************************
+Function: sGetAiopNumChan
+Purpose:  Get the number of channels supported by an AIOP
+Call:     sGetAiopNumChan(CtlP,AiopNum)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+          int AiopNum; AIOP number
+Return:   int: The number of channels supported by the AIOP
+*/
+#define sGetAiopNumChan(CTLP,AIOPNUM) (CTLP)->AiopNumChan[AIOPNUM]
+
+/***************************************************************************
+Function: sGetChanIntID
+Purpose:  Get a channel's interrupt identification byte
+Call:     sGetChanIntID(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   Byte_t: The channel interrupt ID.  Can be any
+             combination of the following flags:
+                RXF_TRIG:     Rx FIFO trigger level interrupt
+                TXFIFO_MT:    Tx FIFO empty interrupt
+                SRC_INT:      Special receive condition interrupt
+                DELTA_CD:     CD change interrupt
+                DELTA_CTS:    CTS change interrupt
+                DELTA_DSR:    DSR change interrupt
+*/
+#define sGetChanIntID(ChP) (sInB((ChP)->IntID) & (RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR))
+
+/***************************************************************************
+Function: sGetChanNum
+Purpose:  Get the number of a channel within an AIOP
+Call:     sGetChanNum(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   int: Channel number within AIOP, or NULLCHAN if channel does
+               not exist.
+*/
+#define sGetChanNum(ChP) (ChP)->ChanNum
+
+/***************************************************************************
+Function: sGetChanStatus
+Purpose:  Get the channel status
+Call:     sGetChanStatus(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   Word_t: The channel status.  Can be any combination of
+             the following flags:
+                LOW BYTE FLAGS
+                CTS_ACT:      CTS input asserted
+                DSR_ACT:      DSR input asserted
+                CD_ACT:       CD input asserted
+                TXFIFOMT:     Tx FIFO is empty
+                TXSHRMT:      Tx shift register is empty
+                RDA:          Rx data available
+
+                HIGH BYTE FLAGS
+                STATMODE:     status mode enable bit
+                RXFOVERFL:    receive FIFO overflow
+                RX2MATCH:     receive compare byte 2 match
+                RX1MATCH:     receive compare byte 1 match
+                RXBREAK:      received BREAK
+                RXFRAME:      received framing error
+                RXPARITY:     received parity error
+Warnings: This function will clear the high byte flags in the Channel
+          Status Register.
+*/
+#define sGetChanStatus(ChP) sInW((ChP)->ChanStat)
+
+/***************************************************************************
+Function: sGetChanStatusLo
+Purpose:  Get the low byte only of the channel status
+Call:     sGetChanStatusLo(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   Byte_t: The channel status low byte.  Can be any combination
+             of the following flags:
+                CTS_ACT:      CTS input asserted
+                DSR_ACT:      DSR input asserted
+                CD_ACT:       CD input asserted
+                TXFIFOMT:     Tx FIFO is empty
+                TXSHRMT:      Tx shift register is empty
+                RDA:          Rx data available
+*/
+#define sGetChanStatusLo(ChP) sInB((ByteIO_t)(ChP)->ChanStat)
+
+/***************************************************************************
+Function: sGetControllerIntStatus
+Purpose:  Get the controller interrupt status
+Call:     sGetControllerIntStatus(CtlP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+Return:   Byte_t: The controller interrupt status in the lower 4
+                         bits.  Bits 0 through 3 represent AIOP's 0
+                         through 3 respectively.  If a bit is set that
+                         AIOP is interrupting.  Bits 4 through 7 will
+                         always be cleared.
+*/
+#define sGetControllerIntStatus(CTLP) (sInB((CTLP)->MReg1IO) & 0x0f)
+
+/***************************************************************************
+Function: sPCIGetControllerIntStatus
+Purpose:  Get the controller interrupt status
+Call:     sPCIGetControllerIntStatus(CtlP)
+          CONTROLLER_T *CtlP; Ptr to controller structure
+Return:   unsigned char: The controller interrupt status in the lower 4
+                         bits and bit 4.  Bits 0 through 3 represent AIOP's 0
+                         through 3 respectively. Bit 4 is set if the int 
+                        was generated from periodic. If a bit is set the
+                        AIOP is interrupting.
+*/
+#define sPCIGetControllerIntStatus(CTLP) ((sInW((CTLP)->PCIIO) >> 8) & 0x1f)
+
+/***************************************************************************
+
+Function: sGetRxCnt
+Purpose:  Get the number of data bytes in the Rx FIFO
+Call:     sGetRxCnt(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   int: The number of data bytes in the Rx FIFO.
+Comments: Byte read of count register is required to obtain Rx count.
+
+*/
+#define sGetRxCnt(ChP) sInW((ChP)->TxRxCount)
+
+/***************************************************************************
+Function: sGetTxCnt
+Purpose:  Get the number of data bytes in the Tx FIFO
+Call:     sGetTxCnt(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   Byte_t: The number of data bytes in the Tx FIFO.
+Comments: Byte read of count register is required to obtain Tx count.
+
+*/
+#define sGetTxCnt(ChP) sInB((ByteIO_t)(ChP)->TxRxCount)
+
+/*****************************************************************************
+Function: sGetTxRxDataIO
+Purpose:  Get the I/O address of a channel's TxRx Data register
+Call:     sGetTxRxDataIO(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Return:   WordIO_t: I/O address of a channel's TxRx Data register
+*/
+#define sGetTxRxDataIO(ChP) (ChP)->TxRxData
+
+/***************************************************************************
+Function: sInitChanDefaults
+Purpose:  Initialize a channel structure to it's default state.
+Call:     sInitChanDefaults(ChP)
+          CHANNEL_T *ChP; Ptr to the channel structure
+Comments: This function must be called once for every channel structure
+          that exists before any other SSCI calls can be made.
+
+*/
+#define sInitChanDefaults(ChP) \
+{ \
+   (ChP)->CtlP = NULLCTLPTR; \
+   (ChP)->AiopNum = NULLAIOP; \
+   (ChP)->ChanID = AIOPID_NULL; \
+   (ChP)->ChanNum = NULLCHAN; \
+}
+
+/***************************************************************************
+Function: sResetAiopByNum
+Purpose:  Reset the AIOP by number
+Call:     sResetAiopByNum(CTLP,AIOPNUM)
+       CONTROLLER_T CTLP; Ptr to controller structure
+       AIOPNUM; AIOP index 
+*/
+#define sResetAiopByNum(CTLP,AIOPNUM) \
+{ \
+   sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,RESET_ALL); \
+   sOutB((CTLP)->AiopIO[(AIOPNUM)]+_CMD_REG,0x0); \
+}
+
+/***************************************************************************
+Function: sSendBreak
+Purpose:  Send a transmit BREAK signal
+Call:     sSendBreak(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSendBreak(ChP) \
+{ \
+   (ChP)->TxControl[3] |= SETBREAK; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetBaud
+Purpose:  Set baud rate
+Call:     sSetBaud(ChP,Divisor)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Word_t Divisor; 16 bit baud rate divisor for channel
+*/
+#define sSetBaud(ChP,DIVISOR) \
+{ \
+   (ChP)->BaudDiv[2] = (Byte_t)(DIVISOR); \
+   (ChP)->BaudDiv[3] = (Byte_t)((DIVISOR) >> 8); \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->BaudDiv[0]); \
+}
+
+/***************************************************************************
+Function: sSetData7
+Purpose:  Set data bits to 7
+Call:     sSetData7(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetData7(ChP) \
+{ \
+   (ChP)->TxControl[2] &= ~DATA8BIT; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetData8
+Purpose:  Set data bits to 8
+Call:     sSetData8(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetData8(ChP) \
+{ \
+   (ChP)->TxControl[2] |= DATA8BIT; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetDTR
+Purpose:  Set the DTR output
+Call:     sSetDTR(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetDTR(ChP) \
+{ \
+   (ChP)->TxControl[3] |= SET_DTR; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetEvenParity
+Purpose:  Set even parity
+Call:     sSetEvenParity(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: Function sSetParity() can be used in place of functions sEnParity(),
+          sDisParity(), sSetOddParity(), and sSetEvenParity().
+
+Warnings: This function has no effect unless parity is enabled with function
+          sEnParity().
+*/
+#define sSetEvenParity(ChP) \
+{ \
+   (ChP)->TxControl[2] |= EVEN_PAR; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetOddParity
+Purpose:  Set odd parity
+Call:     sSetOddParity(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: Function sSetParity() can be used in place of functions sEnParity(),
+          sDisParity(), sSetOddParity(), and sSetEvenParity().
+
+Warnings: This function has no effect unless parity is enabled with function
+          sEnParity().
+*/
+#define sSetOddParity(ChP) \
+{ \
+   (ChP)->TxControl[2] &= ~EVEN_PAR; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetRTS
+Purpose:  Set the RTS output
+Call:     sSetRTS(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetRTS(ChP) \
+{ \
+   (ChP)->TxControl[3] |= SET_RTS; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetRxTrigger
+Purpose:  Set the Rx FIFO trigger level
+Call:     sSetRxProcessor(ChP,Level)
+          CHANNEL_T *ChP; Ptr to channel structure
+          Byte_t Level; Number of characters in Rx FIFO at which the
+             interrupt will be generated.  Can be any of the following flags:
+
+             TRIG_NO:   no trigger
+             TRIG_1:    1 character in FIFO
+             TRIG_1_2:  FIFO 1/2 full
+             TRIG_7_8:  FIFO 7/8 full
+Comments: An interrupt will be generated when the trigger level is reached
+          only if function sEnInterrupt() has been called with flag
+          RXINT_EN set.  The RXF_TRIG flag in the Interrupt Idenfification
+          register will be set whenever the trigger level is reached
+          regardless of the setting of RXINT_EN.
+
+*/
+#define sSetRxTrigger(ChP,LEVEL) \
+{ \
+   (ChP)->RxControl[2] &= ~TRIG_MASK; \
+   (ChP)->RxControl[2] |= LEVEL; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->RxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetStop1
+Purpose:  Set stop bits to 1
+Call:     sSetStop1(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetStop1(ChP) \
+{ \
+   (ChP)->TxControl[2] &= ~STOP2; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sSetStop2
+Purpose:  Set stop bits to 2
+Call:     sSetStop2(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+*/
+#define sSetStop2(ChP) \
+{ \
+   (ChP)->TxControl[2] |= STOP2; \
+   sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->TxControl[0]); \
+}
+
+/***************************************************************************
+Function: sStartRxProcessor
+Purpose:  Start a channel's receive processor
+Call:     sStartRxProcessor(ChP)
+          CHANNEL_T *ChP; Ptr to channel structure
+Comments: This function is used to start a Rx processor after it was
+          stopped with sStopRxProcessor() or sStopSWInFlowCtl().  It
+          will restart both the Rx processor and software input flow control.
+
+*/
+#define sStartRxProcessor(ChP) sOutDW((ChP)->IndexAddr,*(DWord_t *)&(ChP)->R[0])
+
+/***************************************************************************
+Function: sWriteTxByte
+Purpose:  Write a transmit data byte to a channel.
+          ByteIO_t io: Channel transmit register I/O address.  This can
+                           be obtained with sGetTxRxDataIO().
+          Byte_t Data; The transmit data byte.
+Warnings: This function writes the data byte without checking to see if
+          sMaxTxSize is exceeded in the Tx FIFO.
+*/
+#define sWriteTxByte(IO,DATA) sOutB(IO,DATA)
+
+int sInitController(   CONTROLLER_T *CtlP,
+                       int CtlNum,
+                       ByteIO_t MudbacIO,
+                       ByteIO_t *AiopIOList,
+                       int AiopIOListSize,
+                       int IRQNum,
+                       Byte_t Frequency,
+                       int PeriodicOnly);
+
+int sPCIInitController(        CONTROLLER_T *CtlP,
+                       int CtlNum,
+                       ByteIO_t *AiopIOList,
+                       int AiopIOListSize,
+                       int IRQNum,
+                       Byte_t Frequency,
+                       int PeriodicOnly);
+
+int sReadAiopID(ByteIO_t io);
+int sReadAiopNumChan(WordIO_t io);
+int sInitChan( CONTROLLER_T *CtlP,
+               CHANNEL_T *ChP,
+               int AiopNum,
+               int ChanNum);
+Byte_t sGetRxErrStatus(CHANNEL_T *ChP);
+void sStopRxProcessor(CHANNEL_T *ChP);
+void sStopSWInFlowCtl(CHANNEL_T *ChP);
+void sFlushRxFIFO(CHANNEL_T *ChP);
+void sFlushTxFIFO(CHANNEL_T *ChP);
+int sWriteTxPrioByte(CHANNEL_T *ChP, Byte_t Data);
+void sEnInterrupts(CHANNEL_T *ChP,Word_t Flags);
+void sDisInterrupts(CHANNEL_T *ChP,Word_t Flags);
+
+extern Byte_t R[RDATASIZE];
+extern CONTROLLER_T sController[CTL_SIZE];
+extern Byte_t sIRQMap[16];
+extern Byte_t sBitMapClrTbl[8];
+extern Byte_t sBitMapSetTbl[8];
+extern int sClockPrescale;
+
+
+/*
+ * Begin Linux specific definitions for the Rocketport driver
+ *
+ * This code is Copyright Theodore Ts'o, 1995-1997
+ */
+
+struct r_port {
+       int                     magic;
+       int                     line;
+       int                     flags;
+       int                     count;
+       int                     blocked_open;
+       struct tty_struct       *tty;
+       int                     board:2;
+       int                     aiop:2;
+       int                     chan:3;
+       CONTROLLER_t *ctlp;
+       CHANNEL_t               channel;
+       int                     closing_wait;
+       int                     close_delay;
+       int                     intmask;
+       int                     xmit_fifo_room; /* room in xmit fifo */
+       unsigned char           *xmit_buf;
+       int                     xmit_head;
+       int                     xmit_tail;
+       int                     xmit_cnt;
+       int                     session;
+       int                     pgrp;
+       int                     cd_status;
+       int                     ignore_status_mask;
+       int                     read_status_mask;
+       int                     cps;
+       struct termios          normal_termios;
+       struct termios          callout_termios;
+       struct tq_struct        tqueue;
+       struct wait_queue       *open_wait;
+       struct wait_queue       *close_wait;
+};
+       
+#define RPORT_MAGIC 0x525001
+
+#define NUM_BOARDS 8
+#define MAX_RP_PORTS (32*NUM_BOARDS)
+
+/*
+ * The size of the xmit buffer is 1 page, or 4096 bytes
+ */
+#define XMIT_BUF_SIZE 4096
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Internal flags used only by the rocketport driver */
+#define ROCKET_INITIALIZED     0x80000000 /* Port is active */
+#define ROCKET_CLOSING         0x40000000 /* Serial port is closing */
+#define ROCKET_NORMAL_ACTIVE   0x20000000 /* Normal port is active */
+#define ROCKET_CALLOUT_ACTIVE  0x10000000 /* Callout port is active */
+
+/*
+ * tty subtypes
+ *
+ */
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+
+/*
+ * Assigned major numbers for the Comtrol Rocketport
+ */
+#define TTY_ROCKET_MAJOR       46
+#define CUA_ROCKET_MAJOR       47
+
+/*
+ * Utility function.
+ */
+#ifndef MIN
+#define MIN(a,b)       ((a) < (b) ? (a) : (b))
+#endif
+
+#ifdef PCI_VENDOR_ID_RP
+#undef PCI_VENDOR_ID_RP
+#undef PCI_DEVICE_ID_RP8OCTA
+#undef PCI_DEVICE_ID_RP8INTF
+#undef PCI_DEVICE_ID_RP16INTF
+#undef PCI_DEVICE_ID_RP32INTF
+#endif
+
+#define PCI_VENDOR_ID_RP               0x11fe
+#define PCI_DEVICE_ID_RP32INTF         0x0001
+#define PCI_DEVICE_ID_RP8INTF          0x0002
+#define PCI_DEVICE_ID_RP16INTF         0x0003
+#define PCI_DEVICE_ID_RP8OCTA          0x0005
+
+#ifndef RP4QUAD
+#define PCI_DEVICE_ID_RP4QUAD         0x0004
+#endif
+#ifndef RP8J
+#define PCI_DEVICE_ID_RP8J            0x0006
+#endif
+
+
index 7a31e162da85aa65b6c212cd2f36f8de84ba8e3b..d63578c55ee74a0507b3e3c46d7fae74f372459b 100644 (file)
@@ -1829,6 +1829,9 @@ __initfunc(int tty_init(void))
 #ifdef CONFIG_SERIAL
        rs_init();
 #endif
+#ifdef CONFIG_ROCKETPORT
+       rp_init();
+#endif
 #ifdef CONFIG_CYCLADES
        cy_init();
 #endif
@@ -1853,4 +1856,3 @@ __initfunc(int tty_init(void))
 #endif
        return 0;
 }
-
diff --git a/drivers/misc/BUGS-parport b/drivers/misc/BUGS-parport
new file mode 100644 (file)
index 0000000..6b8420a
--- /dev/null
@@ -0,0 +1,13 @@
+Currently known (or at least suspected) bugs in parport:
+
+o /proc/parport is untested under 2.0.XX
+
+o SCSI aborts for PPA under 2.0.29 [reported by jmr].  Under investigation.
+
+o make config (etc) allow you to select CONFIG_PNP_PARPORT=m, CONFIG_PPA=y -
+  the resulting kernel won't link.
+
+o IEEE1284 code does not do the terminating handshake after transfers, which
+  seems to upset some devices.
+
+o lp doesn't allow you to read status while printing is in progress.
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
new file mode 100644 (file)
index 0000000..9d568b2
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Makefile for the kernel miscellaneous drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := misc.a
+MX_OBJS  :=
+LX_OBJS  := 
+MI_OBJS  :=
+MIX_OBJS :=
+
+ifeq ($(CONFIG_PARPORT),y)
+  L_OBJS += parport_share.o parport_ieee1284.o
+  ifeq ($(CONFIG_PROC_FS),y)
+    L_OBJS += parport_procfs.o
+  endif
+  ifeq ($(CONFIG_PARPORT_PC),y)
+    LX_OBJS += parport_pc.o
+  endif
+  LX_OBJS += parport_init.o
+else
+  ifeq ($(CONFIG_PARPORT),m)
+    MI_OBJS += parport_share.o parport_ieee1284.o
+    ifneq ($(CONFIG_PROC_FS),n) 
+      MI_OBJS += parport_procfs.o
+    endif
+    MIX_OBJS += parport_init.o
+    M_OBJS += parport.o
+  endif
+  ifeq ($(CONFIG_PARPORT_PC),m)
+    MX_OBJS += parport_pc.o
+  endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+# Special rule to build the composite parport.o module
+parport.o: $(MI_OBJS) $(MIX_OBJS)
+       $(LD) $(LD_RFLAG) -r -o $@ $(MI_OBJS) $(MIX_OBJS)
diff --git a/drivers/misc/TODO-parport b/drivers/misc/TODO-parport
new file mode 100644 (file)
index 0000000..eea6a01
--- /dev/null
@@ -0,0 +1,21 @@
+Things to be done.
+
+0. Fix the bugs (see BUGS-parport).
+
+1. Proper documentation.
+
+2. Overhaul lp.c:
+
+   a) It's a mess, and there is a lot of code duplication.
+
+   b) ECP support would be nice.  This can only work if both the port and
+      the printer support it.
+
+   c) Errors could do with being handled better.  There's no point logging a
+      message every 10 seconds when the printer is out of paper. 
+
+   d) Handle status readback automatically.  IEEE1284 printers can post status
+      bits when they have something to say.  We should read out and deal 
+      with (maybe just log) whatever the printer wants to tell the world.
+
+3. Assimilate more drivers.
diff --git a/drivers/misc/parport_arc.c b/drivers/misc/parport_arc.c
new file mode 100644 (file)
index 0000000..514618a
--- /dev/null
@@ -0,0 +1,77 @@
+/* $Id$
+ * Parallel-port routines for ARC onboard hardware.
+ *
+ * Author: Phil Blundell <pjb27@cam.ac.uk>
+ */
+
+#include <linux/tasks.h>
+
+#include <asm/ptrace.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include <linux/parport.h>
+
+#include <linux/arch/oldlatches.h>
+
+#define DATA_LATCH    0x3350010
+
+/* ARC can't read from the data latch, so we must use a soft copy. */
+static unsigned int data_copy;
+
+static void arc_write_data(struct parport *p, unsigned int data)
+{
+       data_copy = data;
+       outb(data, DATA_LATCH);
+}
+
+static unsigned int arc_read_data(struct parport *p)
+{
+       return data_copy;
+}
+
+static struct parport_operations arc_ops = 
+{
+       arc_write_data,
+       arc_read_data,
+
+       arc_write_control,
+       arc_read_control,
+       arc_frob_control,
+
+       NULL, /* write_econtrol */
+       NULL, /* read_econtrol */
+       NULL, /* frob_econtrol */
+
+       arc_write_status,
+       arc_read_status,
+
+       NULL, /* write_fifo */
+       NULL, /* read_fifo */
+       
+       NULL, /* change_mode */
+       
+       arc_release_resources,
+       arc_claim_resources,
+       
+       NULL, /* epp_write_block */
+       NULL, /* epp_read_block */
+
+       NULL, /* ecp_write_block */
+       NULL, /* epp_write_block */
+       
+       arc_save_state,
+       arc_restore_state,
+
+       arc_enable_irq,
+       arc_disable_irq,
+       arc_examine_irq 
+};
diff --git a/drivers/misc/parport_ieee1284.c b/drivers/misc/parport_ieee1284.c
new file mode 100644 (file)
index 0000000..d7c1368
--- /dev/null
@@ -0,0 +1,89 @@
+/* $Id$ 
+ * IEEE-1284 implementation for parport.
+ *
+ * Authors: Philip Blundell <pjb27@cam.ac.uk>
+ *          Carsten Gross <carsten@sol.wohnheim.uni-ulm.de>
+ *         Jose Renau <renau@acm.org>
+ */
+
+#include <linux/tasks.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+/* The following read functions are an implementation of a status readback
+ * and device id request confirming to IEEE1284-1994.
+ *
+ * These probably ought to go in some seperate file, so people like the SPARC
+ * don't have to pull them in.
+ */
+
+/* Wait for Status line(s) to change in 35 ms - see IEEE1284-1994 page 24 to
+ * 25 for this. After this time we can create a timeout because the
+ * peripheral doesn't conform to IEEE1284. We want to save CPU time: we are
+ * waiting a maximum time of 500 us busy (this is for speed). If there is
+ * not the right answer in this time, we call schedule and other processes
+ * are able "to eat" the time up to 30ms.  So the maximum load avarage can't
+ * get above 5% for a read even if the peripheral is really slow. (but your
+ * read gets very slow then - only about 10 characters per second. This
+ * should be tuneable). Thanks to Andreas who pointed me to this and ordered
+ * the documentation.
+ */ 
+
+int parport_wait_peripheral(struct parport *port, unsigned char mask, 
+       unsigned char result)
+{
+       int counter=0;
+       unsigned char status; 
+       
+       do {
+               status = parport_read_status(port);
+               udelay(25);
+               counter++;
+               if (need_resched)
+                       schedule();
+       } while ( ((status & mask) != result) && (counter < 20) );
+       if ( (counter == 20) && ((status & mask) != result) ) { 
+               current->state=TASK_INTERRUPTIBLE;
+               current->timeout=jiffies+4;
+               schedule(); /* wait for 4 scheduler runs (40ms) */
+               status = parport_read_status(port);
+               if ((status & mask) != result) return 1; /* timeout */
+       }
+       return 0; /* okay right response from device */
+}              
+
+/* Test if nibble mode for status readback is okay. Returns the value false
+ * if the printer doesn't support readback at all. If it supports readbacks
+ * and printer data is available the function returns 1, otherwise 2. The
+ * only valid values for "mode" are 0 and 4. 0 requests normal nibble mode,
+ * 4 is for "request device id using nibble mode". The request for the
+ * device id is best done in an ioctl (or at bootup time).  There is no
+ * check for an invalid value, the only function using this call at the
+ * moment is lp_read and the ioctl LPGETDEVICEID both fixed calls from
+ * trusted kernel.
+ */
+int parport_ieee1284_nibble_mode_ok(struct parport *port, unsigned char mode) 
+{
+       parport_write_data(port, mode);
+       udelay(5);              
+       parport_write_control(port, parport_read_control(port) & ~8);  /* SelectIN  low */
+       parport_write_control(port, parport_read_control(port) | 2); /* AutoFeed  high */
+       if (parport_wait_peripheral(port, 0x78, 0x38)) { /* timeout? */
+               parport_write_control(port, (parport_read_control(port) & ~2) | 8);
+               return 0; /* first stage of negotiation failed, 
+                           * no IEEE1284 compliant device on this port 
+                           */ 
+       }
+       parport_write_control(port, parport_read_control(port) | 1);      /* Strobe  high */
+       udelay(5);                                   /* Strobe wait */
+       parport_write_control(port, parport_read_control(port) & ~1);     /* Strobe  low */
+       udelay(5);
+       parport_write_control(port, parport_read_control(port) & ~2);     /*  AutoFeed low */
+       return (parport_wait_peripheral(port, 0x20, 0))?2:1;
+}
diff --git a/drivers/misc/parport_init.c b/drivers/misc/parport_init.c
new file mode 100644 (file)
index 0000000..d5a6897
--- /dev/null
@@ -0,0 +1,114 @@
+/* $Id: parport_init.c,v 1.1.2.2 1997/04/18 15:00:52 phil Exp $
+ * Parallel-port initialisation code.
+ * 
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *         Jose Renau <renau@acm.org>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/tasks.h>
+
+#include <linux/parport.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+
+#ifndef MODULE
+static int io[PARPORT_MAX+1] __initdata = { 0, };
+static int irq[PARPORT_MAX] __initdata = { PARPORT_IRQ_NONE, };
+static int dma[PARPORT_MAX] __initdata = { PARPORT_DMA_NONE, };
+
+extern int parport_pc_init(int *io, int *irq, int *dma);
+
+static int parport_setup_ptr __initdata = 0;
+
+__initfunc(void parport_setup(char *str, int *ints))
+{
+       if (ints[0] == 0 || ints[1] == 0) {
+               /* Disable parport if "parport=" or "parport=0" in cmdline */
+               io[0] = PARPORT_DISABLE; 
+               return;
+       }
+       if (parport_setup_ptr < PARPORT_MAX) {
+               io[parport_setup_ptr] = ints[1];
+               if (ints[0]>1) {
+                       irq[parport_setup_ptr] = ints[2];
+                       if (ints[0]>2) dma[parport_setup_ptr] = ints[3];
+               }
+               parport_setup_ptr++;
+       } else {
+               printk(KERN_ERR "parport=0x%x", ints[1]);
+               if (ints[0]>1) {
+                       printk(",%d", ints[2]);
+                       if (ints[0]>2) printk(",%d", ints[3]);
+               }
+               printk(" ignored, too many ports.\n");
+       }
+}
+#endif
+
+#ifdef MODULE
+int init_module(void)
+{
+       return 0;
+}
+
+void cleanup_module(void)
+{
+       struct parport *port, *next;
+   
+       for (port = parport_enumerate(); port; port = next) {
+               next = port->next;
+               if (!(port->flags & PARPORT_FLAG_COMA))
+                       parport_quiesce(port);
+               parport_proc_unregister(port);
+               kfree(port->name);
+               kfree(port);
+       }
+       
+       parport_proc_cleanup();
+}
+#else
+__initfunc(int parport_init(void))
+{
+       struct parport *pb;
+
+       if (io[0] == PARPORT_DISABLE) return 1; 
+#ifdef CONFIG_PARPORT_PC
+       parport_pc_init(io, irq, dma);
+#endif
+       return 0;
+}
+#endif
+
+/* Exported symbols for modules. */
+
+EXPORT_SYMBOL(parport_claim);
+EXPORT_SYMBOL(parport_release);
+EXPORT_SYMBOL(parport_register_port);
+EXPORT_SYMBOL(parport_quiesce);
+EXPORT_SYMBOL(parport_register_device);
+EXPORT_SYMBOL(parport_unregister_device);
+EXPORT_SYMBOL(parport_enumerate);
+EXPORT_SYMBOL(parport_ieee1284_nibble_mode_ok);
+EXPORT_SYMBOL(parport_wait_peripheral);
+
+void inc_parport_count(void)
+{
+#ifdef MODULE
+       MOD_INC_USE_COUNT;
+#endif
+}
+
+void dec_parport_count(void)
+{
+#ifdef MODULE
+       MOD_DEC_USE_COUNT;
+#endif
+}
diff --git a/drivers/misc/parport_pc.c b/drivers/misc/parport_pc.c
new file mode 100644 (file)
index 0000000..1df2e2b
--- /dev/null
@@ -0,0 +1,885 @@
+/* $Id: parport_pc.c,v 1.1.2.3 1997/04/18 15:00:52 phil Exp $ 
+ * Parallel-port routines for PC architecture
+ * 
+ * Authors: Phil Blundell <pjb27@cam.ac.uk>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *         Jose Renau <renau@acm.org>
+ *          David Campbell <campbell@tirian.che.curtin.edu.au>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/tasks.h>
+
+#include <asm/ptrace.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include <linux/parport.h>
+
+#define ECONTROL 0x402
+#define CONFIGB  0x401
+#define CONFIGA  0x400
+#define EPPREG   0x4
+#define CONTROL  0x2
+#define STATUS   0x1
+#define DATA     0
+
+#define PC_MAX_PORTS  8
+
+static void pc_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+       /* NULL function - Does nothing */
+       return;
+}
+
+static void pc_write_epp(struct parport *p, unsigned int d)
+{
+       outb(d, p->base+EPPREG);
+}
+
+static unsigned int pc_read_epp(struct parport *p)
+{
+       return (unsigned int)inb(p->base+EPPREG);
+}
+
+static unsigned int pc_read_configb(struct parport *p)
+{
+       return (unsigned int)inb(p->base+CONFIGB);
+}
+
+static void pc_write_data(struct parport *p, unsigned int d)
+{
+       outb(d, p->base+DATA);
+}
+
+static unsigned int pc_read_data(struct parport *p)
+{
+       return (unsigned int)inb(p->base+DATA);
+}
+
+static void pc_write_control(struct parport *p, unsigned int d)
+{
+       outb(d, p->base+CONTROL);
+}
+
+static unsigned int pc_read_control(struct parport *p)
+{
+       return (unsigned int)inb(p->base+CONTROL);
+}
+
+static unsigned int pc_frob_control(struct parport *p, unsigned int mask,  unsigned int val)
+{
+       unsigned int old = (unsigned int)inb(p->base+CONTROL);
+       outb(((old & ~mask) ^ val), p->base+CONTROL);
+       return old;
+}
+
+static void pc_write_status(struct parport *p, unsigned int d)
+{
+       outb(d, p->base+STATUS);
+}
+
+static unsigned int pc_read_status(struct parport *p)
+{
+       return (unsigned int)inb(p->base+STATUS);
+}
+
+static void pc_write_econtrol(struct parport *p, unsigned int d)
+{
+       outb(d, p->base+ECONTROL);
+}
+
+static unsigned int pc_read_econtrol(struct parport *p)
+{
+       return (unsigned int)inb(p->base+ECONTROL);
+}
+
+static unsigned int pc_frob_econtrol(struct parport *p, unsigned int mask,  unsigned int val)
+{
+       unsigned int old = (unsigned int)inb(p->base+ECONTROL);
+       outb(((old & ~mask) ^ val), p->base+ECONTROL);
+       return old;
+}
+
+static void pc_change_mode(struct parport *p, int m)
+{
+       /* FIXME */
+}
+
+static void pc_write_fifo(struct parport *p, unsigned int v)
+{
+       /* FIXME */
+}
+
+static unsigned int pc_read_fifo(struct parport *p)
+{
+       return 0; /* FIXME */
+}
+
+static void pc_disable_irq(struct parport *p)
+{
+       /* FIXME */
+}
+
+static void pc_enable_irq(struct parport *p)
+{
+       /* FIXME */
+}
+
+static void pc_release_resources(struct parport *p)
+{
+       if (p->irq != PARPORT_IRQ_NONE)
+               free_irq(p->irq, NULL);
+       release_region(p->base, p->size);
+       if (p->modes & PARPORT_MODE_PCECR)
+               release_region(p->base+0x400, 3);
+}
+
+static int pc_claim_resources(struct parport *p)
+{
+       /* FIXME check that resources are free */
+       if (p->irq != PARPORT_IRQ_NONE)
+               request_irq(p->irq, pc_null_intr_func, 0, p->name, NULL);
+       request_region(p->base, p->size, p->name);
+       if (p->modes & PARPORT_MODE_PCECR)
+               request_region(p->base+0x400, 3, p->name);
+       return 0;
+}
+
+static void pc_save_state(struct parport *p, struct parport_state *s)
+{
+       /* FIXME */
+}
+
+static void pc_restore_state(struct parport *p, struct parport_state *s)
+{
+       /* FIXME */
+}
+
+static unsigned int pc_epp_read_block(struct parport *p, void *buf, unsigned  int length)
+{
+       return 0; /* FIXME */
+}
+
+static unsigned int pc_epp_write_block(struct parport *p, void *buf, unsigned  int length)
+{
+       return 0; /* FIXME */
+}
+
+static unsigned int pc_ecp_read_block(struct parport *p, void *buf, unsigned  int length, void (*fn)(struct parport *, void *, unsigned int), void *handle)
+{
+       return 0; /* FIXME */
+}
+
+static unsigned int pc_ecp_write_block(struct parport *p, void *buf, unsigned  int length, void (*fn)(struct parport *, void *, unsigned int), void *handle)
+{
+       return 0; /* FIXME */
+}
+
+static int pc_examine_irq(struct parport *p)
+{
+       return 0; /* FIXME */
+}
+
+static struct parport_operations pc_ops = 
+{
+       pc_write_data,
+       pc_read_data,
+
+       pc_write_control,
+       pc_read_control,
+       pc_frob_control,
+
+       pc_write_econtrol,
+       pc_read_econtrol,
+       pc_frob_econtrol,
+
+       pc_write_status,
+       pc_read_status,
+
+       pc_write_fifo,
+       pc_read_fifo,
+       
+       pc_change_mode,
+       
+       pc_release_resources,
+       pc_claim_resources,
+       
+       pc_epp_write_block,
+       pc_epp_read_block,
+
+       pc_ecp_write_block,
+       pc_ecp_read_block,
+       
+       pc_save_state,
+       pc_restore_state,
+
+       pc_enable_irq,
+       pc_disable_irq,
+       pc_examine_irq 
+};
+
+/******************************************************
+ *  DMA detection section:
+ */
+
+/*
+ * Prepare DMA channels from 0-8 to transmit towards buffer
+ */
+static int parport_prepare_dma(char *buff, int size)
+{
+       int tmp = 0;
+       int i,retv;
+       
+       for (i = 0; i < 8; i++) {
+               retv = request_dma(i, "probe");
+               if (retv)
+                       continue;
+               tmp |= 1 << i;
+
+               cli();
+               disable_dma(i);
+               clear_dma_ff(i);
+               set_dma_addr(i, virt_to_bus(buff));
+               set_dma_count(i, size);
+               set_dma_mode(i, DMA_MODE_READ);
+               sti();
+       }
+
+       return tmp;
+}
+
+/*
+ * Activate all DMA channels passed in dma
+ */
+static int parport_enable_dma(int dma)
+{
+       int i;
+       
+       for (i = 0; i < 8; i++)
+               if (dma & (1 << i)) {
+               cli();
+               enable_dma(i);
+               sti();
+           }
+
+       return dma;
+}
+
+static int parport_detect_dma_transfer(int dma, int size)
+{
+       int i,n,retv;
+       int count=0;
+
+       retv = PARPORT_DMA_NONE;
+       for (i = 0; i < 8; i++)
+               if (dma & (1 << i)) {
+                       disable_dma(i);
+                       clear_dma_ff(i);
+                       n = get_dma_residue(i);
+                       if (n != size) {
+                               retv = i;
+                               if (count > 0) {
+                                       retv = PARPORT_DMA_NONE; /* Multiple DMA's */
+                                       printk(KERN_ERR "parport: multiple DMA detected.  Huh?\n");
+                               }
+                               count++;
+                       }
+                       free_dma(i);
+               }
+
+       return retv;    
+}
+
+/* Only if supports ECP mode */
+static int programmable_dma_support(struct parport *pb)
+{
+       int dma, oldstate = pc_read_econtrol(pb);
+
+       pc_write_econtrol(pb, 0xe0); /* Configuration MODE */
+       
+       dma = pc_read_configb(pb) & 0x07;
+
+       pc_write_econtrol(pb, oldstate);
+       
+       if (dma == 0 || dma == 4) /* Jumper selection */
+               return PARPORT_DMA_NONE;
+       else
+               return dma;
+}
+
+/* Only called if port supports ECP mode.
+ *
+ * The only restriction on DMA channels is that it has to be
+ * between 0 to 7 (inclusive). Used only in an ECP mode, DMAs are
+ * considered a shared resource and hence they should be registered
+ * when needed and then immediately unregistered.
+ *
+ * DMA autoprobes for ECP mode are known not to work for some
+ * main board BIOS configs. I had to remove everything from the
+ * port, set the mode to SPP, reboot to DOS, set the mode to ECP,
+ * and reboot again, then I got IRQ probes and DMA probes to work.
+ * [Is the BIOS doing a device detection?]
+ *
+ * A value of PARPORT_DMA_NONE is allowed indicating no DMA support.
+ *
+ * if( 0 < DMA < 4 )
+ *    1Byte DMA transfer
+ * else // 4 < DMA < 8
+ *    2Byte DMA transfer
+ *
+ */
+static int parport_dma_probe(struct parport *pb)
+{
+       int dma,retv;
+       int dsr,dsr_read;
+       char *buff;
+
+       retv = programmable_dma_support(pb);
+       if (retv != PARPORT_DMA_NONE)
+               return retv;
+       
+       if (!(buff = kmalloc(2048, GFP_KERNEL | GFP_DMA))) {
+           printk(KERN_ERR "parport: memory squeeze\n");
+           return PARPORT_DMA_NONE;
+       }
+       
+       dsr = pb->ops->read_control(pb);
+       dsr_read = (dsr & ~(0x20)) | 0x04;    /* Direction == read */
+
+       pb->ops->write_econtrol(pb, 0xc0);         /* ECP MODE */
+       pb->ops->write_control(pb, dsr_read );
+       dma = parport_prepare_dma(buff, 1000);
+       pb->ops->write_econtrol(pb, 0xd8);         /* ECP FIFO + enable DMA */
+       parport_enable_dma(dma);
+       udelay(500);           /* Give some for DMA tranfer */
+       retv = parport_detect_dma_transfer(dma, 1000);
+       
+       /*
+        * National Semiconductors only supports DMA tranfers
+        * in ECP MODE
+        */
+       if (retv == PARPORT_DMA_NONE) {
+               pb->ops->write_econtrol(pb, 0x60);         /* ECP MODE */
+               pb->ops->write_control(pb, dsr_read );
+               dma=parport_prepare_dma(buff,1000);
+               pb->ops->write_econtrol(pb, 0x68);         /* ECP FIFO + enable DMA */
+               parport_enable_dma(dma);
+               udelay(500);           /* Give some for DMA tranfer */
+               retv = parport_detect_dma_transfer(dma, 1000);
+       }
+       
+       kfree(buff);
+       
+       return retv;
+}
+/******************************************************
+ *  MODE detection section:
+ */
+
+/*
+ * Clear TIMEOUT BIT in EPP MODE
+ */
+static int epp_clear_timeout(struct parport *pb)
+{
+       int r;
+
+       if (!(pc_read_status(pb) & 0x01))
+               return 1;
+
+       /* To clear timeout some chips require double read */
+       pc_read_status(pb);
+       r = pc_read_status(pb);
+       pc_write_status(pb, r | 0x01); /* Some reset by writing 1 */
+       pc_write_status(pb, r & 0xfe); /* Others by writing 0 */
+       r = pc_read_status(pb);
+
+       return !(r & 0x01);
+}
+
+
+/*
+ * Checks for port existence, all ports support SPP MODE
+ */
+static int parport_SPP_supported(struct parport *pb)
+{
+       /* Do a simple read-write test to make sure the port exists. */
+       pc_write_control(pb, 0xc);
+       pc_write_data(pb, 0xaa);
+       if (pc_read_data(pb) != 0xaa) return 0;
+       
+       pc_write_data(pb, 0x55);
+       if (pc_read_data(pb) != 0x55) return 0;
+
+       return PARPORT_MODE_PCSPP;
+}
+
+/* Check for ECP
+ *
+ * Old style XT ports alias io ports every 0x400, hence accessing ECR
+ * on these cards actually accesses the CTR.
+ *
+ * Modern cards don't do this but reading from ECR will return 0xff
+ * regardless of what is written here if the card does NOT support
+ * ECP.
+ *
+ * We will write 0x2c to ECR and 0xcc to CTR since both of these
+ * values are "safe" on the CTR since bits 6-7 of CTR are unused.
+ */
+static int parport_ECR_present(struct parport *pb)
+{
+       int r, octr = pc_read_control(pb), oecr = pc_read_econtrol(pb);
+
+       r= pc_read_control(pb); 
+       if ((pc_read_econtrol(pb) & 0x03) == (r & 0x03)) {
+               pc_write_control(pb, r ^ 0x03 ); /* Toggle bits 0-1 */
+
+               r= pc_read_control(pb); 
+               if ((pc_read_econtrol(pb) & 0x03) == (r & 0x03))
+                       return 0; /* Sure that no ECR register exists */
+       }
+       
+       if ((pc_read_econtrol(pb) & 0x03 ) != 0x01)
+               return 0;
+
+       pc_write_econtrol(pb,0x34);
+       if (pc_read_econtrol(pb) != 0x35)
+               return 0;
+
+       pc_write_econtrol(pb, oecr);
+       pc_write_control(pb, octr);
+       
+       return PARPORT_MODE_PCECR;
+}
+
+static int parport_ECP_supported(struct parport *pb)
+{
+       int i, oecr = pc_read_econtrol(pb);
+       
+       /* If there is no ECR, we have no hope of supporting ECP. */
+       if (!(pb->modes & PARPORT_MODE_PCECR))
+               return 0;
+
+       /*
+        * Using LGS chipset it uses ECR register, but
+        * it doesn't support ECP or FIFO MODE
+        */
+       
+       pc_write_econtrol(pb, 0xc0); /* TEST FIFO */
+       for (i=0; i < 1024 && (pc_read_econtrol(pb) & 0x01); i++)
+               pc_write_fifo(pb, 0xaa);
+
+       pc_write_econtrol(pb, oecr);
+       return (i==1024)?0:PARPORT_MODE_PCECP;
+}
+
+/* EPP mode detection
+ * Theory:
+ *     Bit 0 of STR is the EPP timeout bit, this bit is 0
+ *     when EPP is possible and is set high when an EPP timeout
+ *     occurs (EPP uses the HALT line to stop the CPU while it does
+ *     the byte transfer, an EPP timeout occurs if the attached
+ *     device fails to respond after 10 micro seconds).
+ *
+ *     This bit is cleared by either reading it (National Semi)
+ *     or writing a 1 to the bit (SMC, UMC, WinBond), others ???
+ *     This bit is always high in non EPP modes.
+ */
+static int parport_EPP_supported(struct parport *pb)
+{
+       /* If EPP timeout bit clear then EPP available */
+       if (!epp_clear_timeout(pb))
+               return 0;  /* No way to clear timeout */
+
+       pc_write_control(pb, pc_read_control(pb) | 0x20);
+       pc_write_control(pb, pc_read_control(pb) | 0x10);
+       epp_clear_timeout(pb);
+       
+       pc_read_epp(pb);
+       udelay(30);  /* Wait for possible EPP timeout */
+       
+       if (pc_read_status(pb) & 0x01) {
+               epp_clear_timeout(pb);
+               return PARPORT_MODE_PCEPP;
+       }
+
+       return 0;
+}
+
+static int parport_ECPEPP_supported(struct parport *pb)
+{
+       int mode, oecr = pc_read_econtrol(pb);
+
+       if (!(pb->modes & PARPORT_MODE_PCECR))
+               return 0;
+       
+       /* Search for SMC style EPP+ECP mode */
+       pc_write_econtrol(pb, 0x80);
+       
+       mode = parport_EPP_supported(pb);
+
+       pc_write_econtrol(pb, oecr);
+       
+       return mode?PARPORT_MODE_PCECPEPP:0;
+}
+
+/* Detect PS/2 support.
+ *
+ * Bit 5 (0x20) sets the PS/2 data direction; setting this high
+ * allows us to read data from the data lines.  In theory we would get back
+ * 0xff but any peripheral attached to the port may drag some or all of the
+ * lines down to zero.  So if we get back anything that isn't the contents
+ * of the data register we deem PS/2 support to be present. 
+ *
+ * Some SPP ports have "half PS/2" ability - you can't turn off the line
+ * drivers, but an external peripheral with sufficiently beefy drivers of
+ * its own can overpower them and assert its own levels onto the bus, from
+ * where they can then be read back as normal.  Ports with this property
+ * and the right type of device attached are likely to fail the SPP test,
+ * (as they will appear to have stuck bits) and so the fact that they might
+ * be misdetected here is rather academic. 
+ */
+
+static int parport_PS2_supported(struct parport *pb)
+{
+       int ok = 0, octr = pc_read_control(pb);
+  
+       epp_clear_timeout(pb);
+
+       pc_write_control(pb, octr | 0x20);  /* try to tri-state the buffer */
+       
+       pc_write_data(pb, 0x55);
+       if (pc_read_data(pb) != 0x55) ok++;
+
+       pc_write_data(pb, 0xaa);
+       if (pc_read_data(pb) != 0xaa) ok++;
+       
+       pc_write_control(pb, octr);          /* cancel input mode */
+
+       return ok?PARPORT_MODE_PCPS2:0;
+}
+
+static int parport_ECPPS2_supported(struct parport *pb)
+{
+       int mode, oecr = pc_read_econtrol(pb);
+
+       if (!(pb->modes & PARPORT_MODE_PCECR))
+               return 0;
+       
+       pc_write_econtrol(pb, 0x20);
+       
+       mode = parport_PS2_supported(pb);
+
+       pc_write_econtrol(pb, oecr);
+       return mode?PARPORT_MODE_PCECPPS2:0;
+}
+
+/******************************************************
+ *  IRQ detection section:
+ *
+ * This code is for detecting ECP interrupts (due to problems with the
+ * monolithic interrupt probing routines).
+ *
+ * In short this is a voting system where the interrupt with the most
+ * "votes" is the elected interrupt (it SHOULD work...)
+ *
+ * This is horribly x86-specific at the moment.  I'm not convinced it
+ * belongs at all.
+ */
+
+static int intr_vote[16];
+
+static void parport_vote_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+       intr_vote[irq]++;
+       return;
+}
+
+static long open_intr_election(void)
+{
+       long tmp = 0;
+       int i;
+
+       /* We ignore the timer - irq 0 */
+       for (i = 1; i < 16; i++) {
+               intr_vote[i] = 0;
+               if (request_irq(i, parport_vote_intr_func,
+                      SA_INTERRUPT, "probe", intr_vote) == 0)
+                       tmp |= 1 << i;
+       }
+       return tmp;
+}
+
+static int close_intr_election(long tmp)
+{
+       int irq = PARPORT_IRQ_NONE;
+       int i;
+
+       /* We ignore the timer - irq 0 */
+       for (i = 1; i < 16; i++)
+               if (tmp & (1 << i)) {
+                       if (intr_vote[i]) {
+                               if (irq != PARPORT_IRQ_NONE)
+                                       /* More than one interrupt */
+                                       return PARPORT_IRQ_NONE;
+                               irq = i;
+                       }
+                       free_irq(i, intr_vote);
+               }
+       return irq;
+}
+
+/* Only if supports ECP mode */
+static int programmable_irq_support(struct parport *pb)
+{
+       int irq, oecr = pc_read_econtrol(pb);
+
+       pc_write_econtrol(pb,0xE0); /* Configuration MODE */
+       
+       irq = (pc_read_configb(pb) >> 3) & 0x07;
+
+       switch(irq){
+       case 2:
+               irq = 9;
+               break;
+       case 7:
+               irq = 5;
+               break;
+       case 0:
+               irq = PARPORT_IRQ_NONE;
+               break;
+       default:
+               irq += 7;
+       }
+       
+       pc_write_econtrol(pb, oecr);
+       return irq;
+}
+
+static int irq_probe_ECP(struct parport *pb)
+{
+       int irqs, i, oecr = pc_read_econtrol(pb);
+               
+       probe_irq_off(probe_irq_on());  /* Clear any interrupts */
+       irqs = open_intr_election();
+               
+       pc_write_econtrol(pb, 0x00);        /* Reset FIFO */
+       pc_write_econtrol(pb, 0xd0);        /* TEST FIFO + nErrIntrEn */
+
+       /* If Full FIFO sure that WriteIntrThresold is generated */
+       for (i=0; i < 1024 && !(pc_read_econtrol(pb) & 0x02) ; i++) 
+               pc_write_fifo(pb, 0xaa);
+               
+       pb->irq = close_intr_election(irqs);
+       pc_write_econtrol(pb, oecr);
+       return pb->irq;
+}
+
+/*
+ * This detection seems that only works in National Semiconductors
+ * This doesn't work in SMC, LGS, and Winbond 
+ */
+static int irq_probe_EPP(struct parport *pb)
+{
+       int irqs, octr = pc_read_control(pb);
+
+#ifndef ADVANCED_DETECT
+       return PARPORT_IRQ_NONE;
+#endif
+       
+       probe_irq_off(probe_irq_on());  /* Clear any interrupts */
+       irqs = open_intr_election();
+
+       if (pb->modes & PARPORT_MODE_PCECR)
+               pc_write_econtrol(pb, pc_read_econtrol(pb) | 0x10);
+       
+       epp_clear_timeout(pb);
+       pc_write_control(pb, pc_read_control(pb) | 0x20);
+       pc_write_control(pb, pc_read_control(pb) | 0x10);
+       epp_clear_timeout(pb);
+
+       /*  Device isn't expecting an EPP read
+        * and generates an IRQ.
+        */
+       pc_read_epp(pb);
+       udelay(20);
+
+       pb->irq = close_intr_election(irqs);
+       pc_write_control(pb, octr);
+       return pb->irq;
+}
+
+static int irq_probe_SPP(struct parport *pb)
+{
+       int irqs, octr = pc_read_control(pb);
+
+#ifndef ADVANCED_DETECT
+       return PARPORT_IRQ_NONE;
+#endif
+
+       probe_irq_off(probe_irq_on());  /* Clear any interrupts */
+       irqs = probe_irq_on();
+
+       if (pb->modes & PARPORT_MODE_PCECR)
+               pc_write_econtrol(pb, 0x10);
+
+       pc_write_data(pb,0x00);
+       pc_write_control(pb,0x00);
+       pc_write_control(pb,0x0c);
+       udelay(5);
+       pc_write_control(pb,0x0d);
+       udelay(5);
+       pc_write_control(pb,0x0c);
+       udelay(25);
+       pc_write_control(pb,0x08);
+       udelay(25);
+       pc_write_control(pb,0x0c);
+       udelay(50);
+
+       pb->irq = probe_irq_off(irqs);
+       if (pb->irq <= 0)
+               pb->irq = PARPORT_IRQ_NONE;     /* No interrupt detected */
+       
+       pc_write_control(pb, octr);
+       return pb->irq;
+}
+
+/* We will attempt to share interrupt requests since other devices
+ * such as sound cards and network cards seem to like using the
+ * printer IRQs.
+ *
+ * When ECP is available we can autoprobe for IRQs.
+ * NOTE: If we can autoprobe it, we can register the IRQ.
+ */
+static int parport_irq_probe(struct parport *pb)
+{
+       if (pb->modes & PARPORT_MODE_PCECR)
+               pb->irq = programmable_irq_support(pb);
+
+       if (pb->modes & PARPORT_MODE_PCECP)
+               pb->irq = irq_probe_ECP(pb);
+                       
+       if (pb->irq == PARPORT_IRQ_NONE && 
+           (pb->modes & PARPORT_MODE_PCECPEPP)) {
+               int oecr = pc_read_econtrol(pb);
+               pc_write_econtrol(pb, 0x80);
+               pb->irq = irq_probe_EPP(pb);
+               pc_write_econtrol(pb, oecr);
+       }
+
+       epp_clear_timeout(pb);
+
+       if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_PCEPP))
+               pb->irq = irq_probe_EPP(pb);
+
+       epp_clear_timeout(pb);
+
+       if (pb->irq == PARPORT_IRQ_NONE)
+               pb->irq = irq_probe_SPP(pb);
+
+       return pb->irq;
+}
+
+static int probe_one_port(unsigned long int base, int irq, int dma)
+{
+       struct parport tmpport, *p;
+       if (check_region(base, 3)) return 0;
+       tmpport.base = base;
+       tmpport.ops = &pc_ops;
+       if (!(parport_SPP_supported(&tmpport))) return 0;
+               if (!(p = parport_register_port(base, irq, dma, &pc_ops))) return 0;
+       p->modes = PARPORT_MODE_PCSPP | parport_PS2_supported(p);
+       if (p->base != 0x3bc) {
+               if (!check_region(base+0x400,3)) {
+                       p->modes |= parport_ECR_present(p);     
+                       p->modes |= parport_ECP_supported(p);
+                       p->modes |= parport_ECPPS2_supported(p);
+               }
+               if (!check_region(base+0x3, 5)) {
+                       p->modes |= parport_EPP_supported(p);
+                       p->modes |= parport_ECPEPP_supported(p);
+               }
+       }
+       p->size = (p->modes & (PARPORT_MODE_PCEPP 
+                              | PARPORT_MODE_PCECPEPP))?8:3;
+       printk(KERN_INFO "%s: PC-style at 0x%x", p->name, p->base);
+       if (p->irq == PARPORT_IRQ_AUTO) {
+               p->irq = PARPORT_IRQ_NONE;
+               parport_irq_probe(p);
+       }
+       if (p->irq != PARPORT_IRQ_NONE)
+               printk(", irq %d", p->irq);
+       if (p->irq != PARPORT_DMA_NONE)
+               printk(", dma %d", p->dma);
+       printk(" [");
+#define printmode(x) {if(p->modes&PARPORT_MODE_PC##x){printk("%s%s",f?",":"",#x);f++;}}
+       {
+               int f = 0;
+               printmode(SPP);
+               printmode(PS2);
+               printmode(EPP);
+               printmode(ECP);
+               printmode(ECPEPP);
+               printmode(ECPPS2);
+       }
+#undef printmode
+       printk("]\n");
+       return 1;
+}
+
+int parport_pc_init(int *io, int *irq, int *dma)
+{
+       int count = 0, i = 0;
+       if (io && *io) {
+               /* Only probe the ports we were given. */
+               do {
+                       count += probe_one_port(*(io++), *(irq++), *(dma++));
+               } while (*io && (++i < PC_MAX_PORTS));
+       } else {
+               /* Probe all the likely ports. */
+               count += probe_one_port(0x378, PARPORT_IRQ_AUTO, PARPORT_DMA_AUTO);
+               
+#if defined(__i386__)
+               count += probe_one_port(0x278, PARPORT_IRQ_AUTO, PARPORT_DMA_AUTO);
+               count += probe_one_port(0x3bc, PARPORT_IRQ_AUTO, PARPORT_DMA_AUTO);
+#endif
+       }
+       return count;
+}
+
+#ifdef MODULE
+static int io[PC_MAX_PORTS+1] = { 0, };
+static int dma[PC_MAX_PORTS] = { PARPORT_DMA_AUTO, };
+static int irq[PC_MAX_PORTS] = { PARPORT_IRQ_AUTO, };
+MODULE_PARM(io, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
+MODULE_PARM(irq, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
+MODULE_PARM(dma, "1-" __MODULE_STRING(PC_MAX_PORTS) "i");
+
+static int init_module(void)
+{      
+       return (parport_pc_init(NULL, NULL, NULL)?0:1);
+}
+
+static void cleanup_module(void)
+{
+       struct parport *p = parport_enumerate();
+       while (p) {
+               if (p->modes & PARPORT_MODE_PCSPP) { 
+                       if (!(p->flags & PARPORT_FLAG_COMA)) 
+                               parport_quiesce(p);
+               }
+               p = p->next;
+       }
+}
+#endif
diff --git a/drivers/misc/parport_procfs.c b/drivers/misc/parport_procfs.c
new file mode 100644 (file)
index 0000000..edcead8
--- /dev/null
@@ -0,0 +1,341 @@
+/* $Id: parport_procfs.c,v 1.1.2.2 1997/04/18 15:00:52 phil Exp $
+ * Parallel port /proc interface code.
+ * 
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/tasks.h>
+#include <asm/ptrace.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include <linux/proc_fs.h>
+
+#include <linux/parport.h>
+
+#undef PARPORT_INCLUDE_BENCH
+
+struct proc_dir_entry *base=NULL;
+
+extern void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs);
+
+static int irq_write_proc(struct file *file, const char *buffer,
+                                         unsigned long count, void *data)
+{
+       int newirq;
+       struct parport *pp = (struct parport *)data;
+       
+       if (count > 4 )  /* more than 4 digits for a irq 0x?? 0?? ??  */
+               return(-EOVERFLOW);
+
+       if (buffer[0] < 32 || !strncmp(buffer, "none", 4)) {
+               newirq = PARPORT_IRQ_NONE;
+       } else {
+               if (buffer[0] == '0') {
+                       if( buffer[1] == 'x' )
+                               newirq = simple_strtoul(&buffer[2],0,16);
+                       else
+                               newirq = simple_strtoul(&buffer[1],0,8);
+               } else {
+                       newirq = simple_strtoul(buffer,0,10);
+               }
+       }
+
+       if (pp->irq != PARPORT_IRQ_NONE && !(pp->flags & PARPORT_FLAG_COMA)) 
+               free_irq(pp->irq, pp);
+
+       pp->irq = newirq;
+
+       if (pp->irq != PARPORT_IRQ_NONE && !(pp->flags & PARPORT_FLAG_COMA)) { 
+               struct pardevice *pd = pp->cad;
+
+               if (pd == NULL) {
+                       pd = pp->devices;
+                       if (pd != NULL) 
+                               request_irq(pp->irq, pd->irq_func ? 
+                                           pd->irq_func :
+                                           parport_null_intr_func,
+                                           SA_INTERRUPT, pd->name, pd->port);
+               } else {
+                       request_irq(pp->irq, pd->irq_func ? pd->irq_func :
+                                   parport_null_intr_func,
+                                   SA_INTERRUPT, pp->name, pd->port);
+               }
+       }
+
+       return count;
+}
+
+static int irq_read_proc(char *page, char **start, off_t off,
+                                        int count, int *eof, void *data)
+{
+       struct parport *pp = (struct parport *)data;
+       int len;
+       
+       if (pp->irq == PARPORT_IRQ_NONE)
+               len = sprintf(page, "none\n");
+       else
+               len = sprintf(page, "%d\n", pp->irq);
+       
+       *start = 0;
+       *eof   = 1;
+       return len;
+}
+
+static int devices_read_proc(char *page, char **start, off_t off,
+                                        int count, int *eof, void *data)
+{
+       struct parport *pp = (struct parport *)data;
+       struct pardevice *pd1;
+       int len=0;
+
+       for (pd1 = pp->devices; pd1 ; pd1 = pd1->next) {
+               if (pd1 == pp->cad)
+                       len += sprintf(page+len, "+");
+               else
+                       len += sprintf(page+len, " ");
+
+               len += sprintf(page+len, "%s",pd1->name);
+
+               if (pd1 == pp->lurker)
+                       len += sprintf(page+len, " LURK");
+               
+               len += sprintf(page+len,"\n");
+       }
+               
+       *start = 0;
+       *eof   = 1;
+       return len;
+}
+
+static int hardware_read_proc(char *page, char **start, off_t off,
+                                                         int count, int *eof, void *data)
+{
+       struct parport *pp = (struct parport *)data;
+       int len=0;
+       
+       len += sprintf(page+len, "base:\t0x%x\n",pp->base);
+       if (pp->irq == PARPORT_IRQ_NONE)
+               len += sprintf(page+len, "irq:\tnone\n");
+       else
+               len += sprintf(page+len, "irq:\t%d\n",pp->irq);
+       len += sprintf(page+len, "dma:\t%d\n",pp->dma);
+
+
+#if 0
+       len += sprintf(page+len, "modes:\t");
+       {
+#define printmode(x) {if(pp->modes&PARPORT_MODE_##x){len+=sprintf(page+len,"%s%s",f?",":"",#x);f++;}}
+               int f = 0;
+               printmode(SPP);
+               printmode(PS2);
+               printmode(EPP);
+               printmode(ECP);
+               printmode(ECPEPP);
+               printmode(ECPPS2);
+#undef printmode
+       }
+       len += sprintf(page+len, "\n");
+
+       len += sprintf(page+len, "mode:\t");
+       if (pp->modes & PARPORT_MODE_ECR) {
+               switch (r_ecr(pp) >> 5) {
+               case 0:
+                       len += sprintf(page+len, "SPP");
+                       if( pp->modes & PARPORT_MODE_PS2 )
+                               len += sprintf(page+len, ",PS2");
+                       if( pp->modes & PARPORT_MODE_EPP )
+                               len += sprintf(page+len, ",EPP");
+                       break;
+               case 1:
+                       len += sprintf(page+len, "ECPPS2");
+                       break;
+               case 2:
+                       len += sprintf(page+len, "DATAFIFO");
+                       break;
+               case 3:
+                       len += sprintf(page+len, "ECP");
+                       break;
+               case 4:
+                       len += sprintf(page+len, "ECPEPP");
+                       break;
+               case 5:
+                       len += sprintf(page+len, "Reserved?");
+                       break;
+               case 6:
+                       len += sprintf(page+len, "TEST");
+                       break;
+               case 7:
+                       len += sprintf(page+len, "Configuration");
+                       break;
+               }
+       } else {
+               len += sprintf(page+len, "SPP");
+               if (pp->modes & PARPORT_MODE_PS2)
+                       len += sprintf(page+len, ",PS2");
+               if (pp->modes & PARPORT_MODE_EPP)
+                       len += sprintf(page+len, ",EPP");
+       }
+       len += sprintf(page+len, "\n");
+#endif 
+#if 0
+       /* Now no detection, please fix with an external function */
+       len += sprintf(page+len, "chipset:\tunknown\n");
+#endif
+#ifdef PARPORT_INCLUDE_BENCHMARK
+       if (pp->speed)
+               len += sprintf(page+len, "bench:\t%d Bytes/s\n",pp->speed);
+       else
+               len += sprintf(page+len, "bench:\tunknown\n");
+#endif 
+
+       *start = 0;
+       *eof   = 1;
+       return len;
+}
+
+static struct proc_dir_entry *new_proc_entry(const char *name, mode_t mode,
+                                            struct proc_dir_entry *parent,
+                                            unsigned short ino)
+{
+       struct proc_dir_entry *ent;
+
+       ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
+       if (!ent)
+               return NULL;
+       memset(ent, 0, sizeof(struct proc_dir_entry));
+       
+       if (mode == S_IFDIR)
+               mode |= S_IRUGO | S_IXUGO;
+       else if (mode == 0)
+               mode = S_IFREG | S_IRUGO;
+
+
+       ent->low_ino = ino;
+       ent->name = name;
+       ent->namelen = strlen(name);
+       ent->mode = mode;
+       if (S_ISDIR(mode)) 
+               ent->nlink = 2;
+       else
+               ent->nlink = 1;
+
+       proc_register(parent, ent);
+       
+       return ent;
+}
+
+
+int parport_proc_init()
+{
+       base = new_proc_entry("parport", S_IFDIR, &proc_root,PROC_PARPORT);
+
+       if (base)
+               return 1;
+       else {
+               printk(KERN_ERR "parport: Error creating proc entry /proc/parport\n");
+               return 0;
+       }
+}
+
+int parport_proc_cleanup()
+{
+       if (base)
+               proc_unregister(&proc_root,base->low_ino);
+
+       base = NULL;
+       
+       return 0;
+}
+
+int parport_proc_register(struct parport *pp)
+{
+       struct proc_dir_entry *ent;
+       static int conta=0;
+       char *name;
+
+       memset(&pp->pdir,0,sizeof(struct parport_dir));
+
+       if (!base) {
+               printk(KERN_ERR "parport: Error entry /proc/parport, not generated?\n");
+               return 1;
+       }
+       
+       name = pp->pdir.name;
+       sprintf(name,"%d",conta++);
+
+       ent = new_proc_entry(name, S_IFDIR, base,0);
+       if (!ent) {
+               printk(KERN_ERR "parport: Error registering proc_entry /proc/%s\n",name);
+               return 1;
+       }
+       pp->pdir.entry = ent;
+
+       ent = new_proc_entry("irq", S_IFREG | S_IRUGO | S_IWUSR, pp->pdir.entry,0);
+       if (!ent) {
+               printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/irq\n",name);
+               return 1;
+       }
+       ent->read_proc = irq_read_proc;
+       ent->write_proc= irq_write_proc;
+       ent->data      = pp;
+       pp->pdir.irq   = ent;
+       
+       ent = new_proc_entry("devices", 0, pp->pdir.entry,0);
+       if (!ent) {
+               printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/devices\n",name);
+               return 1;
+       }
+       ent->read_proc   = devices_read_proc;
+       ent->data        = pp;
+       pp->pdir.devices = ent;
+       
+       ent = new_proc_entry("hardware", 0, pp->pdir.entry,0);
+       if (!ent) {
+               printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/hardware\n",name);
+               return 1;
+       }
+       ent->read_proc    = hardware_read_proc;
+       ent->data         = pp;
+       pp->pdir.hardware = ent;
+       return 0;
+}
+
+int parport_proc_unregister(struct parport *pp)
+{
+       if (pp->pdir.entry) {
+               if (pp->pdir.irq) {
+                       proc_unregister(pp->pdir.entry, pp->pdir.irq->low_ino);
+                       kfree(pp->pdir.irq);
+               }
+               
+               if (pp->pdir.devices) {
+                       proc_unregister(pp->pdir.entry,
+                                       pp->pdir.devices->low_ino);
+                       kfree(pp->pdir.devices);
+               }
+               
+               if (pp->pdir.hardware) {
+                       proc_unregister(pp->pdir.entry,
+                                       pp->pdir.hardware->low_ino);
+                       kfree(pp->pdir.hardware);
+               }
+               
+               proc_unregister(base, pp->pdir.entry->low_ino);
+               kfree(pp->pdir.entry);
+       }
+       
+       return 0;
+}
diff --git a/drivers/misc/parport_share.c b/drivers/misc/parport_share.c
new file mode 100644 (file)
index 0000000..ea9402d
--- /dev/null
@@ -0,0 +1,319 @@
+/* $Id: parport_share.c,v 1.1.2.2 1997/04/18 15:00:52 phil Exp $
+ * Parallel-port resource manager code.
+ * 
+ * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
+ *          Tim Waugh <tmw20@cam.ac.uk>
+ *         Jose Renau <renau@acm.org>
+ *
+ * based on work by Grant Guenther <grant@torque.net>
+ *              and Philip Blundell <Philip.Blundell@pobox.com>
+ */
+
+#include <linux/tasks.h>
+
+#include <linux/parport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#undef PARPORT_PARANOID
+
+static struct parport *portlist = NULL, *portlist_tail = NULL;
+static int portcount = 0;
+
+/* Return a list of all the ports we know about. */
+struct parport *parport_enumerate(void)
+{
+       return portlist;
+}
+
+void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+       /* NULL function - Does nothing */
+       return;
+}
+
+struct parport *parport_register_port(unsigned long base, int irq, int dma,
+                                     struct parport_operations *ops)
+{
+       struct parport *tmp;
+
+       /* Check for a previously registered port.
+        * NOTE: we will ignore irq and dma if we find a previously
+        * registered device.
+        */
+       for (tmp = portlist; tmp; tmp = tmp->next) {
+               if (tmp->base == base)
+                       return tmp;
+       }
+
+       tmp = kmalloc(sizeof(struct parport), GFP_KERNEL);
+       if (!tmp) {
+               printk(KERN_WARNING "parport: memory squeeze\n");
+               return NULL;
+       }
+
+       /* Init our structure */
+       memset(tmp, 0, sizeof(struct parport));
+       tmp->base = base;
+       tmp->irq = irq;
+       tmp->dma = dma;
+       tmp->modes = 0;
+       tmp->next = NULL;
+       tmp->devices = tmp->cad = tmp->lurker = NULL;
+       tmp->flags = 0;
+       tmp->ops = ops; 
+
+       tmp->name = kmalloc(15, GFP_KERNEL);
+       if (!tmp->name) {
+               printk(KERN_ERR "parport: memory squeeze\n");
+               kfree(tmp);
+               return NULL;
+       }
+       sprintf(tmp->name, "parport%d", portcount);
+
+       /* Here we chain the entry to our list. */
+       if (portlist_tail)
+               portlist_tail->next = tmp;
+       portlist_tail = tmp;
+       if (!portlist)
+               portlist = tmp;
+
+       portcount++;
+
+       tmp->probe_info.class = PARPORT_CLASS_LEGACY;  /* assume the worst */
+       return tmp;
+}
+
+void parport_quiesce(struct parport *port)
+{
+       if (port->devices) {
+               printk(KERN_WARNING "%s: attempt to quiesce active port.\n", port->name);
+               return;
+       }
+
+       if (port->flags & PARPORT_FLAG_COMA) {
+               printk(KERN_WARNING "%s: attempt to quiesce comatose port.\n", port->name);
+               return;
+       }
+
+       port->ops->release_resources(port);
+
+       port->flags |= PARPORT_FLAG_COMA; 
+}
+
+struct pardevice *parport_register_device(struct parport *port, const char *name,
+                         int (*pf)(void *), int (*kf)(void *),
+                         void (*irq_func)(int, void *, struct pt_regs *), 
+                         int flags, void *handle)
+{
+       struct pardevice *tmp;
+
+       /* We only allow one lurking device. */
+       if (flags & PARPORT_DEV_LURK) {
+               if (port->lurker) {
+                       printk(KERN_INFO "%s: refused to register second lurker (%s)\n",
+                                  port->name, name);
+                       return NULL;
+               }
+               if (!pf || !kf) {
+                       printk(KERN_INFO "%s: refused to register lurking device (%s) without callbacks\n", port->name, name);
+                       return NULL;
+               }
+       }
+
+       /* We may need to claw back the port hardware. */
+       if (port->flags & PARPORT_FLAG_COMA) {
+               if (port->ops->claim_resources(port)) {
+                       printk(KERN_WARNING "%s: unable to get hardware to register %s.\n", port->name, name);
+                       return NULL;
+               }
+               port->flags &= ~PARPORT_FLAG_COMA;
+       }
+
+       tmp = kmalloc(sizeof(struct pardevice), GFP_KERNEL);
+       if (tmp == NULL) {
+               printk(KERN_WARNING "%s: memory squeeze, couldn't register %s.\n", port->name, name);
+               return NULL;
+       }
+
+       tmp->state = kmalloc(sizeof(struct parport_state), GFP_KERNEL);
+       if (tmp->state == NULL) {
+               printk(KERN_WARNING "%s: memory squeeze, couldn't register %s.\n", port->name, name);
+               kfree(tmp);
+               return NULL;
+       }
+
+       tmp->name = (char *) name;
+       tmp->port = port;
+       tmp->preempt = pf;
+       tmp->wakeup = kf;
+       tmp->private = handle;
+       tmp->flags = flags;
+       tmp->irq_func = irq_func;
+       port->ops->save_state(port, tmp->state);
+
+       /* Chain this onto the list */
+       tmp->prev = NULL;
+       tmp->next = port->devices;
+       if (port->devices)
+               port->devices->prev = tmp;
+       port->devices = tmp;
+
+       if (flags & PARPORT_DEV_LURK)
+               port->lurker = tmp;
+
+       inc_parport_count();
+
+       return tmp;
+}
+
+void parport_unregister_device(struct pardevice *dev)
+{
+       struct parport *port;
+
+       if (dev == NULL) {
+               printk(KERN_ERR "parport_unregister_device: passed NULL\n");
+               return;
+       }
+
+       port = dev->port;
+
+       if (port->cad == dev) {
+               printk(KERN_WARNING "%s: refused to unregister currently active device %s.\n", port->name, dev->name);
+               return;
+       }
+
+       if (port->lurker == dev)
+               port->lurker = NULL;
+
+       if (dev->next)
+               dev->next->prev = dev->prev;
+       if (dev->prev)
+               dev->prev->next = dev->next;
+       else
+               port->devices = dev->next;
+
+       kfree(dev->state);
+       kfree(dev);
+
+       dec_parport_count();
+
+       /* If there are no more devices, put the port to sleep. */
+       if (!port->devices)
+               parport_quiesce(port);
+
+       return;
+}
+
+int parport_claim(struct pardevice *dev)
+{
+       struct pardevice *pd1;
+
+       if (dev->port->cad == dev) {
+               printk(KERN_INFO "%s: %s already owner\n",
+                          dev->port->name,dev->name);
+               return 0;
+       }
+
+       /* Preempt any current device */
+       pd1 = dev->port->cad;
+       if (dev->port->cad) {
+               if (dev->port->cad->preempt) {
+                       /* Now try to preempt */
+                       if (dev->port->cad->preempt(dev->port->cad->private))
+                               return -EAGAIN;
+                       dev->port->ops->save_state(dev->port, dev->state);
+               } else
+                       return -EAGAIN;
+       }
+
+       /* Watch out for bad things */
+       if (dev->port->cad != pd1) {
+               printk(KERN_WARNING "%s: death while preempting %s\n",
+                      dev->port->name, dev->name);
+               if (dev->port->cad)
+                       return -EAGAIN;
+       }
+
+       /* Now we do the change of devices */
+       dev->port->cad = dev;
+
+       /* Swap the IRQ handlers. */
+       if (dev->port->irq >= 0) {
+               free_irq(dev->port->irq, dev->port);
+               request_irq(dev->port->irq, dev->irq_func ? dev->irq_func :
+                           parport_null_intr_func, SA_INTERRUPT, dev->name,
+                           dev->port);
+       }
+
+       /* Restore control registers */
+       dev->port->ops->restore_state(dev->port, dev->state);
+
+       return 0;
+}
+
+void parport_release(struct pardevice *dev)
+{
+       struct pardevice *pd1;
+
+       /* Make sure that dev is the current device */
+       if (dev->port->cad != dev) {
+               printk(KERN_WARNING "%s: %s tried to release parport when not owner\n", dev->port->name, dev->name);
+               return;
+       }
+       dev->port->cad = NULL;
+
+       /* Save control registers */
+       dev->port->ops->save_state(dev->port, dev->state);
+
+       /* Point IRQs somewhere harmless. */
+       if (dev->port->irq >= 0) {
+               free_irq(dev->port->irq, dev->port);
+               request_irq(dev->port->irq, parport_null_intr_func,
+                           SA_INTERRUPT, dev->port->name, dev->port);
+       }
+
+       /* Walk the list, offering a wakeup callback to everybody other
+        * than the lurker and the device that called us.
+        */
+       for (pd1 = dev->next; pd1; pd1 = pd1->next) {
+               if (!(pd1->flags & PARPORT_DEV_LURK)) {
+                       if (pd1->wakeup) {
+                               pd1->wakeup(pd1->private);
+                               if (dev->port->cad)
+                                       return;
+                       }
+               }
+       }
+
+       for (pd1 = dev->port->devices; pd1 && pd1 != dev; pd1 = pd1->next) {
+               if (!(pd1->flags & PARPORT_DEV_LURK)) {
+                       if (pd1->wakeup) {
+                               pd1->wakeup(pd1->private);
+                               if (dev->port->cad)
+                                       return;
+                       }
+               }
+       }
+
+       /* Now give the lurker a chance.
+        * There should be a wakeup callback because we checked for it
+        * at registration.
+        */
+       if (dev->port->lurker && (dev->port->lurker != dev)) {
+               if (dev->port->lurker->wakeup) {
+                       dev->port->lurker->wakeup(dev->port->lurker->private);
+               } 
+#ifdef PARPORT_PARANOID
+               else {  /* can't happen */
+                       printk(KERN_DEBUG
+                         "%s (%s): lurker's wakeup callback went away!\n",
+                              dev->port->name, dev->name);
+               }
+#endif
+       }
+}
index 8e8852bc03ea53e9db129e4e55298ec4c3b0a5c9..eb0117eb43d382d2b44a176323340d8dab70c82d 100644 (file)
@@ -1493,7 +1493,7 @@ eexp_set_multicast(struct device *dev)
 #endif
                 oj = jiffies;
                 while ((SCB_CUstat(scb_status(dev)) == 2) &&
-                       ((jiffies-oj) < 100));
+                       ((jiffies-oj) < 2000));
                if (SCB_CUstat(scb_status(dev)) == 2)
                        printk("%s: warning, CU didn't stop\n", dev->name);
                 lp->started &= ~(STARTED_CU);
diff --git a/drivers/pnp/BUGS-parport b/drivers/pnp/BUGS-parport
deleted file mode 100644 (file)
index 6b8420a..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-Currently known (or at least suspected) bugs in parport:
-
-o /proc/parport is untested under 2.0.XX
-
-o SCSI aborts for PPA under 2.0.29 [reported by jmr].  Under investigation.
-
-o make config (etc) allow you to select CONFIG_PNP_PARPORT=m, CONFIG_PPA=y -
-  the resulting kernel won't link.
-
-o IEEE1284 code does not do the terminating handshake after transfers, which
-  seems to upset some devices.
-
-o lp doesn't allow you to read status while printing is in progress.
index 44bb52d6843ae958ffba62680aee4d745c70f720..f8279fd67dcb4fb29c5732a03e289d75f2a394a6 100644 (file)
@@ -8,8 +8,8 @@ comment 'Plug and Play support'
 bool 'Plug and Play support' CONFIG_PNP
 
 if [ "$CONFIG_PNP" = "y" ]; then
-  if [ "$CONFIG_PNP_PARPORT" != "n" ]; then
-    bool '  Auto-probe for parallel devices' CONFIG_PNP_PARPORT_AUTOPROBE
+  if [ "$CONFIG_PARPORT" != "n" ]; then
+    dep_tristate '  Auto-probe for parallel devices' CONFIG_PNP_PARPORT $CONFIG_PARPORT
   fi
 fi
 
index 6153b8da657add9fcce23c33d9b26ba9127c752b..565062f7e91a6bcafe7a960c32dc9f644e10c6c7 100644 (file)
@@ -23,29 +23,11 @@ MI_OBJS  :=
 MIX_OBJS :=
 
 ifeq ($(CONFIG_PNP_PARPORT),y)
-  L_OBJS += parport_share.o
-  ifeq ($(CONFIG_PROC_FS),y)
-    L_OBJS += parport_procfs.o
-  endif
-  ifeq ($(CONFIG_PNP_PARPORT_AUTOPROBE),y)
-    L_OBJS += parport_probe.o
-  endif
-  LX_OBJS += parport_init.o
+  LX_OBJS += parport_probe.o
 else
   ifeq ($(CONFIG_PNP_PARPORT),m)
-    MI_OBJS += parport_share.o
-    ifneq ($(CONFIG_PROC_FS),n) 
-      MI_OBJS += parport_procfs.o
-    endif
-    ifeq ($(CONFIG_PNP_PARPORT_AUTOPROBE),y)
-      MI_OBJS += parport_probe.o
-    endif
-    MIX_OBJS += parport_init.o
-    M_OBJS += parport.o
+    MX_OBJS += parport_probe.o
   endif
 endif
 
 include $(TOPDIR)/Rules.make
-
-parport.o: $(MI_OBJS) $(MIX_OBJS)
-       $(LD) $(LD_RFLAG) -r -o $@ $(MI_OBJS) $(MIX_OBJS)
diff --git a/drivers/pnp/TODO-parport b/drivers/pnp/TODO-parport
deleted file mode 100644 (file)
index eea6a01..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-Things to be done.
-
-0. Fix the bugs (see BUGS-parport).
-
-1. Proper documentation.
-
-2. Overhaul lp.c:
-
-   a) It's a mess, and there is a lot of code duplication.
-
-   b) ECP support would be nice.  This can only work if both the port and
-      the printer support it.
-
-   c) Errors could do with being handled better.  There's no point logging a
-      message every 10 seconds when the printer is out of paper. 
-
-   d) Handle status readback automatically.  IEEE1284 printers can post status
-      bits when they have something to say.  We should read out and deal 
-      with (maybe just log) whatever the printer wants to tell the world.
-
-3. Assimilate more drivers.
diff --git a/drivers/pnp/parport_init.c b/drivers/pnp/parport_init.c
deleted file mode 100644 (file)
index 57574b6..0000000
+++ /dev/null
@@ -1,831 +0,0 @@
-/* $Id: parport_init.c,v 1.3.2.4 1997/04/16 21:20:44 phil Exp $
- * Parallel-port initialisation code.
- * 
- * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
- *          Tim Waugh <tmw20@cam.ac.uk>
- *         Jose Renau <renau@acm.org>
- *
- * based on work by Grant Guenther <grant@torque.net>
- *              and Philip Blundell <Philip.Blundell@pobox.com>
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/tasks.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#include <linux/parport.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/malloc.h>
-
-#include "parport_ll_io.h"
-
-static int io[PARPORT_MAX] = { 0, };
-static int irq[PARPORT_MAX] = { PARPORT_IRQ_NONE, };
-static int dma[PARPORT_MAX] = { PARPORT_DMA_NONE, };
-
-/******************************************************
- *  DMA detection section:
- */
-
-/*
- * Prepare DMA channels from 0-8 to transmit towards buffer
- */
-static int parport_prepare_dma(char *buff, int size)
-{
-       int tmp = 0;
-       int i,retv;
-       
-       for (i = 0; i < 8; i++) {
-               retv = request_dma(i, "probe");
-               if (retv)
-                       continue;
-               tmp |= 1 << i;
-
-               cli();
-               disable_dma(i);
-               clear_dma_ff(i);
-               set_dma_addr(i, virt_to_bus(buff));
-               set_dma_count(i, size);
-               set_dma_mode(i, DMA_MODE_READ);
-               sti();
-       }
-
-       return tmp;
-}
-
-/*
- * Activate all DMA channels passed in dma
- */
-static int parport_enable_dma(int dma)
-{
-       int i;
-       
-       for (i = 0; i < 8; i++)
-               if (dma & (1 << i)) {
-               cli();
-               enable_dma(i);
-               sti();
-           }
-
-       return dma;
-}
-
-static int parport_detect_dma_transfer(int dma,int size,int *resid)
-{
-       int i,n,retv;
-       int count=0;
-
-       retv = -1;
-       for (i = 0; i < 8; i++)
-               if (dma & (1 << i)) {
-                       disable_dma(i);
-                       clear_dma_ff(i);
-                       n = get_dma_residue(i);
-                       if (n != size) {
-                               *resid = n;
-                               retv = i;
-                               if (count > 0) {
-                                       retv = -1;      /* Multiple DMA's */
-                                       printk(KERN_ERR "parport: multiple DMA detected.  Huh?\n");
-                               }
-                               count++;
-                       }
-                       free_dma(i);
-               }
-
-       return retv;    
-}
-
-/* Only if supports ECP mode */
-static int programmable_dma_support(struct parport *pb)
-{
-       int dma;
-
-       w_ecr(pb,0xE0); /* Configuration MODE */
-       
-       dma = r_cnfgB(pb) & 0x07;
-
-       w_ecr(pb,pb->ecr);
-       
-       if( dma == 0 || dma == 4 ) /* Jumper selection */
-               return -1;
-       else
-               return dma;
-}
-
-/* Only called if port supports ECP mode.
- *
- * The only restriction on DMA channels is that it has to be
- * between 0 to 7 (inclusive). Used only in an ECP mode, DMAs are
- * considered a shared resource and hence they should be registered
- * when needed and then immediately unregistered.
- *
- * DMA autoprobes for ECP mode are known not to work for some
- * main board BIOS configs. I had to remove everything from the
- * port, set the mode to SPP, reboot to DOS, set the mode to ECP,
- * and reboot again, then I got IRQ probes and DMA probes to work.
- * [Is the BIOS doing a device detection?]
- *
- * A value of -1 is allowed indicating no DMA support.
- *
- * if( 0 < DMA < 4 )
- *    1Byte DMA transfer
- * else // 4 < DMA < 8
- *    2Byte DMA transfer
- *
- */
-static int parport_dma_probe(struct parport *pb)
-{
-       int dma,retv;
-       int dsr,dsr_read;
-       char *buff;
-
-       retv = programmable_dma_support(pb);
-       if (retv != -1)
-               return retv;
-       
-       if (!(buff = kmalloc(2048, GFP_KERNEL | GFP_DMA))) {
-           printk(KERN_ERR "parport: memory squeeze\n");
-           return -1;
-       }
-       
-       dsr = r_ctr(pb);
-       dsr_read = (dsr & ~(0x20)) | 0x04;    /* Direction == read */
-
-       w_ecr(pb, 0xc0);           /* ECP MODE */
-       w_ctr(pb, dsr_read );
-       dma=parport_prepare_dma(buff,1000);
-       w_ecr(pb, 0xd8);           /* ECP FIFO + enable DMA */
-       parport_enable_dma(dma);
-       udelay(500);           /* Give some for DMA tranfer */
-       retv = parport_detect_dma_transfer(dma,1000,&pb->speed);
-       pb->speed = pb->speed * 2000;  /* 500uSec * 2000 = 1sec */
-       
-       /*
-        * National Semiconductors only supports DMA tranfers
-        * in ECP MODE
-        */
-       if (retv == -1) {
-               w_ecr(pb, 0x60);           /* ECP MODE */
-               w_ctr(pb, dsr_read );
-               dma=parport_prepare_dma(buff,1000);
-               w_ecr(pb, 0x68);           /* ECP FIFO + enable DMA */
-               parport_enable_dma(dma);
-               udelay(500);           /* Give some for DMA tranfer */
-               retv = parport_detect_dma_transfer(dma,1000,&pb->speed);
-               pb->speed = pb->speed * 2000;  /* 500uSec * 2000 = 1sec */
-       }
-       
-       kfree(buff);
-       
-       w_ctr(pb, pb->ctr);
-       w_ecr(pb, pb->ecr);
-
-       return retv;
-}
-/******************************************************
- *  MODE detection section:
- */
-
-/*
- * Clear TIMEOUT BIT in EPP MODE
- */
-static int epp_clear_timeout(struct parport *pb)
-{
-       int r;
-
-       if (!(r_str(pb) & 0x01))
-               return 1;
-
-       /* To clear timeout some chips require double read */
-       r_str(pb);
-       r = r_str(pb);
-       w_str(pb, r | 0x01); /* Some reset by writing 1 */
-       w_str(pb, r & 0xfe); /* Others by writing 0 */
-       r = r_str(pb);
-
-       return !(r & 0x01);
-}
-
-
-/*
- * Checks for port existence, all ports support SPP MODE
- */
-static int parport_SPP_supported(struct parport *pb)
-{
-       /* Do a simple read-write test to make sure the port exists. */
-
-       w_dtr(pb, 0xaa);
-       if (r_dtr(pb) != 0xaa) return 0;
-       
-       w_dtr(pb, 0x55);
-       if (r_dtr(pb) != 0x55) return 0;
-
-       return PARPORT_MODE_SPP;
-}
-
-/* Check for ECP
- *
- * Old style XT ports alias io ports every 0x400, hence accessing ECR
- * on these cards actually accesses the CTR.
- *
- * Modern cards don't do this but reading from ECR will return 0xff
- * regardless of what is written here if the card does NOT support
- * ECP.
- *
- * We will write 0x2c to ECR and 0xcc to CTR since both of these
- * values are "safe" on the CTR since bits 6-7 of CTR are unused.
- */
-static int parport_ECR_present(struct parport *pb)
-{
-       int r;
-
-       r= r_ctr(pb);   
-       if ((r_ecr(pb) & 0x03) == (r & 0x03)) {
-               w_ctr(pb, r ^ 0x03 ); /* Toggle bits 0-1 */
-
-               r= r_ctr(pb);   
-               if ((r_ecr(pb) & 0x03) == (r & 0x03))
-                       return 0; /* Sure that no ECR register exists */
-       }
-       
-       if ((r_ecr(pb) & 0x03 ) != 0x01)
-               return 0;
-
-       w_ecr(pb,0x34);
-       if (r_ecr(pb) != 0x35)
-               return 0;
-
-       w_ecr(pb,pb->ecr);
-       w_ctr(pb,pb->ctr);
-       
-       return PARPORT_MODE_ECR;
-}
-
-static int parport_ECP_supported(struct parport *pb)
-{
-       int i;
-       
-       /* If there is no ECR, we have no hope of supporting ECP. */
-       if (!(pb->modes & PARPORT_MODE_ECR))
-               return 0;
-
-       /*
-        * Using LGS chipset it uses ECR register, but
-        * it doesn't support ECP or FIFO MODE
-        */
-       
-       w_ecr(pb, 0xc0); /* TEST FIFO */
-       for (i=0; i < 1024 && (r_ecr(pb) & 0x01); i++)
-               w_fifo(pb, 0xaa);
-
-       w_ecr(pb, pb->ecr);
-
-       if (i == 1024)
-               return 0;
-       
-       return PARPORT_MODE_ECP;
-}
-
-/* EPP mode detection
- * Theory:
- *     Bit 0 of STR is the EPP timeout bit, this bit is 0
- *     when EPP is possible and is set high when an EPP timeout
- *     occurs (EPP uses the HALT line to stop the CPU while it does
- *     the byte transfer, an EPP timeout occurs if the attached
- *     device fails to respond after 10 micro seconds).
- *
- *     This bit is cleared by either reading it (National Semi)
- *     or writing a 1 to the bit (SMC, UMC, WinBond), others ???
- *     This bit is always high in non EPP modes.
- */
-static int parport_EPP_supported(struct parport *pb)
-{
-       /* If EPP timeout bit clear then EPP available */
-       if (!epp_clear_timeout(pb))
-               return 0;  /* No way to clear timeout */
-
-       w_ctr(pb, r_ctr(pb) | 0x20);
-       w_ctr(pb, r_ctr(pb) | 0x10);
-       epp_clear_timeout(pb);
-       
-       r_epp(pb);
-       udelay(30);  /* Wait for possible EPP timeout */
-       
-       if (r_str(pb) & 0x01) {
-               epp_clear_timeout(pb);
-               return PARPORT_MODE_EPP;
-       }
-
-       return 0;
-}
-
-static int parport_ECPEPP_supported(struct parport *pb)
-{
-       int mode;
-
-       if (!(pb->modes & PARPORT_MODE_ECR))
-               return 0;
-       
-       /* Search for SMC style EPP+ECP mode */
-       w_ecr(pb, 0x80);
-       
-       mode = parport_EPP_supported(pb);
-
-       w_ecr(pb, pb->ecr);
-       
-       if (mode)
-               return PARPORT_MODE_ECPEPP;
-       
-       return 0;
-}
-
-/* Detect PS/2 support.
- *
- * Bit 5 (0x20) sets the PS/2 data direction; setting this high
- * allows us to read data from the data lines.  In theory we would get back
- * 0xff but any peripheral attached to the port may drag some or all of the
- * lines down to zero.  So if we get back anything that isn't the contents
- * of the data register we deem PS/2 support to be present. 
- *
- * Some SPP ports have "half PS/2" ability - you can't turn off the line
- * drivers, but an external peripheral with sufficiently beefy drivers of
- * its own can overpower them and assert its own levels onto the bus, from
- * where they can then be read back as normal.  Ports with this property
- * and the right type of device attached are likely to fail the SPP test,
- * (as they will appear to have stuck bits) and so the fact that they might
- * be misdetected here is rather academic. 
- */
-
-static int parport_PS2_supported(struct parport *pb)
-{
-       int ok = 0;
-  
-       epp_clear_timeout(pb);
-
-       w_ctr(pb, pb->ctr | 0x20);   /* try to tri-state the buffer */
-       
-       w_dtr(pb, 0x55);
-       if (r_dtr(pb) != 0x55) ok++;
-
-       w_dtr(pb, 0xaa);
-       if (r_dtr(pb) != 0xaa) ok++;
-       
-       w_ctr(pb, pb->ctr);          /* cancel input mode */
-
-       return ok?PARPORT_MODE_PS2:0;
-}
-
-static int parport_ECPPS2_supported(struct parport *pb)
-{
-       int mode;
-
-       if (!(pb->modes & PARPORT_MODE_ECR))
-               return 0;
-       
-       w_ecr(pb, 0x20);
-       
-       mode = parport_PS2_supported(pb);
-
-       w_ecr(pb,pb->ecr);
-       
-       if (mode)
-               return PARPORT_MODE_ECPPS2;
-       
-       return 0;
-}
-
-/******************************************************
- *  IRQ detection section:
- *
- * This code is for detecting ECP interrupts (due to problems with the
- * monolithic interrupt probing routines).
- *
- * In short this is a voting system where the interrupt with the most
- * "votes" is the elected interrupt (it SHOULD work...)
- *
- * This is horribly x86-specific at the moment.  I'm not convinced it
- * belongs at all.
- */
-
-static int intr_vote[16];
-
-static void parport_vote_intr_func(int irq, void *dev_id, struct pt_regs *regs)
-{
-       intr_vote[irq]++;
-       return;
-}
-
-static long open_intr_election(void)
-{
-       long tmp = 0;
-       int i;
-
-       /* We ignore the timer - irq 0 */
-       for (i = 1; i < 16; i++) {
-               intr_vote[i] = 0;
-               if (request_irq(i, parport_vote_intr_func,
-                      SA_INTERRUPT, "probe", intr_vote) == 0)
-                       tmp |= 1 << i;
-       }
-       return tmp;
-}
-
-static int close_intr_election(long tmp)
-{
-       int irq = PARPORT_IRQ_NONE;
-       int i;
-
-       /* We ignore the timer - irq 0 */
-       for (i = 1; i < 16; i++)
-               if (tmp & (1 << i)) {
-                       if (intr_vote[i]) {
-                               if (irq != PARPORT_IRQ_NONE)
-                                       /* More than one interrupt */
-                                       return PARPORT_IRQ_NONE;
-                               irq = i;
-                       }
-                       free_irq(i, intr_vote);
-               }
-       return irq;
-}
-
-/* Only if supports ECP mode */
-static int programmable_irq_support(struct parport *pb)
-{
-       int irq;
-
-       w_ecr(pb,0xE0); /* Configuration MODE */
-       
-       irq = (r_cnfgB(pb) >> 3) & 0x07;
-
-       switch(irq){
-         case 2:
-                 irq = 9;
-                 break;
-         case 7:
-                 irq = 5;
-                 break;
-         case 0:
-                 irq = -1;
-                 break;
-         default:
-                 irq += 7;
-       }
-                 
-       w_ecr(pb,pb->ecr);
-       
-       return irq;
-}
-
-static int irq_probe_ECP(struct parport *pb)
-{
-       int irqs,i;
-               
-       probe_irq_off(probe_irq_on());  /* Clear any interrupts */
-       irqs = open_intr_election();
-               
-       w_ecr(pb, 0x00);            /* Reset FIFO */
-       w_ctr(pb, pb->ctr );        /* Force direction = 0 */
-       w_ecr(pb, 0xd0);            /* TEST FIFO + nErrIntrEn */
-
-       /* If Full FIFO sure that WriteIntrThresold is generated */
-       for( i=0 ; i < 1024 && !(r_ecr(pb) & 0x02) ; i++ ){
-               w_fifo(pb, 0xaa);
-       }
-               
-       pb->irq = close_intr_election(irqs);
-       
-       w_ecr(pb, pb->ecr);
-
-       return pb->irq;
-}
-
-/*
- * This detection seems that only works in National Semiconductors
- * This doesn't work in SMC, LGS, and Winbond 
- */
-static int irq_probe_EPP(struct parport *pb)
-{
-       int irqs;
-
-#ifndef ADVANCED_DETECT
-       return -1;
-#endif
-       
-       probe_irq_off(probe_irq_on());  /* Clear any interrupts */
-       irqs = open_intr_election();
-
-       if( pb->modes & PARPORT_MODE_ECR )
-               w_ecr(pb, r_ecr(pb) | 0x10 );
-       
-       epp_clear_timeout(pb);
-       w_ctr(pb, r_ctr(pb) | 0x20);
-       w_ctr(pb, r_ctr(pb) | 0x10);
-       epp_clear_timeout(pb);
-
-       /*  Device isn't expecting an EPP read
-        * and generates an IRQ.
-        */
-       r_epp(pb);
-       udelay(20);
-
-       pb->irq = close_intr_election(irqs);
-       
-       w_ctr(pb,pb->ctr);
-       
-       return pb->irq;
-}
-
-static int irq_probe_SPP(struct parport *pb)
-{
-       int irqs;
-
-#ifndef ADVANCED_DETECT
-       return -1;
-#endif
-
-       probe_irq_off(probe_irq_on());  /* Clear any interrupts */
-       irqs = probe_irq_on();
-
-       if( pb->modes & PARPORT_MODE_ECR )
-               w_ecr(pb, 0x10 );
-
-       w_dtr(pb,0x00);
-       w_ctr(pb,0x00);
-       w_ctr(pb,0x0c);
-       udelay(5);
-       w_ctr(pb,0x0d);
-       udelay(5);
-       w_ctr(pb,0x0c);
-       udelay(25);
-       w_ctr(pb,0x08);
-       udelay(25);
-       w_ctr(pb,0x0c);
-       udelay(50);
-
-       pb->irq = probe_irq_off(irqs);
-       if (pb->irq <= 0)
-               pb->irq = PARPORT_IRQ_NONE;     /* No interrupt detected */
-       
-       w_ctr(pb,pb->ctr);
-       
-       return pb->irq;
-}
-
-/* We will attempt to share interrupt requests since other devices
- * such as sound cards and network cards seem to like using the
- * printer IRQs.
- *
- * When ECP is available we can autoprobe for IRQs.
- * NOTE: If we can autoprobe it, we can register the IRQ.
- */
-static int parport_irq_probe(struct parport *pb)
-{
-       if (pb->modes & PARPORT_MODE_ECR)
-               pb->irq = programmable_irq_support(pb);
-
-       if (pb->modes & PARPORT_MODE_ECP)
-               pb->irq = irq_probe_ECP(pb);
-                       
-       if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_ECPEPP)) {
-               w_ecr(pb,0x80);
-               pb->irq = irq_probe_EPP(pb);
-               w_ecr(pb,pb->ecr);
-       }
-
-       epp_clear_timeout(pb);
-
-       if (pb->irq == PARPORT_IRQ_NONE && (pb->modes & PARPORT_MODE_EPP))
-               pb->irq = irq_probe_EPP(pb);
-
-       epp_clear_timeout(pb);
-
-       if (pb->irq == PARPORT_IRQ_NONE)
-               pb->irq = irq_probe_SPP(pb);
-
-       return pb->irq;
-}
-
-
-int initialize_parport(struct parport *pb, unsigned long base, int irq, int dma, int count)
-{
-       /* Check some parameters */
-       if (dma < -2) {
-               printk(KERN_ERR "parport: Invalid DMA[%d] at base 0x%lx\n",dma,base);
-               return 0;
-       }
-
-       if (irq < -2) {
-               printk(KERN_ERR "parport: Invalid IRQ[%d] at base 0x%lx\n",irq,base);
-               return 0;
-       }
-       
-       /* Init our structure */
-       memset(pb, 0, sizeof(struct parport));
-       pb->base = base;
-       pb->irq = irq;
-       pb->dma = dma;
-       pb->modes = 0;
-       pb->next = NULL;
-       pb->devices = pb->cad = pb->lurker = NULL;
-       pb->flags = 0;
-
-       /* Before we start, set the control registers to something sensible. */
-       pb->ecr = 0xc;
-       pb->ctr = 0xc;
-
-       pb->name = kmalloc(15, GFP_KERNEL);
-       if (!pb->name) {
-               printk(KERN_ERR "parport: memory squeeze\n");
-               return 0;
-       }
-       sprintf(pb->name, "parport%d", count);
-
-       if (!parport_SPP_supported(pb)) {
-               epp_clear_timeout(pb);
-               if (!parport_SPP_supported(pb)) {
-                       kfree(pb->name);
-                       return 0;
-               }
-       }
-
-       pb->modes |= PARPORT_MODE_SPP;  /* All ports support SPP mode. */
-       pb->modes |= parport_PS2_supported(pb);
-
-       if (pb->base != 0x3bc) {
-               pb->modes |= parport_ECR_present(pb);   
-               pb->modes |= parport_ECP_supported(pb);
-               pb->modes |= parport_ECPPS2_supported(pb);
-               pb->modes |= parport_EPP_supported(pb);
-               pb->modes |= parport_ECPEPP_supported(pb);
-       }
-
-       /* Now register regions */
-       if ((pb->modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) && 
-           (check_region(pb->base, 8))) {
-               printk(KERN_INFO "%s: EPP disabled due to port conflict at %x.\n", pb->name, pb->base + 3);
-               pb->modes &= ~(PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP);
-       }
-       pb->size = (pb->modes & (PARPORT_MODE_EPP | PARPORT_MODE_ECPEPP)) ? 8 : 3;
-
-       request_region(pb->base, pb->size, pb->name);
-       if (pb->modes & PARPORT_MODE_ECR)
-               request_region(pb->base+0x400, 3, pb->name);
-
-       /* DMA check */
-       if (pb->modes & PARPORT_MODE_ECP) {
-               if (pb->dma == PARPORT_DMA_NONE)
-                       pb->dma = parport_dma_probe(pb);
-               else if (pb->dma == -2)
-                       pb->dma = PARPORT_DMA_NONE;
-       }
-
-       /* IRQ check */
-       if (pb->irq == PARPORT_IRQ_NONE)
-               pb->irq = parport_irq_probe(pb);
-       else if (pb->irq == -2)
-               pb->irq = PARPORT_IRQ_NONE;
-
-       return 1;
-}
-
-#ifndef MODULE
-static int parport_setup_ptr = 0;
-
-void parport_setup(char *str, int *ints)
-{
-       if (ints[0] == 0 || ints[1] == 0) {
-               /* Disable parport if "parport=" or "parport=0" in cmdline */
-               io[0] = PARPORT_DISABLE; 
-               return;
-       }
-       if (parport_setup_ptr < PARPORT_MAX) {
-               io[parport_setup_ptr] = ints[1];
-               if (ints[0]>1) {
-                       irq[parport_setup_ptr] = ints[2];
-                       if (ints[0]>2) dma[parport_setup_ptr] = ints[3];
-               }
-               parport_setup_ptr++;
-       } else {
-               printk(KERN_ERR "parport=0x%x", ints[1]);
-               if (ints[0]>1) {
-                       printk(",%d", ints[2]);
-                       if (ints[0]>2) printk(",%d", ints[3]);
-               }
-               printk(" ignored, too many ports.\n");
-       }
-}
-#endif
-
-#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
-extern void parport_probe_one(struct parport *port);
-#endif
-
-#ifdef MODULE
-MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_MAX) "i");
-MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_MAX) "i");
-MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_MAX) "i");
-
-int init_module(void)
-#else
-int pnp_parport_init(void)
-#endif                         /* MODULE */
-{
-       struct parport *pb;
-
-       printk(KERN_INFO "Parallel port sharing: %s\n",
-              "$Revision: 1.3.2.4 $");
-
-       if (io[0] == PARPORT_DISABLE) return 1; 
-
-#ifdef CONFIG_PROC_FS
-       parport_proc_init();
-#endif
-
-       /* Run probes to ensure parport does exist */
-#define PORT(a,b,c) \
-               if ((pb = parport_register_port((a), (b), (c))))  \
-                       parport_destroy(pb); 
-
-
-       if (io[0]) {
-               /* If the user specified any ports, use them */
-               int i;
-               for (i = 0; io[i] && i < PARPORT_MAX; i++) {
-                       PORT(io[i], irq[i], dma[i]);
-               }
-       } else {
-               /* Go for the standard ports. */
-               PORT(0x378, PARPORT_IRQ_NONE, PARPORT_DMA_NONE);
-               PORT(0x278, PARPORT_IRQ_NONE, PARPORT_DMA_NONE);
-               PORT(0x3bc, PARPORT_IRQ_NONE, PARPORT_DMA_NONE);
-#undef PORT
-       }
-
-#if defined(CONFIG_PNP_PARPORT_AUTOPROBE) || defined(CONFIG_PROC_FS)
-       for (pb = parport_enumerate(); pb; pb = pb->next) {
-#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
-               parport_probe_one(pb);
-#endif
-#ifdef CONFIG_PROC_FS
-               parport_proc_register(pb);
-#endif
-       }
-#endif
-
-       return 0;
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{
-       struct parport *port, *next;
-   
-       for (port = parport_enumerate(); port; port = next) {
-               next = port->next;
-               parport_destroy(port);
-               parport_proc_unregister(port);
-               kfree(port->name);
-               kfree(port);
-       }
-       
-       parport_proc_cleanup();
-}
-#endif
-
-/* Exported symbols for modules. */
-
-EXPORT_SYMBOL(parport_claim);
-EXPORT_SYMBOL(parport_release);
-EXPORT_SYMBOL(parport_register_port);
-EXPORT_SYMBOL(parport_destroy);
-EXPORT_SYMBOL(parport_register_device);
-EXPORT_SYMBOL(parport_unregister_device);
-EXPORT_SYMBOL(parport_enumerate);
-EXPORT_SYMBOL(parport_ieee1284_nibble_mode_ok);
-
-#ifdef CONFIG_PNP_PARPORT_AUTOPROBE
-EXPORT_SYMBOL(parport_probe);
-EXPORT_SYMBOL(parport_probe_one);
-#endif
-
-void inc_parport_count(void)
-{
-#ifdef MODULE
-       MOD_INC_USE_COUNT;
-#endif
-}
-
-void dec_parport_count(void)
-{
-#ifdef MODULE
-       MOD_DEC_USE_COUNT;
-#endif
-}
diff --git a/drivers/pnp/parport_ll_io.h b/drivers/pnp/parport_ll_io.h
deleted file mode 100644 (file)
index c2592af..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/* $Id: parport_ll_io.h,v 1.1.2.1 1997/03/26 13:01:09 phil Exp $ 
- * David Campbell's "favourite IO routines" for parallel ports
- */
-
-#define r_dtr(x)       inb((x)->base)
-#define r_str(x)       inb((x)->base+1)
-#define r_ctr(x)       inb((x)->base+2)
-#define r_epp(x)       inb((x)->base+4)
-#define r_fifo(x)      inb((x)->base+0x400)
-#define r_ecr(x)       inb((x)->base+0x402)
-#define r_cnfgA(x)     inb((x)->base+0x400)
-#define r_cnfgB(x)     inb((x)->base+0x401)
-
-#define w_dtr(x,y)     outb((y), (x)->base)
-#define w_str(x,y)     outb((y), (x)->base+1)
-#define w_ctr(x,y)     outb((y), (x)->base+2)
-#define w_epp(x,y)     outb((y), (x)->base+4)
-#define w_fifo(x,y)    outb((y), (x)->base+0x400)
-#define w_ecr(x,y)     outb((y), (x)->base+0x402)
-#define w_cnfgA(x,y)   outb((y), (x)->base+0x400)
-#define w_cnfgB(x,y)   outb((y), (x)->base+0x401)
index b401644966cd710c25ddccf68be13400cd7b82a9..8e841b8e5138a3b52ada46574ab96a3217df951a 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/malloc.h>
 #include <linux/ctype.h>
+#include <linux/module.h>
 
 #include <linux/lp.h>
 
 static inline int read_nibble(struct parport *port) 
 {
        unsigned char i;
-       i = parport_r_status(port)>>3;
+       i = parport_read_status(port)>>3;
        i&=~8;
        if ( ( i & 0x10) == 0) i|=8;
        return(i & 0x0f);
 }
 
 static void read_terminate(struct parport *port) {
-       parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+       parport_write_control(port, (parport_read_control(port) & ~2) | 8);
        /* SelectIN high, AutoFeed low */
        if (parport_wait_peripheral(port, 0x80, 0)) 
                /* timeout, SelectIN high, Autofeed low */
                return;
-       parport_w_ctrl(port, parport_r_ctrl(port) | 2);
+       parport_write_control(port, parport_read_control(port) | 2);
        /* AutoFeed high */
        parport_wait_peripheral(port, 0x80, 0x80);
        /* no timeout possible, Autofeed low, SelectIN high */
-       parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
+       parport_write_control(port, (parport_read_control(port) & ~2) | 8);
        return;
 }
 
@@ -56,14 +57,14 @@ static long read_polled(struct parport *port, char *buf,
        unsigned char Byte=0;
 
        for (i=0; ; i++) {
-               parport_w_ctrl(port, parport_r_ctrl(port) | 2); /* AutoFeed high */
+               parport_write_control(port, parport_read_control(port) | 2); /* AutoFeed high */
                if (parport_wait_peripheral(port, 0x40, 0)) {
                        printk("%s: read1 timeout.\n", port->name);
-                       parport_w_ctrl(port, parport_r_ctrl(port) & ~2);
+                       parport_write_control(port, parport_read_control(port) & ~2);
                        break;
                }
                z = read_nibble(port);
-               parport_w_ctrl(port, parport_r_ctrl(port) & ~2); /* AutoFeed low */
+               parport_write_control(port, parport_read_control(port) & ~2); /* AutoFeed low */
                if (parport_wait_peripheral(port, 0x40, 0x40)) {
                        printk("%s: read2 timeout.\n", port->name);
                        break;
@@ -75,7 +76,7 @@ static long read_polled(struct parport *port, char *buf,
                        if (count++ == length)
                                temp = NULL;
                        /* Does the error line indicate end of data? */
-                       if ((parport_r_status(port) & LP_PERRORP) == LP_PERRORP) 
+                       if ((parport_read_status(port) & LP_PERRORP) == LP_PERRORP) 
                                break;
                } else Byte=z;
        }
@@ -87,7 +88,7 @@ static struct wait_queue *wait_q = NULL;
 
 static int wakeup(void *ref)
 {
-       struct ppd **dev = (struct ppd **)ref;
+       struct pardevice **dev = (struct pardevice **)ref;
        
        if (!wait_q || parport_claim(*dev))
                return 1;
@@ -98,9 +99,7 @@ static int wakeup(void *ref)
 
 int parport_probe(struct parport *port, char *buffer, int len)
 {
-       struct ppd *dev = parport_register_device(port, "IEEE 1284 probe",
-                                                 NULL, wakeup, NULL,
-                                                 PARPORT_DEV_TRAN, &dev);
+       struct pardevice *dev = parport_register_device(port, "IEEE 1284 probe", NULL, wakeup, NULL, PARPORT_DEV_TRAN, &dev);
 
        int result = 0;
 
@@ -264,3 +263,17 @@ void parport_probe_one(struct parport *port)
        }
        kfree(buffer);
 }
+
+#if MODULE
+int init_module(void)
+{
+       struct parport *p;
+       for (p = parport_enumerate(); p; p = p->next) 
+               parport_probe_one(p);
+       return 0;
+}
+
+void cleanup_module(void)
+{
+}
+#endif
diff --git a/drivers/pnp/parport_procfs.c b/drivers/pnp/parport_procfs.c
deleted file mode 100644 (file)
index 46ff5b5..0000000
+++ /dev/null
@@ -1,339 +0,0 @@
-/* $Id: parport_procfs.c,v 1.3.2.6 1997/04/16 21:30:38 phil Exp $
- * Parallel port /proc interface code.
- * 
- * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
- *          Tim Waugh <tmw20@cam.ac.uk>
- *
- * based on work by Grant Guenther <grant@torque.net>
- *              and Philip Blundell <Philip.Blundell@pobox.com>
- */
-
-#include <linux/tasks.h>
-#include <asm/ptrace.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/malloc.h>
-#include <linux/interrupt.h>
-
-#include <linux/proc_fs.h>
-
-#include <linux/parport.h>
-#include "parport_ll_io.h"
-
-#undef PARPORT_INCLUDE_BENCH
-
-struct proc_dir_entry *base=NULL;
-
-void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs);
-
-static int irq_write_proc(struct file *file, const char *buffer,
-                                         unsigned long count, void *data)
-{
-       int newirq;
-       struct parport *pp = (struct parport *)data;
-       
-       if (count > 4 )  /* more than 4 digits for a irq 0x?? 0?? ??  */
-               return(-EOVERFLOW);
-
-       if (buffer[0] < 32 || !strncmp(buffer, "none", 4)) {
-               newirq = PARPORT_IRQ_NONE;
-       } else {
-               if (buffer[0] == '0') {
-                       if( buffer[1] == 'x' )
-                               newirq = simple_strtoul(&buffer[2],0,16);
-                       else
-                               newirq = simple_strtoul(&buffer[1],0,8);
-               } else {
-                       newirq = simple_strtoul(buffer,0,10);
-               }
-       }
-
-       if (pp->irq != PARPORT_IRQ_NONE && !(pp->flags & PARPORT_FLAG_COMA)) 
-               free_irq(pp->irq, pp);
-
-       pp->irq = newirq;
-
-       if (pp->irq != PARPORT_IRQ_NONE && !(pp->flags & PARPORT_FLAG_COMA)) { 
-               struct ppd *pd = pp->cad;
-
-               if (pd == NULL) {
-                       pd = pp->devices;
-                       if (pd != NULL) 
-                               request_irq(pp->irq, pd->irq_func ? 
-                                           pd->irq_func :
-                                           parport_null_intr_func,
-                                           SA_INTERRUPT, pd->name, pd->port);
-               } else {
-                       request_irq(pp->irq, pd->irq_func ? pd->irq_func :
-                                   parport_null_intr_func,
-                                   SA_INTERRUPT, pp->name, pd->port);
-               }
-       }
-
-       return count;
-}
-
-static int irq_read_proc(char *page, char **start, off_t off,
-                                        int count, int *eof, void *data)
-{
-       struct parport *pp = (struct parport *)data;
-       int len;
-       
-       if (pp->irq == PARPORT_IRQ_NONE)
-               len = sprintf(page, "none\n");
-       else
-               len = sprintf(page, "%d\n", pp->irq);
-       
-       *start = 0;
-       *eof   = 1;
-       return len;
-}
-
-static int devices_read_proc(char *page, char **start, off_t off,
-                                        int count, int *eof, void *data)
-{
-       struct parport *pp = (struct parport *)data;
-       struct ppd *pd1;
-       int len=0;
-
-       for (pd1 = pp->devices; pd1 ; pd1 = pd1->next) {
-               if (pd1 == pp->cad)
-                       len += sprintf(page+len, "+");
-               else
-                       len += sprintf(page+len, " ");
-
-               len += sprintf(page+len, "%s",pd1->name);
-
-               if (pd1 == pp->lurker)
-                       len += sprintf(page+len, " LURK");
-               
-               len += sprintf(page+len,"\n");
-       }
-               
-       *start = 0;
-       *eof   = 1;
-       return len;
-}
-
-static int hardware_read_proc(char *page, char **start, off_t off,
-                                                         int count, int *eof, void *data)
-{
-       struct parport *pp = (struct parport *)data;
-       int len=0;
-       
-       len += sprintf(page+len, "base:\t0x%x\n",pp->base);
-       if (pp->irq == PARPORT_IRQ_NONE)
-               len += sprintf(page+len, "irq:\tnone\n");
-       else
-               len += sprintf(page+len, "irq:\t%d\n",pp->irq);
-       len += sprintf(page+len, "dma:\t%d\n",pp->dma);
-
-       len += sprintf(page+len, "modes:\t");
-       {
-#define printmode(x) {if(pp->modes&PARPORT_MODE_##x){len+=sprintf(page+len,"%s%s",f?",":"",#x);f++;}}
-               int f = 0;
-               printmode(SPP);
-               printmode(PS2);
-               printmode(EPP);
-               printmode(ECP);
-               printmode(ECPEPP);
-               printmode(ECPPS2);
-#undef printmode
-       }
-       len += sprintf(page+len, "\n");
-
-       len += sprintf(page+len, "mode:\t");
-       if (pp->modes & PARPORT_MODE_ECR) {
-               switch (r_ecr(pp) >> 5) {
-               case 0:
-                       len += sprintf(page+len, "SPP");
-                       if( pp->modes & PARPORT_MODE_PS2 )
-                               len += sprintf(page+len, ",PS2");
-                       if( pp->modes & PARPORT_MODE_EPP )
-                               len += sprintf(page+len, ",EPP");
-                       break;
-               case 1:
-                       len += sprintf(page+len, "ECPPS2");
-                       break;
-               case 2:
-                       len += sprintf(page+len, "DATAFIFO");
-                       break;
-               case 3:
-                       len += sprintf(page+len, "ECP");
-                       break;
-               case 4:
-                       len += sprintf(page+len, "ECPEPP");
-                       break;
-               case 5:
-                       len += sprintf(page+len, "Reserved?");
-                       break;
-               case 6:
-                       len += sprintf(page+len, "TEST");
-                       break;
-               case 7:
-                       len += sprintf(page+len, "Configuration");
-                       break;
-               }
-       } else {
-               len += sprintf(page+len, "SPP");
-               if (pp->modes & PARPORT_MODE_PS2)
-                       len += sprintf(page+len, ",PS2");
-               if (pp->modes & PARPORT_MODE_EPP)
-                       len += sprintf(page+len, ",EPP");
-       }
-       len += sprintf(page+len, "\n");
-       
-#if 0
-       /* Now no detection, please fix with an external function */
-       len += sprintf(page+len, "chipset:\tunknown\n");
-#endif
-#ifdef PARPORT_INCLUDE_BENCHMARK
-       if (pp->speed)
-               len += sprintf(page+len, "bench:\t%d Bytes/s\n",pp->speed);
-       else
-               len += sprintf(page+len, "bench:\tunknown\n");
-#endif 
-
-       *start = 0;
-       *eof   = 1;
-       return len;
-}
-
-static struct proc_dir_entry *new_proc_entry(const char *name, mode_t mode,
-                                            struct proc_dir_entry *parent,
-                                            unsigned short ino)
-{
-       struct proc_dir_entry *ent;
-
-       ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
-       if (!ent)
-               return NULL;
-       memset(ent, 0, sizeof(struct proc_dir_entry));
-       
-       if (mode == S_IFDIR)
-               mode |= S_IRUGO | S_IXUGO;
-       else if (mode == 0)
-               mode = S_IFREG | S_IRUGO;
-
-
-       ent->low_ino = ino;
-       ent->name = name;
-       ent->namelen = strlen(name);
-       ent->mode = mode;
-       if (S_ISDIR(mode)) 
-               ent->nlink = 2;
-       else
-               ent->nlink = 1;
-
-       proc_register(parent, ent);
-       
-       return ent;
-}
-
-
-int parport_proc_init()
-{
-       base = new_proc_entry("parport", S_IFDIR, &proc_root,PROC_PARPORT);
-
-       if (base)
-               return 1;
-       else {
-               printk(KERN_ERR "parport: Error creating proc entry /proc/parport\n");
-               return 0;
-       }
-}
-
-int parport_proc_cleanup()
-{
-       if (base)
-               proc_unregister(&proc_root,base->low_ino);
-
-       base = NULL;
-       
-       return 0;
-}
-
-int parport_proc_register(struct parport *pp)
-{
-       struct proc_dir_entry *ent;
-       static int conta=0;
-       char *name;
-
-       memset(&pp->pdir,0,sizeof(struct parport_dir));
-
-       if (!base) {
-               printk(KERN_ERR "parport: Error entry /proc/parport, not generated?\n");
-               return 1;
-       }
-       
-       name = pp->pdir.name;
-       sprintf(name,"%d",conta++);
-
-       ent = new_proc_entry(name, S_IFDIR, base,0);
-       if (!ent) {
-               printk(KERN_ERR "parport: Error registering proc_entry /proc/%s\n",name);
-               return 1;
-       }
-       pp->pdir.entry = ent;
-
-       ent = new_proc_entry("irq", S_IFREG | S_IRUGO | S_IWUSR, pp->pdir.entry,0);
-       if (!ent) {
-               printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/irq\n",name);
-               return 1;
-       }
-       ent->read_proc = irq_read_proc;
-       ent->write_proc= irq_write_proc;
-       ent->data      = pp;
-       pp->pdir.irq   = ent;
-       
-       ent = new_proc_entry("devices", 0, pp->pdir.entry,0);
-       if (!ent) {
-               printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/devices\n",name);
-               return 1;
-       }
-       ent->read_proc   = devices_read_proc;
-       ent->data        = pp;
-       pp->pdir.devices = ent;
-       
-       ent = new_proc_entry("hardware", 0, pp->pdir.entry,0);
-       if (!ent) {
-               printk(KERN_ERR "parport: Error registering proc_entry /proc/%s/hardware\n",name);
-               return 1;
-       }
-       ent->read_proc    = hardware_read_proc;
-       ent->data         = pp;
-       pp->pdir.hardware = ent;
-       return 0;
-}
-
-int parport_proc_unregister(struct parport *pp)
-{
-       if (pp->pdir.entry) {
-               if (pp->pdir.irq) {
-                       proc_unregister(pp->pdir.entry, pp->pdir.irq->low_ino);
-                       kfree(pp->pdir.irq);
-               }
-               
-               if (pp->pdir.devices) {
-                       proc_unregister(pp->pdir.entry,
-                                       pp->pdir.devices->low_ino);
-                       kfree(pp->pdir.devices);
-               }
-               
-               if (pp->pdir.hardware) {
-                       proc_unregister(pp->pdir.entry,
-                                       pp->pdir.hardware->low_ino);
-                       kfree(pp->pdir.hardware);
-               }
-               
-               proc_unregister(base, pp->pdir.entry->low_ino);
-               kfree(pp->pdir.entry);
-       }
-       
-       return 0;
-}
diff --git a/drivers/pnp/parport_share.c b/drivers/pnp/parport_share.c
deleted file mode 100644 (file)
index 9b854f1..0000000
+++ /dev/null
@@ -1,470 +0,0 @@
-/* $Id: parport_share.c,v 1.3.2.5 1997/04/16 21:20:44 phil Exp $
- * Parallel-port resource manager code.
- * 
- * Authors: David Campbell <campbell@tirian.che.curtin.edu.au>
- *          Tim Waugh <tmw20@cam.ac.uk>
- *         Jose Renau <renau@acm.org>
- *
- * based on work by Grant Guenther <grant@torque.net>
- *              and Philip Blundell <Philip.Blundell@pobox.com>
- */
-
-#include <linux/tasks.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#include <linux/parport.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/kernel.h>
-#include <linux/malloc.h>
-
-#undef PARPORT_PARANOID
-
-#include "parport_ll_io.h"
-
-static struct parport *portlist = NULL, *portlist_tail = NULL;
-static int portcount = 0;
-
-/* from parport_init.c */
-extern int initialize_parport(struct parport *, unsigned long base, 
-                             int irq, int dma, int count);
-
-/* Return a list of all the ports we know about. */
-struct parport *parport_enumerate(void)
-{
-       return portlist;
-}
-
-void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
-{
-       /* NULL function - Does nothing */
-       return;
-}
-
-struct parport *parport_register_port(unsigned long base, int irq, int dma)
-{
-       struct parport new, *tmp;
-
-       /* Check for a previously registered port.
-        * NOTE: we will ignore irq and dma if we find a previously
-        * registered device.
-        */
-       for (tmp = portlist; tmp; tmp = tmp->next) {
-               if (tmp->base == base)
-                       return tmp;
-       }
-
-       /* Has someone grabbed the address yet? */
-       if (check_region(base, 3))
-               return NULL;
-
-       if (!initialize_parport(&new,base,irq,dma,portcount))
-               return NULL;
-                                          
-       if (new.dma >= 0) {
-               if (request_dma(new.dma, new.name)) {
-                       printk(KERN_INFO "%s: unable to claim DMA %d\n", 
-                              new.name, new.dma);
-                       release_region(new.base, new.size);
-                       if( new.modes & PARPORT_MODE_ECR )
-                               release_region(new.base+0x400, 3);
-                       kfree(new.name);
-                       return NULL;
-               }
-       }
-
-       tmp = kmalloc(sizeof(struct parport), GFP_KERNEL);
-       if (!tmp) {
-               printk(KERN_WARNING "parport: memory squeeze\n");
-               release_region(new.base, new.size);
-               if( new.modes & PARPORT_MODE_ECR )
-                       release_region(new.base+0x400, 3);
-               kfree(new.name);
-               return NULL;
-       }
-       memcpy(tmp, &new, sizeof(struct parport));
-
-       if (new.irq != PARPORT_IRQ_NONE) {
-               if (request_irq(new.irq, parport_null_intr_func,
-                         SA_INTERRUPT, new.name, tmp) != 0) {
-                       printk(KERN_INFO "%s: unable to claim IRQ %d\n", 
-                              new.name, new.irq);
-                       kfree(tmp);
-                       release_region(new.base, new.size);
-                       if( new.modes & PARPORT_MODE_ECR )
-                               release_region(new.base+0x400, 3);
-                       kfree(new.name);
-                       return NULL;
-               }
-       }
-
-       /* Here we chain the entry to our list. */
-       if (portlist_tail)
-               portlist_tail->next = tmp;
-       portlist_tail = tmp;
-       if (!portlist)
-               portlist = tmp;
-
-       printk(KERN_INFO "%s at 0x%x", tmp->name, tmp->base);
-       if (tmp->irq >= 0)
-               printk(", irq %d", tmp->irq);
-       if (tmp->dma >= 0)
-               printk(", dma %d", tmp->dma);
-       printk(" [");
-       {
-               /* Ugh! */
-#define printmode(x) {if(tmp->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}}
-               int f = 0;
-               printmode(SPP);
-               printmode(PS2);
-               printmode(EPP);
-               printmode(ECP);
-               printmode(ECPEPP);
-               printmode(ECPPS2);
-#undef printmode
-       }
-       printk("]\n");
-       portcount++;
-
-       /* Restore device back to default conditions */
-       if (tmp->modes & PARPORT_MODE_ECR)
-               w_ecr(tmp, tmp->ecr);
-       w_ctr(tmp, tmp->ctr);
-
-       tmp->probe_info.class = PARPORT_CLASS_LEGACY;  /* assume the worst */
-       return tmp;
-}
-
-void parport_destroy(struct parport *port)
-{
-       /* Dangerous to try destroying a port if its friends are nearby. */
-       if (port->devices) {
-               printk("%s: attempt to release active port\n", port->name);
-               return;         /* Devices still present */
-       }
-
-       /* No point in further destroying a port that already lies in ruins. */
-       if (port->flags & PARPORT_FLAG_COMA) 
-               return;
-
-       /* Now clean out the port entry */
-       if (port->irq >= 0)
-               free_irq(port->irq, port);
-       if (port->dma >= 0)
-               free_dma(port->dma);
-       release_region(port->base, port->size);
-       if( port->modes & PARPORT_MODE_ECR )
-               release_region(port->base+0x400, 3);
-       port->flags |= PARPORT_FLAG_COMA; 
-}
-
-struct ppd *parport_register_device(struct parport *port, const char *name,
-                                  callback_func pf, callback_func kf,
-                                  irq_handler_func irq_func, int flags,
-                                  void *handle)
-{
-       struct ppd *tmp;
-
-       /* We only allow one lurker device (eg PLIP) */
-       if (flags & PARPORT_DEV_LURK) {
-               if (port->lurker) {
-                       printk(KERN_INFO "%s: refused to register second lurker (%s)\n",
-                                  port->name, name);
-                       return NULL;
-               }
-               if (!pf || !kf) {
-                       printk(KERN_INFO "%s: refused to register lurking device (%s) without callbacks\n"
-                                  ,port->name, name);
-                       return NULL;
-               }
-       }
-
-       /* We may need to claw back the port hardware. */
-       if (port->flags & PARPORT_FLAG_COMA) {
-               if (check_region(port->base, 3)) {
-                       return NULL;
-               }
-               request_region(port->base, port->size, port->name);
-               if( port->modes & PARPORT_MODE_ECR )
-                       request_region(port->base+0x400, 3,port->name);
-                       
-               if (port->dma >= 0) {
-                       if (request_dma(port->dma, port->name)) {
-                               release_region(port->base, port->size);
-                               if( port->modes & PARPORT_MODE_ECR )
-                                       release_region(port->base+0x400, 3);
-                               return NULL;
-                       }
-               }
-               if (port->irq != PARPORT_IRQ_NONE) {
-                       if (request_irq(port->irq, parport_null_intr_func,
-                                       SA_INTERRUPT, port->name,
-                                       port) != 0) {
-                               release_region(port->base, port->size);
-                               if( port->modes & PARPORT_MODE_ECR )
-                                       release_region(port->base+0x400, 3);
-                               if (port->dma >= 0)
-                                       free_dma(port->dma);
-                               return NULL;
-                       }
-               }
-               port->flags &= ~PARPORT_FLAG_COMA;
-       }
-
-       tmp = kmalloc(sizeof(struct ppd), GFP_KERNEL);
-       tmp->name = (char *) name;
-       tmp->port = port;
-       tmp->preempt = pf;
-       tmp->wakeup = kf;
-       tmp->private = handle;
-       tmp->flags = flags;
-       tmp->irq_func = irq_func;
-       tmp->ctr = port->ctr;
-       tmp->ecr = port->ecr;
-
-       /* Chain this onto the list */
-       tmp->prev = NULL;
-       tmp->next = port->devices;
-       if (port->devices)
-               port->devices->prev = tmp;
-       port->devices = tmp;
-
-       if (flags & PARPORT_DEV_LURK)
-               port->lurker = tmp;
-
-       inc_parport_count();
-
-       return tmp;
-}
-
-void parport_unregister_device(struct ppd *dev)
-{
-       struct parport *port;
-
-       if (!dev) {
-               printk(KERN_ERR "parport_unregister_device: passed NULL\n");
-               return;
-       }
-
-       port = dev->port;
-
-       if (port->cad == dev) {
-               printk(KERN_INFO "%s: refused to unregister currently active device %s\n", port->name, dev->name);
-               return;
-       }
-
-       if (port->lurker == dev)
-               port->lurker = NULL;
-
-       if (dev->next)
-               dev->next->prev = dev->prev;
-       if (dev->prev)
-               dev->prev->next = dev->next;
-       else
-               port->devices = dev->next;
-
-       kfree(dev);
-
-       dec_parport_count();
-
-       /* If there are no more devices, put the port to sleep. */
-       if (!port->devices)
-               parport_destroy(port);
-
-       return;
-}
-
-int parport_claim(struct ppd *dev)
-{
-       struct ppd *pd1;
-
-       if (dev->port->cad == dev) {
-               printk(KERN_INFO "%s: %s already owner\n",
-                          dev->port->name,dev->name);
-               return 0;
-       }
-
-       /* Preempt any current device */
-       pd1 = dev->port->cad;
-       if (dev->port->cad) {
-               if (dev->port->cad->preempt) {
-                       /* Now try to preempt */
-                       if (dev->port->cad->preempt(dev->port->cad->private))
-                               return -EAGAIN;
-
-                       /* Save control registers */
-                       if (dev->port->modes & PARPORT_MODE_ECR)
-                               dev->port->cad->ecr = dev->port->ecr = 
-                                       r_ecr(dev->port);
-                       if (dev->port->modes & PARPORT_MODE_SPP)
-                               dev->port->cad->ctr = dev->port->ctr =
-                                       r_ctr(dev->port);
-               } else
-                       return -EAGAIN;
-       }
-
-       /* Watch out for bad things */
-       if (dev->port->cad != pd1) {
-               printk(KERN_WARNING "%s: death while preempting %s\n",
-                      dev->port->name, dev->name);
-               if (dev->port->cad)
-                       return -EAGAIN;
-       }
-
-       /* Now we do the change of devices */
-       dev->port->cad = dev;
-
-       if (dev->port->irq >= 0) {
-               free_irq(dev->port->irq, dev->port);
-               request_irq(dev->port->irq, dev->irq_func ? dev->irq_func :
-                           parport_null_intr_func, SA_INTERRUPT, dev->name,
-                           dev->port);
-       }
-
-       /* Restore control registers */
-       if (dev->port->modes & PARPORT_MODE_ECR)
-               if (dev->ecr != dev->port->ecr) w_ecr(dev->port, dev->ecr);
-       if (dev->port->modes & PARPORT_MODE_SPP)
-               if (dev->ctr != dev->port->ctr) w_ctr(dev->port, dev->ctr);
-
-       return 0;
-}
-
-void parport_release(struct ppd *dev)
-{
-       struct ppd *pd1;
-
-       /* Make sure that dev is the current device */
-       if (dev->port->cad != dev) {
-               printk(KERN_WARNING "%s: %s tried to release parport when not owner\n", dev->port->name, dev->name);
-               return;
-       }
-       dev->port->cad = NULL;
-
-       /* Save control registers */
-       if (dev->port->modes & PARPORT_MODE_ECR)
-               dev->ecr = dev->port->ecr = r_ecr(dev->port);
-       if (dev->port->modes & PARPORT_MODE_SPP)
-               dev->ctr = dev->port->ctr = r_ctr(dev->port);
-       
-       if (dev->port->irq >= 0) {
-               free_irq(dev->port->irq, dev->port);
-               request_irq(dev->port->irq, parport_null_intr_func,
-                           SA_INTERRUPT, dev->port->name, dev->port);
-       }
-
-       /* Walk the list, offering a wakeup callback to everybody other
-        * than the lurker and the device that called us.
-        */
-       for (pd1 = dev->next; pd1; pd1 = pd1->next) {
-               if (!(pd1->flags & PARPORT_DEV_LURK)) {
-                       if (pd1->wakeup) {
-                               pd1->wakeup(pd1->private);
-                               if (dev->port->cad)
-                                       return;
-                       }
-               }
-       }
-
-       for (pd1 = dev->port->devices; pd1 && pd1 != dev; pd1 = pd1->next) {
-               if (!(pd1->flags & PARPORT_DEV_LURK)) {
-                       if (pd1->wakeup) {
-                               pd1->wakeup(pd1->private);
-                               if (dev->port->cad)
-                                       return;
-                       }
-               }
-       }
-
-       /* Now give the lurker a chance.
-        * There should be a wakeup callback because we checked for it
-        * at registration.
-        */
-       if (dev->port->lurker && (dev->port->lurker != dev)) {
-               if (dev->port->lurker->wakeup) {
-                       dev->port->lurker->wakeup(dev->port->lurker->private);
-               } 
-#ifdef PARPORT_PARANOID
-               else {  /* can't happen */
-                       printk(KERN_DEBUG
-                         "%s (%s): lurker's wakeup callback went away!\n",
-                              dev->port->name, dev->name);
-               }
-#endif
-       }
-}
-
-/* The following read funktions are an implementation of a status readback
- * and device id request confirming to IEEE1284-1994.
- *
- * These probably ought to go in some seperate file, so people like the SPARC
- * don't have to pull them in.
- */
-
-/* Wait for Status line(s) to change in 35 ms - see IEEE1284-1994 page 24 to
- * 25 for this. After this time we can create a timeout because the
- * peripheral doesn't conform to IEEE1284. We want to save CPU time: we are
- * waiting a maximum time of 500 us busy (this is for speed). If there is
- * not the right answer in this time, we call schedule and other processes
- * are able "to eat" the time up to 30ms.  So the maximum load avarage can't
- * get above 5% for a read even if the peripheral is really slow. (but your
- * read gets very slow then - only about 10 characters per second. This
- * should be tuneable). Thanks to Andreas who pointed me to this and ordered
- * the documentation.
- */ 
-
-int parport_wait_peripheral(struct parport *port, unsigned char mask, 
-       unsigned char result)
-{
-       int counter=0;
-       unsigned char status; 
-       
-       do {
-               status = parport_r_status(port);
-               udelay(25);
-               counter++;
-               if (need_resched)
-                       schedule();
-       } while ( ((status & mask) != result) && (counter < 20) );
-       if ( (counter == 20) && ((status & mask) != result) ) { 
-               current->state=TASK_INTERRUPTIBLE;
-               current->timeout=jiffies+4;
-               schedule(); /* wait for 4 scheduler runs (40ms) */
-               status = parport_r_status(port);
-               if ((status & mask) != result) return 1; /* timeout */
-       }
-       return 0; /* okay right response from device */
-}              
-
-/* Test if nibble mode for status readback is okay. Returns the value false
- * if the printer doesn't support readback at all. If it supports readbacks
- * and printer data is available the function returns 1, otherwise 2. The
- * only valid values for "mode" are 0 and 4. 0 requests normal nibble mode,
- * 4 is for "request device id using nibble mode". The request for the
- * device id is best done in an ioctl (or at bootup time).  There is no
- * check for an invalid value, the only function using this call at the
- * moment is lp_read and the ioctl LPGETDEVICEID both fixed calls from
- * trusted kernel.
- */
-int parport_ieee1284_nibble_mode_ok(struct parport *port, unsigned char mode) 
-{
-       parport_w_data(port, mode);
-       udelay(5);              
-       parport_w_ctrl(port, parport_r_ctrl(port) & ~8);  /* SelectIN low */
-       parport_w_ctrl(port, parport_r_ctrl(port) | 2); /* AutoFeed high */
-       if (parport_wait_peripheral(port, 0x78, 0x38)) { /* timeout? */
-               parport_w_ctrl(port, (parport_r_ctrl(port) & ~2) | 8);
-               return 0; /* first stage of negotiation failed, 
-                           * no IEEE1284 compliant device on this port 
-                           */ 
-       }
-       parport_w_ctrl(port, parport_r_ctrl(port) | 1);      /* Strobe high */
-       udelay(5);                                   /* Strobe wait */
-       parport_w_ctrl(port, parport_r_ctrl(port) & ~1);     /* Strobe low */
-       udelay(5);
-       parport_w_ctrl(port, parport_r_ctrl(port) & ~2);     /* AutoFeed low */
-       return (parport_wait_peripheral(port, 0x20, 0))?2:1;
-}
index 395aed829c993163d60d6e5afe18f6039347ca62..97937822fe879dc98e541d13c522d5edef45afbd 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/malloc.h>
+#include <linux/init.h>
 
 /*
  * This is the single most critical data structure when it comes
@@ -229,6 +230,46 @@ struct dentry * d_lookup(struct dentry * dir, struct qstr * name)
        return __dlookup(d_hash(dir, name->hash), dir, name);
 }
 
+/*
+ * An insecure source has sent us a dentry, here we verify it.
+ *
+ * This is just to make knfsd able to have the dentry pointer
+ * in the NFS file handle.
+ *
+ * NOTE! Do _not_ dereference the pointers before we have
+ * validated them. We can test the pointer values, but we
+ * must not actually use them until we have found a valid
+ * copy of the pointer in kernel space..
+ */
+int d_validate(struct dentry *dentry, struct dentry *dparent,
+              unsigned int hash, unsigned int len)
+{
+       struct list_head *base = d_hash(dparent, hash);
+       struct list_head *lhp = base;
+
+       while ((lhp = lhp->next) != base) {
+               if (dentry == list_entry(lhp, struct dentry, d_hash))
+                       goto found_it;
+       }
+
+       /* Special case, local mount points don't live in the hashes.
+        * So if we exhausted the chain, search the super blocks.
+        */
+       if (dentry && dentry == dparent) {
+               struct super_block *sb;
+
+               for (sb = super_blocks + 0; sb < super_blocks + NR_SUPER; sb++) {
+                       if (sb->s_root == dentry)
+                               goto found_it;
+               }
+       }
+       return 0;
+found_it:
+       return  (dentry->d_parent == dparent) &&
+               (dentry->d_name.hash == hash) &&
+               (dentry->d_name.len == len);
+}
+
 static inline void d_insert_to_parent(struct dentry * entry, struct dentry * parent)
 {
        list_add(&entry->d_hash, d_hash(dget(parent), entry->d_name.hash));
@@ -313,13 +354,17 @@ void d_move(struct dentry * dentry, struct dentry * newdir, struct qstr * newnam
        d_insert_to_parent(dentry, newdir);
 }
 
+/*
+ * This is broken in more ways than one. Unchecked recursion,
+ * unchecked buffer size. Get rid of it.
+ */
 int d_path(struct dentry * entry, struct dentry * chroot, char * buf)
 {
        if (IS_ROOT(entry) || (chroot && entry == chroot)) {
                *buf = '/';
                return 1;
        } else {
-               int len = d_path(entry->d_parent, chroot, buf);
+               int len = d_path(entry->d_covers->d_parent, chroot, buf);
 
                buf += len;
                if (len > 1) {
@@ -331,7 +376,7 @@ int d_path(struct dentry * entry, struct dentry * chroot, char * buf)
        }
 }
 
-void dcache_init(void)
+__initfunc(void dcache_init(void))
 {
        int i;
        struct list_head *d = dentry_hashtable;
index e35722affc8ffb85c7d1cd88f92b268e8040cf1c..8ffd91e3d092e257476bc08eb016611ac6816812 100644 (file)
@@ -44,8 +44,18 @@ void fat_put_inode(struct inode *inode)
                        MSDOS_I(inode)->i_linked = NULL;
                }
                if (MSDOS_I(inode)->i_busy) fat_cache_inval_inode(inode);
-               return;
        }
+}
+
+void fat_delete_inode(struct inode *inode)
+{
+       struct inode *depend, *linked;
+       struct super_block *sb;
+
+       depend = MSDOS_I(inode)->i_depend;
+       linked = MSDOS_I(inode)->i_linked;
+       sb = inode->i_sb;
+
        inode->i_size = 0;
        fat_truncate(inode);
        if (depend) {
index 034f62c1f96492948259c3d568e8eeea9a19a123..ee0df4a15e88ac58b6087d35c91d2aac1d42f873 100644 (file)
@@ -40,7 +40,7 @@ void fat_fs_panic(struct super_block *s,const char *msg)
 
        not_ro = !(s->s_flags & MS_RDONLY);
        if (not_ro) s->s_flags |= MS_RDONLY;
-       printk("Filesystem panic (dev %s).", kdevname(s->s_dev));
+       printk("Filesystem panic (dev %s).\n  %s\n", kdevname(s->s_dev), msg);
        if (not_ro)
                printk("  File system has been set read-only\n");
 }
index 878a3f0693dbea3ef9a986f7280501eb4b2b5814..70f293821b4823da707053ed6d38f9e84de9d2f7 100644 (file)
@@ -128,15 +128,16 @@ typedef void nonconst;
 
 static void hpfs_read_inode(struct inode *);
 static void hpfs_put_super(struct super_block *);
-static void hpfs_statfs(struct super_block *, struct statfs *, int);
+static int hpfs_statfs(struct super_block *, struct statfs *, int);
 static int hpfs_remount_fs(struct super_block *, int *, char *);
 
 static const struct super_operations hpfs_sops =
 {
        hpfs_read_inode,                /* read_inode */
-       NULL,                           /* notify_change */
        NULL,                           /* write_inode */
        NULL,                           /* put_inode */
+       NULL,                           /* delete_inode */
+       NULL,                           /* notify_change */
        hpfs_put_super,                 /* put_super */
        NULL,                           /* write_super */
        hpfs_statfs,                    /* statfs */
@@ -175,6 +176,7 @@ static const struct inode_operations hpfs_file_iops =
        NULL,                           /* mknod */
        NULL,                           /* rename */
        NULL,                           /* readlink */
+       NULL,                           /* follow_link */
        generic_readpage,               /* readpage */
        NULL,                           /* writepage */
        (int (*)(struct inode *, int))
@@ -189,7 +191,7 @@ static long hpfs_dir_read(struct inode *inode, struct file *filp,
                          char *buf, unsigned long count);
 static int hpfs_readdir(struct inode *inode, struct file *filp,
                        void *dirent, filldir_t filldir);
-static int hpfs_lookup(struct inode *, const char *, int, struct inode **);
+static int hpfs_lookup(struct inode *, struct dentry *);
 
 static const struct file_operations hpfs_dir_ops =
 {
@@ -485,10 +487,10 @@ struct super_block *hpfs_read_super(struct super_block *s,
         * all set.  try it out.
         */
 
-       s->s_mounted = iget(s, s->s_hpfs_root);
+       s->s_root = d_alloc_root(iget(s, s->s_hpfs_root), NULL);
        unlock_super(s);
 
-       if (!s->s_mounted) {
+       if (!s->s_root) {
                printk("HPFS: hpfs_read_super: inode get failed\n");
                s->s_dev = 0;
                MOD_DEC_USE_COUNT;
@@ -501,7 +503,8 @@ struct super_block *hpfs_read_super(struct super_block *s,
 
        root_dno = fnode_dno(dev, s->s_hpfs_root);
        if (root_dno)
-               de = map_dirent(s->s_mounted, root_dno, "\001\001", 2, &qbh);
+               de = map_dirent(s->s_root->d_inode, root_dno,
+                               "\001\001", 2, &qbh);
        if (!root_dno || !de) {
                printk("HPFS: "
                       "hpfs_read_super: root dir isn't in the root dir\n");
@@ -510,9 +513,9 @@ struct super_block *hpfs_read_super(struct super_block *s,
                return 0;
        }
 
-       s->s_mounted->i_atime = local_to_gmt(de->read_date);
-       s->s_mounted->i_mtime = local_to_gmt(de->write_date);
-       s->s_mounted->i_ctime = local_to_gmt(de->creation_date);
+       s->s_root->d_inode->i_atime = local_to_gmt(de->read_date);
+       s->s_root->d_inode->i_mtime = local_to_gmt(de->write_date);
+       s->s_root->d_inode->i_ctime = local_to_gmt(de->creation_date);
 
        brelse4(&qbh);
        return s;
@@ -739,7 +742,7 @@ static void hpfs_put_super(struct super_block *s)
  * directory band -- not exactly right but pretty analogous.
  */
 
-static void hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
+static int hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
 {
        struct statfs tmp;
 
@@ -763,7 +766,8 @@ static void hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
        tmp.f_files = s->s_hpfs_dirband_size;
        tmp.f_ffree = s->s_hpfs_n_free_dnodes;
        tmp.f_namelen = 254;
-       copy_to_user(buf, &tmp, bufsiz);
+
+       return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
 }
 
 /*
@@ -1115,17 +1119,17 @@ static secno bplus_lookup(struct inode *inode, struct bplus_header *b,
  * the boondocks.)
  */
 
-static int hpfs_lookup(struct inode *dir, const char *name, int len,
-                      struct inode **result)
+static int hpfs_lookup(struct inode *dir, struct dentry *dentry)
 {
        struct quad_buffer_head qbh;
        struct hpfs_dirent *de;
        struct inode *inode;
        ino_t ino;
+       const char *name = dentry->d_name.name;
+       int len = dentry->d_name.len;
 
        /* In case of madness */
 
-       *result = 0;
        if (dir == 0)
                return -ENOENT;
        if (!S_ISDIR(dir->i_mode))
@@ -1197,7 +1201,7 @@ static int hpfs_lookup(struct inode *dir, const char *name, int len,
         * Made it.
         */
 
-       *result = inode;
+       d_instantiate(dentry, inode);
        iput(dir);
        return 0;
 
@@ -1697,7 +1701,7 @@ static void *map_4sectors(kdev_t dev, unsigned secno,
        if (!data)
                goto bail;
 
-       qbh->bh[0] = bh = breada(dev, secno, 512, 0, UINT_MAX);
+       qbh->bh[0] = bh = bread(dev, secno, 512);
        if (!bh)
                goto bail0;
        memcpy(data, bh->b_data, 512);
index 8813bbd4571615c3c86642273f7a912ee2d7cd5e..8fc081b77844881ddb51f850915a2ec6f39448f4 100644 (file)
@@ -141,32 +141,31 @@ static inline void write_inode(struct inode *inode)
                inode->i_sb->s_op->write_inode(inode);
 }
 
+static inline void sync_one(struct list_head *head, struct list_head *clean,
+                           struct list_head *placement, struct inode *inode)
+{
+       list_del(placement);
+       if (test_bit(I_LOCK, &inode->i_state)) {
+               list_add(placement, head);
+               spin_unlock(&inode_lock);
+               __wait_on_inode(inode);
+       } else {
+               list_add(placement, clean);
+               clear_bit(I_DIRTY, &inode->i_state);
+               set_bit(I_LOCK, &inode->i_state);
+               spin_unlock(&inode_lock);
+               write_inode(inode);
+               unlock_inode(inode);
+       }
+       spin_lock(&inode_lock);
+}
+
 static inline void sync_list(struct list_head *head, struct list_head *clean)
 {
        struct list_head * tmp;
 
-       while ((tmp = head->prev) != head) {
-               struct inode *inode = list_entry(tmp, struct inode, i_list);
-               list_del(tmp);
-
-               /*
-                * If the inode is locked, it's already being written out.
-                * We have to wait for it, though.
-                */
-               if (test_bit(I_LOCK, &inode->i_state)) {
-                       list_add(tmp, head);
-                       spin_unlock(&inode_lock);
-                       __wait_on_inode(inode);
-               } else {
-                       list_add(tmp, clean);
-                       clear_bit(I_DIRTY, &inode->i_state);
-                       set_bit(I_LOCK, &inode->i_state);
-                       spin_unlock(&inode_lock);
-                       write_inode(inode);
-                       unlock_inode(inode);
-               }
-               spin_lock(&inode_lock);
-       }       
+       while ((tmp = head->prev) != head)
+               sync_one(head, clean, tmp, list_entry(tmp, struct inode, i_list));
 }
 
 /*
@@ -181,6 +180,17 @@ void sync_inodes(kdev_t dev)
        spin_unlock(&inode_lock);
 }
 
+/*
+ * Needed by knfsd
+ */
+void write_inode_now(struct inode *inode)
+{
+       spin_lock(&inode_lock);
+       if (test_bit(I_DIRTY, &inode->i_state))
+               sync_one(&inode_dirty, &inode_in_use, &inode->i_list, inode);
+       spin_unlock(&inode_lock);
+}
+
 /*
  * This is called by the filesystem to tell us
  * that the inode is no longer useful. We just
index afc3fb2f06e14b3a01b08e45361690f060f62f6c..3526867ca4be96236fc0a73c28ce29e67130fb32 100644 (file)
@@ -24,7 +24,7 @@
  * Global file hash table
  */
 #define FILE_NRHASH            32
-#define FILE_HASH(dev, ino)    (((dev) + (ino)) & FILE_NRHASH)
+#define FILE_HASH(dhash)       ((dhash) & FILE_NRHASH)
 static struct nlm_file *       nlm_files[FILE_NRHASH];
 static struct semaphore                nlm_file_sema = MUTEX;
 
@@ -43,21 +43,21 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
 {
        struct nlm_file *file;
        struct knfs_fh  *fh = (struct knfs_fh *) f;
-       unsigned int    hash = FILE_HASH(fh->fh_dev, fh->fh_ino);
+       unsigned int    hash = FILE_HASH(fh->fh_dhash);
        u32             nfserr;
 
-       dprintk("lockd: nlm_file_lookup(%04x/%ld)\n", fh->fh_dev, fh->fh_ino);
+       dprintk("lockd: nlm_file_lookup(%p)\n", fh->fh_dentry);
 
        /* Lock file table */
        down(&nlm_file_sema);
 
        for (file = nlm_files[hash]; file; file = file->f_next) {
-               if (file->f_handle.fh_ino == fh->fh_ino
+               if (file->f_handle.fh_dentry == fh->fh_dentry
                 && !memcmp(&file->f_handle, fh, sizeof(*fh)))
                        goto found;
        }
 
-       dprintk("lockd: creating file for %04x/%ld\n", fh->fh_dev, fh->fh_ino);
+       dprintk("lockd: creating file for %p\n", fh->fh_dentry);
        if (!(file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL))) {
                up(&nlm_file_sema);
                return nlm_lck_denied_nolocks;
@@ -93,11 +93,11 @@ found:
 static inline void
 nlm_delete_file(struct nlm_file *file)
 {
-       struct inode    *inode = nlmsvc_file_inode(file);
+       struct dentry *dentry = file->f_file.f_dentry;
        struct nlm_file **fp, *f;
 
-       dprintk("lockd: closing file %04x/%ld\n", inode->i_dev, inode->i_ino);
-       fp = nlm_files + FILE_HASH(inode->i_dev, inode->i_ino);
+       dprintk("lockd: closing file %p\n", dentry);
+       fp = nlm_files + FILE_HASH(dentry->d_name.hash);
        while ((f = *fp) != NULL) {
                if (f == file) {
                        *fp = file->f_next;
index f47f779d5d5b466f521a359c115a1b5c7ce78605..9e27ee1188786a8da64e4ee1b84952b8f0a01950 100644 (file)
@@ -191,7 +191,7 @@ void minix_free_inode(struct inode * inode)
                printk("free_inode: inode has no device\n");
                return;
        }
-       if (inode->i_count != 1) {
+       if (inode->i_count > 1) {
                printk("free_inode: inode has count=%d\n",inode->i_count);
                return;
        }
@@ -251,7 +251,6 @@ struct inode * minix_new_inode(const struct inode * dir)
                iput(inode);
                return NULL;
        }
-       inode->i_count = 1;
        inode->i_nlink = 1;
        inode->i_dev = sb->s_dev;
        inode->i_uid = current->fsuid;
index ec5113c4a67fee58bb11f4ae52462ee5bfe05de3..31ac394a2952a975f053fdafc70d29961509f1fd 100644 (file)
@@ -92,5 +92,6 @@ static int minix_readdir(struct inode * inode, struct file * filp,
                } while (offset < 1024 && filp->f_pos < inode->i_size);
                brelse(bh);
        }
+       UPDATE_ATIME(inode);
        return 0;
 }
index 7ca7cb075c7351eeb5ec2c35cb3919b4a83550d8..ecd0d24d98ee22df1d112666b34b4a8ad07199fe 100644 (file)
@@ -108,7 +108,13 @@ static long minix_file_write(struct inode * inode, struct file * filp,
                        }
                }
                p = (pos % BLOCK_SIZE) + bh->b_data;
-               copy_from_user(p,buf,c);
+               c -= copy_from_user(p,buf,c);
+               if (!c) {
+                       brelse(bh);
+                       if (!written)
+                               written = -EFAULT;
+                       break;
+               }
                update_vm_cache(inode, pos, p, c);
                mark_buffer_uptodate(bh, 1);
                mark_buffer_dirty(bh, 0);
index 718d3dd070fd452c4966e0e77620e05c0056e549..19c7df24739a3cdb14e379bff1073f1a59aa7fd6 100644 (file)
@@ -109,7 +109,7 @@ int minix_lookup(struct inode * dir, struct dentry *dentry)
 
        bh = minix_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);
        if (bh) {
-               unsigned long ino = le32_to_cpu(de->inode);
+               int ino = de->inode;
                brelse (bh);
                inode = iget(dir->i_sb, ino);
  
@@ -593,6 +593,7 @@ int minix_link(struct inode * inode, struct inode * dir,
        inode->i_nlink++;
        inode->i_ctime = CURRENT_TIME;
        mark_inode_dirty(inode);
+       inode->i_count++;
        d_instantiate(dentry, inode);
        return 0;
 }
index 9481a763cdae49564777054fb805017ba6a1545f..637649dae4bed15dd666bba15e4df10341155ba8 100644 (file)
@@ -53,9 +53,10 @@ void msdos_put_super(struct super_block *sb)
 
 struct super_operations msdos_sops = { 
        msdos_read_inode,
-       fat_notify_change,
        fat_write_inode,
        fat_put_inode,
+       fat_delete_inode,
+       fat_notify_change,
        msdos_put_super,
        NULL, /* added in 0.96c */
        fat_statfs,
@@ -190,79 +191,56 @@ static int msdos_find(struct inode *dir,const char *name,int len,
 }
 
 /***** Get inode using directory and name */
-int msdos_lookup(struct inode *dir,const char *name,int len,
-    struct inode **result)
+int msdos_lookup(struct inode *dir,struct dentry *dentry)
 {
        struct super_block *sb = dir->i_sb;
        int ino,res;
        struct msdos_dir_entry *de;
        struct buffer_head *bh;
-       struct inode *next;
+       struct inode *next, *inode;
        
        PRINTK (("msdos_lookup\n"));
 
-       *result = NULL;
-       if (!dir) return -ENOENT;
-       if (!S_ISDIR(dir->i_mode)) {
-               iput(dir);
-               return -ENOENT;
-       }
-       PRINTK (("msdos_lookup 2\n"));
-       if (len == 1 && name[0] == '.') {
-               *result = dir;
-               return 0;
-       }
-       if (len == 2 && name[0] == '.' && name[1] == '.') {
-               ino = fat_parent_ino(dir,0);
-               iput(dir);
-               if (ino < 0) return ino;
-               if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
+       if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,&bh,&de,&ino)) < 0) {
+               d_add(dentry, NULL);
                return 0;
        }
-       PRINTK (("msdos_lookup 3\n"));
-       if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) {
-               iput(dir);
-               return res;
-       }
        PRINTK (("msdos_lookup 4\n"));
        if (bh)
                fat_brelse(sb, bh);
        PRINTK (("msdos_lookup 4.5\n"));
-       if (!(*result = iget(dir->i_sb,ino))) {
-               iput(dir);
+       if (!(inode = iget(dir->i_sb,ino)))
                return -EACCES;
-       }
        PRINTK (("msdos_lookup 5\n"));
-       if (!(*result)->i_sb ||
-           ((*result)->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
+       if (!inode->i_sb ||
+           (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
                /* crossed a mount point into a non-msdos fs */
-               iput(dir);
+               d_add(dentry, inode);
                return 0;
        }
-       if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
-               iput(*result);
-               iput(dir);
-               return -ENOENT;
+       if (MSDOS_I(inode)->i_busy) { /* mkdir in progress */
+               iput(inode);
+               d_add(dentry, NULL);
+               return 0;
        }
        PRINTK (("msdos_lookup 6\n"));
-       while (MSDOS_I(*result)->i_old) {
-               next = MSDOS_I(*result)->i_old;
-               iput(*result);
-               if (!(*result = iget(next->i_sb,next->i_ino))) {
+       while (MSDOS_I(inode)->i_old) {
+               next = MSDOS_I(inode)->i_old;
+               iput(inode);
+               if (!(inode = iget(next->i_sb,next->i_ino))) {
                        fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
-                       iput(dir);
                        return -ENOENT;
                }
        }
        PRINTK (("msdos_lookup 7\n"));
-       iput(dir);
+       d_add(dentry, inode);
        PRINTK (("msdos_lookup 8\n"));
        return 0;
 }
 
 
 /***** Creates a directory entry (name is already formatted). */
-static int msdos_create_entry(struct inode *dir, const char *name,int len,
+static int msdos_create_entry(struct inode *dir, const char *name,
     int is_dir, int is_hid, struct inode **result)
 {
        struct super_block *sb = dir->i_sb;
@@ -270,6 +248,7 @@ static int msdos_create_entry(struct inode *dir, const char *name,int len,
        struct msdos_dir_entry *de;
        int res,ino;
 
+       *result = NULL;
        if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
                if (res != -ENOENT) return res;
                if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
@@ -300,23 +279,22 @@ static int msdos_create_entry(struct inode *dir, const char *name,int len,
 }
 
 /***** Create a file or directory */
-int msdos_create(struct inode *dir,const char *name,int len,int mode,
-       struct inode **result)
+int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
 {
        struct super_block *sb = dir->i_sb;
        struct buffer_head *bh;
        struct msdos_dir_entry *de;
+       struct inode *inode;
        char msdos_name[MSDOS_NAME];
        int ino,res,is_hid;
 
        if (!dir) return -ENOENT;
        if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
-                                    name,len,msdos_name,0,
-                                    MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) {
-               iput(dir);
+                                    dentry->d_name.name,dentry->d_name.len,
+                                    msdos_name,0,
+                                    MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0)
                return res;
-       }
-       is_hid = (name[0]=='.') && (msdos_name[0]!='.');
+       is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
        fat_lock_creation();
        /* Scan for existing file twice, so that creating a file fails
         * with -EINVAL if the other (dotfile/nondotfile) exists.
@@ -325,19 +303,17 @@ int msdos_create(struct inode *dir,const char *name,int len,int mode,
        if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
                fat_unlock_creation();
                fat_brelse(sb, bh);
-               iput(dir);
                return is_hid ? -EEXIST : -EINVAL;
        }
        if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
                fat_unlock_creation();
                fat_brelse(sb, bh);
-               iput(dir);
                return is_hid ? -EINVAL : -EEXIST;
        }
-       res = msdos_create_entry(dir,msdos_name,len,S_ISDIR(mode),is_hid,
-                                result);
+       res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid,
+                                &inode);
        fat_unlock_creation();
-       iput(dir);
+       d_instantiate(dentry, inode);
        return res;
 }
 
@@ -388,7 +364,7 @@ static int msdos_empty(struct inode *dir)
 }
 
 /***** Remove a directory */
-int msdos_rmdir(struct inode *dir,const char *name,int len)
+int msdos_rmdir(struct inode *dir,struct dentry *dentry)
 {
        struct super_block *sb = dir->i_sb;
        int res,ino;
@@ -399,11 +375,10 @@ int msdos_rmdir(struct inode *dir,const char *name,int len)
        bh = NULL;
        inode = NULL;
        res = -EPERM;
-       if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.')))
+       if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,
+                             &bh,&de,&ino)) < 0)
                goto rmdir_done;
-       if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) goto rmdir_done;
-       res = -ENOENT;
-       if (!(inode = iget(dir->i_sb,ino))) goto rmdir_done;
+       inode = dentry->d_inode;
        res = -ENOTDIR;
        if (!S_ISDIR(inode->i_mode)) goto rmdir_done;
        res = -EBUSY;
@@ -419,16 +394,15 @@ int msdos_rmdir(struct inode *dir,const char *name,int len)
        mark_inode_dirty(dir);
        de->name[0] = DELETED_FLAG;
        fat_mark_buffer_dirty(sb, bh, 1);
+       d_delete(dentry);
        res = 0;
 rmdir_done:
        fat_brelse(sb, bh);
-       iput(dir);
-       iput(inode);
        return res;
 }
 
 /***** Make a directory */
-int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
+int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
 {
        struct super_block *sb = dir->i_sb;
        struct buffer_head *bh;
@@ -438,37 +412,34 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
        int ino,res,is_hid;
 
        if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
-                                    name,len,msdos_name,0,
-                                    MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) {
-               iput(dir);
+                                    dentry->d_name.name,dentry->d_name.len,
+                                    msdos_name,0,
+                                    MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0)
                return res;
-       }
-       is_hid = (name[0]=='.') && (msdos_name[0]!='.');
+       is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
        fat_lock_creation();
        if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
                fat_unlock_creation();
                fat_brelse(sb, bh);
-               iput(dir);
                return -EEXIST;
        }
-       if ((res = msdos_create_entry(dir,msdos_name,len,1,is_hid,
+       if ((res = msdos_create_entry(dir,msdos_name,1,is_hid,
                                      &inode)) < 0) {
                fat_unlock_creation();
-               iput(dir);
                return res;
        }
        dir->i_nlink++;
        inode->i_nlink = 2; /* no need to mark them dirty */
        MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
        if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error;
-       if ((res = msdos_create_entry(inode,MSDOS_DOT,1,1,0,&dot)) < 0)
+       if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
                goto mkdir_error;
        dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
        MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
        dot->i_nlink = inode->i_nlink;
        mark_inode_dirty(dot);
        iput(dot);
-       if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,2,1,0,&dot)) < 0)
+       if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0)
                goto mkdir_error;
        fat_unlock_creation();
        dot->i_size = dir->i_size;
@@ -477,12 +448,10 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
        mark_inode_dirty(dot);
        MSDOS_I(inode)->i_busy = 0;
        iput(dot);
-       iput(inode);
-       iput(dir);
+       d_instantiate(dentry, inode);
        return 0;
 mkdir_error:
-       iput(inode);
-       if (msdos_rmdir(dir,name,len) < 0)
+       if (msdos_rmdir(dir,dentry) < 0)
                fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
        fat_unlock_creation();
        return res;
@@ -491,8 +460,7 @@ mkdir_error:
 /***** Unlink a file */
 static int msdos_unlinkx(
        struct inode *dir,
-       const char *name,
-       int len,
+       struct dentry *dentry,
        int nospc)      /* Flag special file ? */
 {
        struct super_block *sb = dir->i_sb;
@@ -503,12 +471,10 @@ static int msdos_unlinkx(
 
        bh = NULL;
        inode = NULL;
-       if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0)
-               goto unlink_done;
-       if (!(inode = iget(dir->i_sb,ino))) {
-               res = -ENOENT;
+       if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,
+                             &bh,&de,&ino)) < 0)
                goto unlink_done;
-       }
+       inode = dentry->d_inode;
        if (!S_ISREG(inode->i_mode) && nospc){
                res = -EPERM;
                goto unlink_done;
@@ -524,28 +490,28 @@ static int msdos_unlinkx(
        mark_inode_dirty(dir);
        de->name[0] = DELETED_FLAG;
        fat_mark_buffer_dirty(sb, bh, 1);
+       d_delete(dentry);       /* This also frees the inode */
 unlink_done:
        fat_brelse(sb, bh);
-       iput(inode);
-       iput(dir);
        return res;
 }
 
 /***** Unlink, as called for msdosfs */
-int msdos_unlink(struct inode *dir,const char *name,int len)
+int msdos_unlink(struct inode *dir,struct dentry *dentry)
 {
-       return msdos_unlinkx (dir,name,len,1);
+       return msdos_unlinkx (dir,dentry,1);
 }
 
 /***** Unlink, as called for umsdosfs */
-int msdos_unlink_umsdos(struct inode *dir,const char *name,int len)
+int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry)
 {
-       return msdos_unlinkx (dir,name,len,0);
+       return msdos_unlinkx (dir,dentry,0);
 }
 
 /***** Rename within a directory */
-static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
-    struct inode *new_dir,char *new_name,int new_len,
+static int rename_same_dir(struct inode *old_dir,char *old_name,
+    struct dentry *old_dentry,
+    struct inode *new_dir,char *new_name,struct dentry *new_dentry,
     struct buffer_head *old_bh,
     struct msdos_dir_entry *old_de,int old_ino,int is_hid)
 {
@@ -563,10 +529,7 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
                return -ENOENT;
        }
        if (exists) {
-               if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
-                       fat_brelse(sb, new_bh);
-                       return -EIO;
-               }
+               new_inode = new_dentry->d_inode;
                error = S_ISDIR(new_inode->i_mode)
                        ? (old_de->attr & ATTR_DIR)
                                ? msdos_empty(new_inode)
@@ -576,7 +539,6 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
                                : 0;
                if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
                if (error) {
-                       iput(new_inode);
                        fat_brelse(sb, new_bh);
                        return error;
                }
@@ -589,50 +551,49 @@ static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
                mark_inode_dirty(new_inode);
                new_de->name[0] = DELETED_FLAG;
                fat_mark_buffer_dirty(sb, new_bh, 1);
-               iput(new_inode);
                fat_brelse(sb, new_bh);
        }
        memcpy(old_de->name,new_name,MSDOS_NAME);
+       /* Update the dcache */
+       d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
+       d_delete(new_dentry);
 set_hid:
        old_de->attr = is_hid
                ? (old_de->attr | ATTR_HIDDEN)
                : (old_de->attr &~ ATTR_HIDDEN);
        fat_mark_buffer_dirty(sb, old_bh, 1);
        /* update binary info for conversion, i_attrs */
-       if ((old_inode = iget(old_dir->i_sb,old_ino)) != NULL) {
-               MSDOS_I(old_inode)->i_attrs = is_hid
-                       ? (MSDOS_I(old_inode)->i_attrs |  ATTR_HIDDEN)
-                       : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
-               iput(old_inode);
-       }
+       old_inode = old_dentry->d_inode;
+       MSDOS_I(old_inode)->i_attrs = is_hid
+               ? (MSDOS_I(old_inode)->i_attrs |  ATTR_HIDDEN)
+               : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
        return 0;
 }
 
 /***** Rename across directories - a nonphysical move */
-static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
-    struct inode *new_dir,char *new_name,int new_len,
+static int rename_diff_dir(struct inode *old_dir,char *old_name,
+    struct dentry *old_dentry,
+    struct inode *new_dir,char *new_name,struct dentry *new_dentry,
     struct buffer_head *old_bh,
     struct msdos_dir_entry *old_de,int old_ino,int is_hid)
 {
        struct super_block *sb = old_dir->i_sb;
        struct buffer_head *new_bh,*free_bh,*dotdot_bh;
        struct msdos_dir_entry *new_de,*free_de,*dotdot_de;
-       struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode,*walk;
+       struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode;
+       struct dentry *walk;
        int new_ino,free_ino,dotdot_ino;
        int error,exists,ino;
 
        if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
        if (old_ino == new_dir->i_ino) return -EINVAL;
-       if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
+       walk = new_dentry;
        /* prevent moving directory below itself */
-       while (walk->i_ino != MSDOS_ROOT_INO) {
-               ino = fat_parent_ino(walk,1);
-               iput(walk);
-               if (ino < 0) return ino;
-               if (ino == old_ino) return -EINVAL;
-               if (!(walk = iget(new_dir->i_sb,ino))) return -EIO;
+       for (;;) {
+               if (walk == old_dentry) return -EINVAL;
+               if (walk == walk->d_parent) break;
+               walk = walk->d_parent;
        }
-       iput(walk);
        /* find free spot */
        while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino,
            SCAN_ANY)) < 0) {
@@ -641,14 +602,8 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
                if (error) return error;
        }
        exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
-       if (!(old_inode = iget(old_dir->i_sb,old_ino))) {
-               fat_brelse(sb, free_bh);
-               if (exists)
-                       fat_brelse(sb, new_bh);
-               return -EIO;
-       }
+       old_inode = old_dentry->d_inode;
        if (*(unsigned char *) old_de->name == DELETED_FLAG) {
-               iput(old_inode);
                fat_brelse(sb, free_bh);
                if (exists)
                        fat_brelse(sb, new_bh);
@@ -656,11 +611,7 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
        }
        new_inode = NULL; /* to make GCC happy */
        if (exists) {  /* Trash the old file! */
-               if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
-                       iput(old_inode);
-                       fat_brelse(sb, new_bh);
-                       return -EIO;
-               }
+               new_inode = new_dentry->d_inode;
                error = S_ISDIR(new_inode->i_mode)
                        ? (old_de->attr & ATTR_DIR)
                                ? msdos_empty(new_inode)
@@ -670,8 +621,6 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
                                : 0;
                if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
                if (error) {
-                       iput(new_inode);
-                       iput(old_inode);
                        fat_brelse(sb, new_bh);
                        return error;
                }
@@ -690,10 +639,8 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
                free_de->name[0] = DELETED_FLAG;
 /*  Don't mark free_bh as dirty. Both states are supposed to be equivalent. */
                fat_brelse(sb, free_bh);
-               if (exists) {
-                       iput(new_inode);
+               if (exists)
                        fat_brelse(sb, new_bh);
-               }
                return -EIO;
        }
        if (exists && S_ISDIR(new_inode->i_mode)) {
@@ -715,7 +662,6 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
                /* Two references now exist to free_inode so increase count */
                free_inode->i_count++;
                /* free_inode is put after putting new_inode and old_inode */
-               iput(new_inode);
                fat_brelse(sb, new_bh);
        }
        if (S_ISDIR(old_inode->i_mode)) {
@@ -737,16 +683,18 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
                iput(dotdot_inode);
                fat_brelse(sb, dotdot_bh);
        }
+       /* Update the dcache */
+       d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
+       d_delete(new_dentry);
        error = 0;
 rename_done:
        fat_brelse(sb, free_bh);
-       iput(old_inode);
        return error;
 }
 
 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
-int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
-                struct inode *new_dir,const char *new_name,int new_len)
+int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
+                struct inode *new_dir,struct dentry *new_dentry)
 {
        struct super_block *sb = old_dir->i_sb;
        char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME];
@@ -756,28 +704,30 @@ int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
        int is_hid,old_hid; /* if new file and old file are hidden */
 
        if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->options.name_check,
-                                      old_name,old_len,old_msdos_name,1,
+                                      old_dentry->d_name.name,
+                                      old_dentry->d_name.len,old_msdos_name,1,
                                       MSDOS_SB(old_dir->i_sb)->options.dotsOK))
            < 0) goto rename_done;
        if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->options.name_check,
-                                      new_name,new_len,new_msdos_name,0,
+                                      new_dentry->d_name.name,
+                                      new_dentry->d_name.len,new_msdos_name,0,
                                       MSDOS_SB(new_dir->i_sb)->options.dotsOK))
            < 0) goto rename_done;
-       is_hid = (new_name[0]=='.') && (new_msdos_name[0]!='.');
-       old_hid = (old_name[0]=='.') && (old_msdos_name[0]!='.');
+       is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
+       old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.');
        if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de,
            &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done;
        fat_lock_creation();
        if (old_dir == new_dir)
-               error = rename_same_dir(old_dir,old_msdos_name,old_len,new_dir,
-                   new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid);
-       else error = rename_diff_dir(old_dir,old_msdos_name,old_len,new_dir,
-                   new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid);
+               error = rename_same_dir(old_dir,old_msdos_name,old_dentry,
+                   new_dir,new_msdos_name,new_dentry,
+                   old_bh,old_de,old_ino,is_hid);
+       else error = rename_diff_dir(old_dir,old_msdos_name,old_dentry,
+                    new_dir,new_msdos_name,new_dentry,
+                    old_bh,old_de,old_ino,is_hid);
        fat_unlock_creation();
        fat_brelse(sb, old_bh);
 rename_done:
-       iput(old_dir);
-       iput(new_dir);
        return error;
 }
 
index 2ec173f0d3f8dad182dd6bb7158291af5ae71139..8870e3a99679ef1bcaed0b2b5a4bdd10ed12ecd4 100644 (file)
@@ -338,25 +338,38 @@ done_error:
        return result;
 }
 
-/*
- * This should check "link_count", but doesn't do that yet..
- */
 static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry)
 {
        struct inode * inode = dentry->d_inode;
 
        if (inode && inode->i_op && inode->i_op->follow_link) {
-               struct dentry *result;
+               if (current->link_count < 5) {
+                       struct dentry * result;
 
-               /* This eats the base */
-               result = inode->i_op->follow_link(inode, base);
-               base = dentry;
-               dentry = result;
+                       current->link_count++;
+                       /* This eats the base */
+                       result = inode->i_op->follow_link(inode, base);
+                       current->link_count--;
+                       dput(dentry);
+                       return result;
+               }
+               dput(dentry);
+               dentry = ERR_PTR(-ELOOP);
        }
        dput(base);
        return dentry;
 }
 
+/*
+ * Allow a filesystem to translate the character set of
+ * a file name. This allows for filesystems that are not
+ * case-sensitive, for example.
+ *
+ * This is only a dummy define right now, but eventually
+ * it might become something like "(parent)->d_charmap[c]"
+ */
+#define name_translate_char(parent, c) (c)
+
 /*
  * Name resolution.
  *
@@ -397,6 +410,7 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo
                c = *name;
                do {
                        len++; name++;
+                       c = name_translate_char(base, c);
                        hash = partial_name_hash(c, hash);
                        c = *name;
                } while (c && (c != '/'));
index 9241c679e9841f7509d93888e89342656e3f311f..449fc0d4e58f4bb358fb2c0c84c4d96cbdac7dea 100644 (file)
@@ -424,7 +424,6 @@ nfs_writepage(struct inode *inode, struct page *page)
 
 /*
  * Update and possibly write a cached page of an NFS file.
- * The page is already locked when we get here.
  *
  * XXX: Keep an eye on generic_file_read to make sure it doesn't do bad
  * things with a page scheduled for an RPC call (e.g. invalidate it).
@@ -441,6 +440,7 @@ nfs_updatepage(struct inode *inode, struct page *page, const char *buffer,
                                inode->i_dev, inode->i_ino,
                                count, page->offset+offset, sync);
 
+       set_bit(PG_locked, &page->flags);
        page_addr = (u8 *) page_address(page);
 
        /* If wsize is smaller than page size, update and write
index c83150b5fc33e5cb58d1e680a5bbaea1711df30f..2214835870caa67e905f72767503541aaa36d5a4 100644 (file)
@@ -89,10 +89,26 @@ exp_get(svc_client *clp, dev_t dev, ino_t ino)
        return (exp && exp->ex_ino == ino)? exp : NULL;
 }
 
+/*
+ * Look up the root inode of the parent fs.
+ * We have to go through iget in order to allow for wait_on_inode.
+ */
+static inline int
+nfsd_parentdev(dev_t *devp)
+{
+       struct super_block      *sb;
+
+       if (!(sb = get_super(*devp)) || !sb->s_root->d_covers)
+               return 0;
+       if (*devp == sb->s_root->d_covers->d_inode->i_dev)
+               return 0;
+       *devp = sb->s_root->d_covers->d_inode->i_dev;
+       return 1;
+}
+
 /*
  * Find the parent export entry for a given fs. This function is used
  * only by the export syscall to keep the export tree consistent.
- * nfsd_parentdev(dev) returns the device on which dev is mounted.
  */
 static svc_export *
 exp_parent(svc_client *clp, dev_t dev)
@@ -118,6 +134,7 @@ exp_export(struct nfsctl_export *nxp)
        svc_client      *clp;
        svc_export      *exp, *parent;
        svc_export      **head;
+       struct dentry   *dentry = NULL;
        struct inode    *inode = NULL;
        int             i, err;
        dev_t           dev;
@@ -128,7 +145,7 @@ exp_export(struct nfsctl_export *nxp)
            !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
                return -EINVAL;
 
-       dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
+       dprintk("exp_export called for %s:%s (%x/%d fl %x).\n",
                        nxp->ex_client, nxp->ex_path,
                        nxp->ex_dev, nxp->ex_ino, nxp->ex_flags);
        dev = nxp->ex_dev;
@@ -161,8 +178,19 @@ exp_export(struct nfsctl_export *nxp)
                goto finish;
        }
 
-       /* Look up the inode */
-       if (!(inode = nfsd_iget(nxp->ex_dev, nxp->ex_ino))) {
+       /* Look up the dentry */
+       dentry = lookup_dentry(nxp->ex_path, NULL, 0);
+       if (IS_ERR(dentry)) {
+               err = -EINVAL;
+               goto finish;
+       }
+       inode = dentry->d_inode;
+       if(!inode) {
+               err = -ENOENT;
+               goto finish;
+       }
+       if(inode->i_dev != nxp->ex_dev || inode->i_ino != nxp->ex_ino) {
+               /* I'm just being paranoid... */
                err = -EINVAL;
                goto finish;
        }
@@ -175,9 +203,9 @@ exp_export(struct nfsctl_export *nxp)
 
        /* If this is a sub-export, must be root of FS */
        if ((parent = exp_parent(clp, dev)) != NULL) {
-               struct super_block *sb;
+               struct super_block *sb = inode->i_sb;
 
-               if ((sb = inode->i_sb) && (inode != sb->s_mounted)) {
+               if (sb && (inode != sb->s_root->d_inode)) {
                        err = -EINVAL;
                        goto finish;
                }
@@ -192,7 +220,7 @@ exp_export(struct nfsctl_export *nxp)
        strcpy(exp->ex_path, nxp->ex_path);
        exp->ex_client = clp;
        exp->ex_parent = parent;
-       exp->ex_inode = inode;
+       exp->ex_dentry = dentry;
        exp->ex_flags = nxp->ex_flags;
        exp->ex_dev = dev;
        exp->ex_ino = ino;
@@ -219,9 +247,10 @@ exp_export(struct nfsctl_export *nxp)
        err = 0;
 
 finish:
-       /* Release inode */
-       if (err < 0 && inode)
-               iput(inode);
+       /* Release dentry */
+       if (err < 0 && dentry)
+               dput(dentry);
+
        /* Unlock hashtable */
        exp_unlock();
        return err;
@@ -236,6 +265,7 @@ exp_do_unexport(svc_export *unexp)
 {
        svc_export      *exp;
        svc_client      *clp;
+       struct dentry   *dentry;
        struct inode    *inode;
        int             i;
 
@@ -247,11 +277,12 @@ exp_do_unexport(svc_export *unexp)
                                exp->ex_parent = unexp->ex_parent;
        }
 
-       inode = unexp->ex_inode;
+       dentry = unexp->ex_dentry;
+       inode = dentry->d_inode;
        if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino)
-               printk(KERN_WARNING "nfsd: bad inode in unexport!\n");
+               printk(KERN_WARNING "nfsd: bad dentry in unexport!\n");
        else
-               iput(unexp->ex_inode);
+               dput(dentry);
 
        kfree(unexp);
 }
@@ -326,13 +357,28 @@ exp_rootfh(struct svc_client *clp, dev_t dev, ino_t ino, struct knfs_fh *f)
 {
        struct svc_export       *exp = NULL;
        struct svc_fh           fh;
+       struct dentry           *dentry;
+       struct inode            *inode;
 
-       dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, ino);
+       dprintk("nfsd: exp_rootfh(%s:%x/%d)\n", clp->cl_ident, dev, ino);
 
        if (!(exp = exp_get(clp, dev, ino)))
                return -EPERM;
-       exp->ex_inode->i_count++;
-       fh_compose(&fh, exp, exp->ex_inode);
+
+       dentry = exp->ex_dentry;
+       inode = dentry->d_inode;
+       if(!inode) {
+               printk("exp_rootfh: Aieee, NULL d_inode\n");
+               return -EPERM;
+       }
+       if(inode->i_dev != dev || inode->i_ino != ino) {
+               printk("exp_rootfh: Aieee, ino/dev mismatch\n");
+               printk("exp_rootfh: arg[dev(%x):ino(%d)] inode[dev(%x):ino(%ld)]\n",
+                      dev, ino, inode->i_dev, inode->i_ino);
+       }
+
+       dget(dentry);
+       fh_compose(&fh, exp, dentry);
        memcpy(f, &fh.fh_handle, sizeof(struct knfs_fh));
        fh_put(&fh);
 
@@ -613,25 +659,6 @@ exp_verify_string(char *cp, int max)
        return 0;
 }
 
-#if 0
-/*
- * Get the inode associated with a pathname. Used by exp_export.
- */
-struct inode *
-exp_lnamei(char *pathname, int *errp)
-{
-       struct inode    *inode;
-       unsigned long   oldfs;
-
-       oldfs = get_fs();
-       set_fs(KERNEL_DS);
-       *errp = lnamei(pathname, &inode);
-       set_fs(oldfs);
-
-       return inode;
-}
-#endif
-
 /*
  * Initialize the exports module.
  */
@@ -648,7 +675,6 @@ nfsd_export_init(void)
        clients = NULL;
 
        initialized = 1;
-       return;
 }
 
 /*
@@ -672,6 +698,4 @@ nfsd_export_shutdown(void)
        }
        exp_unlock();
        dprintk("nfsd: export shutdown complete.\n");
-
-       return;
 }
index 3732ebcce674a80f95e1c176ad323c77866188ac..6e161d62b4f1c465bbc2e97d1963f65685727a56 100644 (file)
@@ -24,7 +24,7 @@ nlm_fopen(struct svc_rqst *rqstp, struct knfs_fh *f, struct file *filp)
 
        fh.fh_handle = *f;
        fh.fh_export = NULL;
-       fh.fh_inode  = NULL;
+       fh.fh_dverified = 0;
 
        nfserr = nfsd_open(rqstp, &fh, S_IFREG, 0, filp);
        fh_put(&fh);
index fb1c0878b697b1fb113d2532d1f7c780c6159b76..c748e118d1d785afe4114eca6f7fa341df9c9a4c 100644 (file)
@@ -58,7 +58,7 @@ nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
                                SVCFH_INO(&argp->fh));
 
        resp->fh = argp->fh;
-       nfserr = fh_lookup(rqstp, &resp->fh, 0, MAY_NOP);
+       nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
        RETURN(nfserr);
 }
 
@@ -227,7 +227,7 @@ nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
        attr   = &argp->attrs;
 
        /* Get the directory inode */
-       nfserr = fh_lookup(rqstp, dirfhp, S_IFDIR, MAY_CREATE);
+       nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_CREATE);
        if (nfserr)
                RETURN(nfserr);
 
@@ -241,8 +241,7 @@ nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
 
        /* Now create the file and set attributes */
        nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
-                               attr, S_IFREG, 0, newfhp,
-                               argp->createmode);
+                               attr, S_IFREG, 0, newfhp);
 
        RETURN(nfserr);
 }
index 88b69cb405d87ff16c9544e20cd1eee76d18a336..1db374bc637b48f7750ffe4dede0781668a70ea1 100644 (file)
@@ -133,9 +133,10 @@ nfsctl_getfh(struct nfsctl_fhparm *data, struct knfs_fh *res)
 #endif
 
 int
-asmlinkage handle_sys_nfsservctl(int cmd, struct nfsctl_arg *argp,
-                                union nfsctl_res *resp)
+asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
 {
+       struct nfsctl_arg *     argp = opaque_argp;
+       union nfsctl_res *      resp = opaque_resp;
        struct nfsctl_arg *     arg = NULL;
        union nfsctl_res *      res = NULL;
        int                     err;
index 526a4455bf7274b64f9859bbd4bc23093434409a..3ea0ee988ef8d78ee3f777523b35945a162d6e1b 100644 (file)
 #define NFSDDBG_FACILITY               NFSDDBG_FH
 
 /*
- * Get the inode version number
- */
-static inline int
-nfsd_iversion(struct inode *inode)
-{
-       if (inode->i_sb->s_magic == EXT2_SUPER_MAGIC)
-               return inode->u.ext2_i.i_version;
-       return 0;
-}
-
-/*
- * Get the inode given a file handle.
+ * Perform sanity checks on the dentry in a client's file handle.
  */
 u32
-fh_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
+fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
 {
        struct svc_export *exp;
+       struct dentry   *dentry;
        struct inode    *inode;
        struct knfs_fh  *fh = &fhp->fh_handle;
 
-       /* Already checked */
-       if (fhp->fh_inode)
+       if(fhp->fh_dverified)
                return 0;
 
-       dprintk("nfsd: fh_lookup(exp %x/%ld fh %x/%ld)\n",
-                       fh->fh_xdev, fh->fh_xino, fh->fh_dev, fh->fh_ino);
+       dprintk("nfsd: fh_lookup(exp %x/%d fh %p)\n",
+                       fh->fh_xdev, fh->fh_xino, fh->fh_dentry);
 
-       /* Make sure that clients don't cheat */
-       if (fh->fh_dev != fh->fh_xdev) {
-               printk(KERN_NOTICE "nfsd: fh with bad dev fields "
-                               "(%x != %x) from %08lx:%d\n",
-                               fh->fh_dev, fh->fh_xdev,
-                               ntohl(rqstp->rq_addr.sin_addr.s_addr),
-                               ntohs(rqstp->rq_addr.sin_port));
-               return nfserr_perm;
-       }
-
-       /* Look up the export entry */
+       /* Look up the export entry. */
        exp = exp_get(rqstp->rq_client, fh->fh_xdev, fh->fh_xino);
        if (!exp) {
                /* nfsdstats.fhstale++; */
@@ -71,22 +50,26 @@ fh_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
                return nfserr_perm;
        }
 
-       /* Set user creds if we haven't done so already */
+       /* Set user creds if we haven't done so already. */
        nfsd_setuser(rqstp, exp);
 
-       /* Get the inode */
-       if (!(inode = nfsd_iget(fh->fh_dev, fh->fh_ino))
-        || !inode->i_nlink || fh->fh_version != nfsd_iversion(inode)) {
-               if (inode)
-                       iput(inode);
+       dentry = fh->fh_dentry;
+
+       if(!d_validate(dentry, fh->fh_dparent, fh->fh_dhash, fh->fh_dlen) ||
+          !(inode = dentry->d_inode) ||
+          !inode->i_nlink) {
+               /* Currently we cannot tell the difference between
+                * a bogus pointer and a true unlink between fh
+                * uses.  But who cares about accurate error reporting
+                * to buggy/malicious clients... -DaveM
+                */
+
                /* nfsdstats.fhstale++; */
-               return nfserr_stale;    /* unlinked in the meanwhile */
+               return nfserr_stale;
        }
 
-       /* This is basically what wait_on_inode does */
-       while (inode->i_lock)
-               sleep_on(&inode->i_wait);
-       fhp->fh_inode  = inode;
+       dget(dentry);
+       fhp->fh_dverified = 1;
        fhp->fh_export = exp;
 
        /* Type check. The correct error return for type mismatches
@@ -100,25 +83,28 @@ fh_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
        if (type < 0 && (inode->i_mode & S_IFMT) == -type)
                return (type == -S_IFDIR)? nfserr_notdir : nfserr_isdir;
 
-       /* Finally, check access permissions */
-       return nfsd_permission(fhp->fh_export, inode, access);
+       /* Finally, check access permissions. */
+       return nfsd_permission(fhp->fh_export, dentry, access);
 }
 
 /*
  * Compose file handle for NFS reply.
  */
 void
-fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct inode *inode)
+fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
 {
-       dprintk("nfsd: fh_compose(exp %x/%ld fh %x/%ld)\n",
-                       exp->ex_dev, exp->ex_ino, inode->i_dev, inode->i_ino);
+       dprintk("nfsd: fh_compose(exp %x/%d dentry %p)\n",
+                       exp->ex_dev, exp->ex_ino, dentry);
 
        fh_init(fhp);                   /* initialize empty fh */
-       fhp->fh_inode = inode;
-       fhp->fh_export = exp;
-       fhp->fh_handle.fh_dev = inode->i_dev;
-       fhp->fh_handle.fh_ino = inode->i_ino;
+       fhp->fh_handle.fh_dentry = dentry;
+       fhp->fh_handle.fh_dparent = dentry->d_parent;
+       fhp->fh_handle.fh_dhash = dentry->d_name.hash;
+       fhp->fh_handle.fh_dlen = dentry->d_name.len;
        fhp->fh_handle.fh_xdev = exp->ex_dev;
        fhp->fh_handle.fh_xino = exp->ex_ino;
-       fhp->fh_handle.fh_version = nfsd_iversion(inode);
+       fhp->fh_export = exp;
+
+       /* We stuck it there, we know it's good. */
+       fhp->fh_dverified = 1;
 }
index 05b13deab7f240c1b9cfd50c65edf17af255eb46..5b6e6cdf109ab3d95a4d9784d7c7eb041084ed38 100644 (file)
@@ -55,12 +55,10 @@ static int
 nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
                                          struct nfsd_attrstat *resp)
 {
-       dprintk("nfsd: GETATTR  %x/%ld\n",
-                               SVCFH_DEV(&argp->fh),
-                               SVCFH_INO(&argp->fh));
+       dprintk("nfsd: GETATTR  %p\n", SVCFH_DENTRY(&argp->fh));
 
        fh_copy(&resp->fh, &argp->fh);
-       RETURN(fh_lookup(rqstp, &resp->fh, 0, MAY_NOP));
+       RETURN(fh_verify(rqstp, &resp->fh, 0, MAY_NOP));
 }
 
 /*
@@ -70,9 +68,7 @@ static int
 nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
                                          struct nfsd_attrstat  *resp)
 {
-       dprintk("nfsd: SETATTR  %x/%ld\n",
-                               SVCFH_DEV(&argp->fh),
-                               SVCFH_INO(&argp->fh));
+       dprintk("nfsd: SETATTR  %p\n", SVCFH_DENTRY(&argp->fh));
 
        fh_copy(&resp->fh, &argp->fh);
        RETURN(nfsd_setattr(rqstp, &resp->fh, &argp->attrs));
@@ -87,10 +83,7 @@ nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
 {
        int     nfserr;
 
-       dprintk("nfsd: LOOKUP   %x/%ld %s\n",
-                               SVCFH_DEV(&argp->fh),
-                               SVCFH_INO(&argp->fh),
-                               argp->name);
+       dprintk("nfsd: LOOKUP   %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
 
        nfserr = nfsd_lookup(rqstp, &argp->fh,
                                    argp->name,
@@ -111,9 +104,7 @@ nfsd_proc_readlink(struct svc_rqst *rqstp, struct nfsd_fhandle     *argp,
        u32             *path;
        int             dummy, nfserr;
 
-       dprintk("nfsd: READLINK %x/%ld\n",
-                               SVCFH_DEV(&argp->fh),
-                               SVCFH_INO(&argp->fh));
+       dprintk("nfsd: READLINK %p\n", SVCFH_DENTRY(&argp->fh));
 
        /* Reserve room for status and path length */
        svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy, 2);
@@ -136,10 +127,9 @@ nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp,
        u32 *   buffer;
        int     nfserr, avail;
 
-       dprintk("nfsd: READ %x/%ld %d bytes at %d\n",
-                               SVCFH_DEV(&argp->fh),
-                               SVCFH_INO(&argp->fh),
-                               argp->count, argp->offset);
+       dprintk("nfsd: READ %p %d bytes at %d\n",
+               SVCFH_DENTRY(&argp->fh),
+               argp->count, argp->offset);
 
        /* Obtain buffer pointer for payload. 19 is 1 word for
         * status, 17 words for fattr, and 1 word for the byte count.
@@ -173,10 +163,9 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
 {
        int     nfserr;
 
-       dprintk("nfsd: WRITE    %x/%ld %d bytes at %d\n",
-                               SVCFH_DEV(&argp->fh),
-                               SVCFH_INO(&argp->fh),
-                               argp->len, argp->offset);
+       dprintk("nfsd: WRITE    %p %d bytes at %d\n",
+               SVCFH_DENTRY(&argp->fh),
+               argp->len, argp->offset);
 
        nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
                                   argp->offset,
@@ -204,23 +193,22 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
        int             rdonly = 0, exists;
        dev_t           rdev = NODEV;
 
-       dprintk("nfsd: CREATE   %x/%ld %s\n",
-                               SVCFH_DEV(&argp->fh),
-                               SVCFH_INO(&argp->fh),
-                               argp->name);
+       dprintk("nfsd: CREATE   %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
 
        dirfhp = &argp->fh;
        newfhp = &resp->fh;
        attr = &argp->attrs;
 
        /* Get the directory inode */
-       nfserr = fh_lookup(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
+       nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
        if (nfserr)
                RETURN(nfserr);
-       dirp = dirfhp->fh_inode;
+       dirp = dirfhp->fh_handle.fh_dentry->d_inode;
 
        /* Check for MAY_WRITE separately. */
-       nfserr = nfsd_permission(dirfhp->fh_export, dirp, MAY_WRITE);
+       nfserr = nfsd_permission(dirfhp->fh_export,
+                                dirfhp->fh_handle.fh_dentry,
+                                MAY_WRITE);
        if (nfserr == nfserr_rofs) {
                rdonly = 1;     /* Non-fatal error for echo > /dev/null */
        } else if (nfserr) {
@@ -230,7 +218,16 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
 
        /* First, check if the file already exists.  */
        exists = !nfsd_lookup(rqstp, dirfhp, argp->name, argp->len, newfhp);
-       inode = newfhp->fh_inode;
+
+       if (newfhp->fh_dverified)
+               inode = newfhp->fh_handle.fh_dentry->d_inode;
+
+       /* Get rid of this soon... */
+       if (exists && !inode) {
+               printk("nfsd_proc_create: Wheee... exists but d_inode==NULL\n");
+               nfserr = nfserr_rofs;
+               goto done;
+       }               
 
        /* Unfudge the mode bits */
        if (attr->ia_valid & ATTR_MODE) { 
@@ -311,10 +308,7 @@ nfsd_proc_remove(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
 {
        int     nfserr;
 
-       dprintk("nfsd: REMOVE   %x/%ld %s\n",
-                               SVCFH_DEV(&argp->fh),
-                               SVCFH_INO(&argp->fh),
-                               argp->name);
+       dprintk("nfsd: REMOVE   %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
 
        /* Unlink. -SIFDIR means file must not be a directory */
        nfserr = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, argp->name, argp->len);
@@ -328,13 +322,9 @@ nfsd_proc_rename(struct svc_rqst *rqstp, struct nfsd_renameargs *argp,
 {
        int     nfserr;
 
-       dprintk("nfsd: RENAME   %x/%ld %s -> %x/%ld %s\n",
-                               SVCFH_DEV(&argp->ffh),
-                               SVCFH_INO(&argp->ffh),
-                               argp->fname,
-                               SVCFH_DEV(&argp->tfh),
-                               SVCFH_INO(&argp->tfh),
-                               argp->tname);
+       dprintk("nfsd: RENAME   %p %s -> %p %s\n",
+               SVCFH_DENTRY(&argp->ffh), argp->fname,
+               SVCFH_DENTRY(&argp->tfh), argp->tname);
 
        nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen,
                                    &argp->tfh, argp->tname, argp->tlen);
@@ -349,12 +339,10 @@ nfsd_proc_link(struct svc_rqst *rqstp, struct nfsd_linkargs *argp,
 {
        int     nfserr;
 
-       dprintk("nfsd: LINK     %x/%ld -> %x/%ld %s\n",
-                               SVCFH_DEV(&argp->ffh),
-                               SVCFH_INO(&argp->ffh),
-                               SVCFH_DEV(&argp->tfh),
-                               SVCFH_INO(&argp->tfh),
-                               argp->tname);
+       dprintk("nfsd: LINK     %p -> %p %s\n",
+               SVCFH_DENTRY(&argp->ffh),
+               SVCFH_DENTRY(&argp->tfh),
+               argp->tname);
 
        nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen,
                                  &argp->ffh);
@@ -370,10 +358,9 @@ nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp,
        struct svc_fh   newfh;
        int             nfserr;
 
-       dprintk("nfsd: SYMLINK  %x/%ld %s -> %s\n",
-                               SVCFH_DEV(&argp->ffh),
-                               SVCFH_INO(&argp->ffh),
-                               argp->fname, argp->tname);
+       dprintk("nfsd: SYMLINK  %p %s -> %s\n",
+               SVCFH_DENTRY(&argp->ffh),
+               argp->fname, argp->tname);
 
        /*
         * Create the link, look up new file and set attrs.
@@ -381,7 +368,7 @@ nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp,
        nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
                                                 argp->tname, argp->tlen,
                                                 &newfh);
-       if (nfserr)
+       if (!nfserr)
                nfserr = nfsd_setattr(rqstp, &newfh, &argp->attrs);
 
        fh_put(&argp->ffh);
@@ -398,11 +385,11 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
 {
        int     nfserr;
 
-       dprintk("nfsd: MKDIR    %x/%ld %s\n",
-                               SVCFH_DEV(&argp->fh),
-                               SVCFH_INO(&argp->fh),
-                               argp->name);
+       dprintk("nfsd: MKDIR    %p %s\n",
+               SVCFH_DENTRY(&argp->fh),
+               argp->name);
 
+       resp->fh.fh_dverified = 0; /* paranoia */
        nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
                                    &argp->attrs, S_IFDIR, 0, &resp->fh);
        fh_put(&argp->fh);
@@ -418,10 +405,7 @@ nfsd_proc_rmdir(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
 {
        int     nfserr;
 
-       dprintk("nfsd: RMDIR    %x/%ld %s\n",
-                               SVCFH_DEV(&argp->fh),
-                               SVCFH_INO(&argp->fh),
-                               argp->name);
+       dprintk("nfsd: RMDIR    %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
 
        nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
        fh_put(&argp->fh);
@@ -438,10 +422,9 @@ nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp,
        u32 *   buffer;
        int     nfserr, count;
 
-       dprintk("nfsd: READDIR  %x/%ld %d bytes at %d\n",
-                               SVCFH_DEV(&argp->fh),
-                               SVCFH_INO(&argp->fh),
-                               argp->count, argp->cookie);
+       dprintk("nfsd: READDIR  %p %d bytes at %d\n",
+               SVCFH_DENTRY(&argp->fh),
+               argp->count, argp->cookie);
 
        /* Reserve buffer space for status */
        svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1);
@@ -470,9 +453,7 @@ nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle   *argp,
 {
        int     nfserr;
 
-       dprintk("nfsd: STATFS   %x/%ld\n",
-                               SVCFH_DEV(&argp->fh),
-                               SVCFH_INO(&argp->fh));
+       dprintk("nfsd: STATFS   %p\n", SVCFH_DENTRY(&argp->fh));
 
        nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
        fh_put(&argp->fh);
@@ -488,34 +469,34 @@ nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle   *argp,
 struct nfsd_void { int dummy; };
 
 #define PROC(name, argt, rest, relt, cache)    \
- { (svc_procfunc) nfsd_proc_##name,    \
-   (kxdrproc_t) nfssvc_decode_##argt,  \
-   (kxdrproc_t) nfssvc_encode_##rest,  \
-   (kxdrproc_t) nfssvc_release_##relt, \
-   sizeof(struct nfsd_##argt),         \
-   sizeof(struct nfsd_##rest),         \
-   0,                                  \
-   cache                               \
+ { (svc_procfunc) nfsd_proc_##name,            \
+   (kxdrproc_t) nfssvc_decode_##argt,          \
+   (kxdrproc_t) nfssvc_encode_##rest,          \
+   (kxdrproc_t) nfssvc_release_##relt,         \
+   sizeof(struct nfsd_##argt),                 \
+   sizeof(struct nfsd_##rest),                 \
+   0,                                          \
+   cache                                       \
  }
 struct svc_procedure           nfsd_procedures2[18] = {
-  PROC(null,    void,          void,           none,    RC_NOCACHE),
-  PROC(getattr,         fhandle,       attrstat,       fhandle, RC_NOCACHE),
-  PROC(setattr,  sattrargs,    attrstat,       fhandle, RC_REPLBUFF),
-  PROC(none,    void,          void,           none,    RC_NOCACHE),
-  PROC(lookup,  diropargs,     diropres,       fhandle, RC_NOCACHE),
-  PROC(readlink, fhandle,      readlinkres,    none,    RC_NOCACHE),
-  PROC(read,    readargs,      readres,        fhandle, RC_NOCACHE),
-  PROC(none,    void,          void,           none,    RC_NOCACHE),
-  PROC(write,   writeargs,     attrstat,       fhandle, RC_REPLBUFF),
-  PROC(create,  createargs,    diropres,       fhandle, RC_REPLBUFF),
-  PROC(remove,  diropargs,     void,           none,    RC_REPLSTAT),
-  PROC(rename,  renameargs,    void,           none,    RC_REPLSTAT),
-  PROC(link,    linkargs,      void,           none,    RC_REPLSTAT),
-  PROC(symlink,         symlinkargs,   void,           none,    RC_REPLSTAT),
-  PROC(mkdir,   createargs,    diropres,       fhandle, RC_REPLBUFF),
-  PROC(rmdir,   diropargs,     void,           none,    RC_REPLSTAT),
-  PROC(readdir,         readdirargs,   readdirres,     none,    RC_REPLSTAT),
-  PROC(statfs,  fhandle,       statfsres,      none,    RC_NOCACHE),
+  PROC(null,    void,          void,           none,           RC_NOCACHE),
+  PROC(getattr,         fhandle,       attrstat,       fhandle,        RC_NOCACHE),
+  PROC(setattr,  sattrargs,    attrstat,       fhandle,        RC_REPLBUFF),
+  PROC(none,    void,          void,           none,           RC_NOCACHE),
+  PROC(lookup,  diropargs,     diropres,       fhandle,        RC_NOCACHE),
+  PROC(readlink, fhandle,      readlinkres,    none,           RC_NOCACHE),
+  PROC(read,    readargs,      readres,        fhandle,        RC_NOCACHE),
+  PROC(none,    void,          void,           none,           RC_NOCACHE),
+  PROC(write,   writeargs,     attrstat,       fhandle,        RC_REPLBUFF),
+  PROC(create,  createargs,    diropres,       fhandle,        RC_REPLBUFF),
+  PROC(remove,  diropargs,     void,           none,           RC_REPLSTAT),
+  PROC(rename,  renameargs,    void,           none,           RC_REPLSTAT),
+  PROC(link,    linkargs,      void,           none,           RC_REPLSTAT),
+  PROC(symlink,         symlinkargs,   void,           none,           RC_REPLSTAT),
+  PROC(mkdir,   createargs,    diropres,       fhandle,        RC_REPLBUFF),
+  PROC(rmdir,   diropargs,     void,           none,           RC_REPLSTAT),
+  PROC(readdir,         readdirargs,   readdirres,     none,           RC_REPLSTAT),
+  PROC(statfs,  fhandle,       statfsres,      none,           RC_NOCACHE),
 };
 
 
index 34b9ff3f2890dcef41c79f4d6f6ef81123acee9e..cec3ba7f319a316c9ae9e5eae3d3928bced4732e 100644 (file)
@@ -363,7 +363,7 @@ int
 nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
                                        struct nfsd_attrstat *resp)
 {
-       if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+       if (!(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode)))
                return 0;
        return xdr_ressize_check(rqstp, p);
 }
@@ -373,7 +373,7 @@ nfssvc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
                                        struct nfsd_diropres *resp)
 {
        if (!(p = encode_fh(p, &resp->fh))
-        || !(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+        || !(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode)))
                return 0;
        return xdr_ressize_check(rqstp, p);
 }
@@ -391,7 +391,7 @@ int
 nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p,
                                        struct nfsd_readres *resp)
 {
-       if (!(p = encode_fattr(rqstp, p, resp->fh.fh_inode)))
+       if (!(p = encode_fattr(rqstp, p, resp->fh.fh_handle.fh_dentry->d_inode)))
                return 0;
        *p++ = htonl(resp->count);
        p += XDR_QUADLEN(resp->count);
index b6fdb460d5e06ffbd9e87df91f7b5599bbb8cca1..4fa6c08fb8fa4d193b8a6b1e2d69640915f52599 100644 (file)
@@ -5,12 +5,11 @@
  * other parts of the kernel because they weren't in ksyms.c, others
  * are partial duplicates with added or changed functionality.
  *
- * Note that several functions lock the inode upon which they want
- * to act, most notably those that create directory entries. The
- * unlock operation can take place either by calling fh_unlock within
- * the function directly, or at a later time in fh_put(). So if you
- * notice code paths that apparently fail to unlock the inode, don't
- * worry--they have been taken care of.
+ * Note that several functions dget() the dentry upon which they want
+ * to act, most notably those that create directory entries. Response
+ * dentry's are dput()'d if necessary in the release callback.
+ * So if you notice code paths that apparently fail to dput() the
+ * dentry, don't worry--they have been taken care of.
  *
  * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
  */
  * add a hash table here.
  */
 struct raparms {
-       struct raparms *        p_next;
+       struct raparms          *p_next;
        unsigned int            p_count;
-       dev_t                   p_dev;
-       ino_t                   p_ino;
+       struct dentry           *p_dentry;
        unsigned long           p_reada,
                                p_ramax,
                                p_raend,
@@ -85,12 +83,14 @@ fs_off_limits(struct super_block *sb)
 }
 
 /*
- * Check whether directory is a mount point
+ * Check whether directory is a mount point, but it is alright if
+ * this is precisely the local mount point being exported.
  */
 static inline int
-nfsd_iscovered(struct inode *inode)
+nfsd_iscovered(struct dentry *dentry, struct svc_export *exp)
 {
-       return inode->i_mount != NULL;
+       return (dentry != dentry->d_covers &&
+               dentry != exp->ex_dentry);
 }
 
 /*
@@ -101,72 +101,40 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
                                        int len, struct svc_fh *resfh)
 {
        struct svc_export       *exp;
-       struct super_block      *sb;
-       struct inode            *dirp, *inode;
-       int                     perm, err, dotdot = 0;
+       struct dentry           *dparent;
+       int                     err;
 
-       dprintk("nfsd: nfsd_lookup(fh %x/%ld, %s)\n",
-                       SVCFH_DEV(fhp), SVCFH_INO(fhp), name);
+       dprintk("nfsd: nfsd_lookup(fh %p, %s)\n", SVCFH_DENTRY(fhp), name);
 
-       /* Obtain inode and export */
-       if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_NOP)) != 0)
+       /* Obtain dentry and export. */
+       if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_NOP)) != 0)
                return err;
-       dirp = fhp->fh_inode;
-       exp  = fhp->fh_export;
 
-       /* check permissions before traversing mount-points */
-       perm = nfsd_permission(exp, dirp, MAY_EXEC);
-
-       dotdot = (len == 2 && name[0] == '.' && name[1] == '.');
-       if (dotdot) {
-               if (dirp == current->fs->root) {
-                       dirp->i_count++;
-                       *resfh = *fhp;
-                       return 0;
-               }
-
-               if (dirp->i_dev == exp->ex_dev && dirp->i_ino == exp->ex_ino) {
-                       dirp->i_count++;
-                       *resfh = *fhp;
-                       return 0;
-               }
-       } else if (len == 1 && name[0] == '.') {
-               len = 0;
-       } else if (fs_off_limits(dirp->i_sb)) {
-               /* No lookups on NFS mounts and procfs */
-               return nfserr_noent;
-       } else if (nfsd_iscovered(dirp)) {
-               /* broken NFS client */
-               return nfserr_acces;
-       }
-       if (!dirp->i_op || !dirp->i_op->lookup)
-               return nfserr_notdir;
-       if (perm != 0)
-               return perm;
-       if (!len) {
-               dirp->i_count++;
-               *resfh = *fhp;
-               return 0;
-       }
+       dparent = fhp->fh_handle.fh_dentry;
+       exp  = fhp->fh_export;
 
-       dirp->i_count++;                /* lookup eats the dirp inode */
-       err = dirp->i_op->lookup(dirp, name, len, &inode);
+       /* Fast path... */
+       err = nfsd_permission(exp, dparent, MAY_EXEC);
+       if ((err == 0)                                  &&
+           !fs_off_limits(dparent->d_inode->i_sb)      &&
+           !nfsd_iscovered(dparent, exp)) {
+               struct dentry *dchild;
 
-       if (err)
-               return nfserrno(-err);
+               dchild = lookup_dentry(name, dget(dparent), 1);
+               err = PTR_ERR(dchild);
+               if (IS_ERR(dchild))
+                       return nfserrno(-err);
 
-       /* Note that lookup() has already done a call to iget() so that
-        * the inode returned never refers to an inode covered by a mount.
-        * When this has happened, return the covered inode.
-        */
-       if (!dotdot && (sb = inode->i_sb) && (inode == sb->s_mounted)) {
-               iput(inode);
-               inode = sb->s_covered;
-               inode->i_count++;
+               fh_compose(resfh, exp, dchild);
+               return (dchild->d_inode ? 0 : nfserr_noent);
        }
 
-       fh_compose(resfh, exp, inode);
-       return 0;
+       /* Slow path... */
+       if (fs_off_limits(dparent->d_inode->i_sb))
+               return nfserr_noent;
+       if (nfsd_iscovered(dparent, exp))
+               return nfserr_acces;
+       return err;
 }
 
 /*
@@ -175,6 +143,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
 int
 nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
 {
+       struct dentry   *dentry;
        struct inode    *inode;
        int             accmode = MAY_SATTR;
        int             ftype = 0;
@@ -187,16 +156,16 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
                ftype = S_IFREG;
 
        /* Get inode */
-       if ((err = fh_lookup(rqstp, fhp, ftype, accmode)) != 0)
+       if ((err = fh_verify(rqstp, fhp, ftype, accmode)) != 0)
                return err;
 
-       fh_lock(fhp);                   /* lock inode */
-       inode = fhp->fh_inode;
+       dentry = fhp->fh_handle.fh_dentry;
+       inode = dentry->d_inode;
 
        /* The size case is special... */
        if ((iap->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) {
                if (iap->ia_size < inode->i_size) {
-                       err = nfsd_permission(fhp->fh_export, inode, MAY_TRUNC);
+                       err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC);
                        if (err != 0)
                                return err;
                }
@@ -205,7 +174,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
                inode->i_size = iap->ia_size;
                if (inode->i_op && inode->i_op->truncate)
                        inode->i_op->truncate(inode);
-               inode->i_dirt = 1;
+               mark_inode_dirty(inode);
                put_write_access(inode);
                iap->ia_valid &= ATTR_SIZE;
                iap->ia_valid |= ATTR_MTIME;
@@ -234,11 +203,11 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
        if (iap->ia_valid) {
                iap->ia_valid |= ATTR_CTIME;
                iap->ia_ctime = CURRENT_TIME;
-               err = nfsd_notify_change(inode, iap);
+               err = notify_change(inode, iap);
                if (err)
                        return nfserrno(-err);
                if (EX_ISSYNC(fhp->fh_export))
-                       nfsd_write_inode(inode);
+                       write_inode_now(inode);
        }
 
        return 0;
@@ -252,13 +221,16 @@ int
 nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
                        int wflag, struct file *filp)
 {
+       struct dentry   *dentry;
        struct inode    *inode;
        int             access, err;
 
        access = wflag? MAY_WRITE : MAY_READ;
-       if ((err = fh_lookup(rqstp, fhp, type, access)) != 0)
+       if ((err = fh_verify(rqstp, fhp, type, access)) != 0)
                return err;
-       inode = fhp->fh_inode;
+
+       dentry = fhp->fh_handle.fh_dentry;
+       inode = dentry->d_inode;
 
        /* Disallow access to files with the append-only bit set or
         * with mandatory locking enabled */
@@ -275,7 +247,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
        filp->f_count = 1;
        filp->f_flags = wflag? O_WRONLY : O_RDONLY;
        filp->f_mode  = wflag? FMODE_WRITE : FMODE_READ;
-       filp->f_inode = inode;
+       filp->f_dentry = dentry;
 
        if (filp->f_op->open) {
                err = filp->f_op->open(inode, filp);
@@ -291,7 +263,6 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
                }
        }
 
-       inode->i_count++;
        return 0;
 }
 
@@ -301,16 +272,17 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
 void
 nfsd_close(struct file *filp)
 {
-       struct inode    *inode;
+       struct dentry   *dentry = filp->f_dentry;
+       struct inode    *inode = dentry->d_inode;
 
-       inode = filp->f_inode;
        if (!inode->i_count)
                printk(KERN_WARNING "nfsd: inode count == 0!\n");
+       if (!dentry->d_count)
+               printk(KERN_WARNING "nfsd: wheee, dentry count == 0!\n");
        if (filp->f_op && filp->f_op->release)
                filp->f_op->release(inode, filp);
        if (filp->f_mode & FMODE_WRITE)
                put_write_access(inode);
-       iput(inode);
 }
 
 /*
@@ -326,12 +298,12 @@ nfsd_sync(struct inode *inode, struct file *filp)
  * Obtain the readahead parameters for the given file
  */
 static inline struct raparms *
-nfsd_get_raparms(dev_t dev, ino_t ino)
+nfsd_get_raparms(struct dentry *dentry)
 {
        struct raparms  *ra, **rap, **frap = NULL;
 
        for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) {
-               if (ra->p_dev != dev || ra->p_ino != ino) {
+               if (ra->p_dentry != dentry) {
                        if (ra->p_count == 0)
                                frap = rap;
                } else
@@ -342,6 +314,7 @@ nfsd_get_raparms(dev_t dev, ino_t ino)
        rap = frap;
        ra = *frap;
        memset(ra, 0, sizeof(*ra));
+       ra->p_dentry = dentry;
 found:
        if (rap != &raparm_cache) {
                *rap = ra->p_next;
@@ -361,6 +334,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf,
                                                unsigned long *count)
 {
        struct raparms  *ra;
+       struct dentry   *dentry;
        struct inode    *inode;
        struct file     file;
        unsigned long   oldfs;
@@ -368,14 +342,15 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf,
 
        if ((err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_READ, &file)) != 0)
                return err;
-       inode = file.f_inode;
+       dentry = file.f_dentry;
+       inode = dentry->d_inode;
        if (!file.f_op->read) {
                nfsd_close(&file);
                return nfserr_perm;
        }
 
        /* Get readahead parameters */
-       if ((ra = nfsd_get_raparms(inode->i_dev, inode->i_ino)) != NULL) {
+       if ((ra = nfsd_get_raparms(dentry)) != NULL) {
                file.f_reada = ra->p_reada;
                file.f_ramax = ra->p_ramax;
                file.f_raend = ra->p_raend;
@@ -385,7 +360,7 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, char *buf,
        file.f_pos = offset;
 
        oldfs = get_fs(); set_fs(KERNEL_DS);
-       err = file.f_op->read(file.f_inode, &file, buf, *count);
+       err = file.f_op->read(inode, &file, buf, *count);
        set_fs(oldfs);
 
        /* Write back readahead params */
@@ -419,6 +394,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
 {
        struct svc_export       *exp;
        struct file             file;
+       struct dentry           *dentry;
        struct inode            *inode;
        unsigned long           oldfs;
        int                     err;
@@ -432,7 +408,8 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
                return nfserr_perm;
        }
 
-       inode = fhp->fh_inode;
+       dentry = file.f_dentry;
+       inode = dentry->d_inode;
        exp   = fhp->fh_export;
 
        /*
@@ -459,7 +436,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
 
                ia.ia_valid = ATTR_MODE;
                ia.ia_mode  = inode->i_mode & ~(S_ISUID | S_ISGID);
-               nfsd_notify_change(inode, &ia);
+               notify_change(inode, &ia);
        }
 
        fh_unlock(fhp);                 /* unlock inode */
@@ -494,10 +471,10 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
 #endif
                }
 
-               if (inode->i_dirt) {
+               if (test_bit(I_DIRTY, &inode->i_state)) {
                        dprintk("nfsd: write sync %d\n", current->pid);
                        nfsd_sync(inode, &file);
-                       nfsd_write_inode(inode);
+                       write_inode_now(inode);
                }
                wake_up(&inode->i_wait);
                last_ino = inode->i_ino;
@@ -519,110 +496,120 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
                char *fname, int flen, struct iattr *iap,
                int type, dev_t rdev, struct svc_fh *resfhp)
 {
-       struct inode    *dirp, *inode = NULL;
+       struct dentry   *dentry, *dchild;
+       struct inode    *dirp;
        int             err;
 
        if (!flen)
                return nfserr_perm;
-
        if (!(iap->ia_valid & ATTR_MODE))
                iap->ia_mode = 0;
-
-       if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
+       if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
                return err;
 
-       fh_lock(fhp);                   /* lock directory */
-       dirp = fhp->fh_inode;
-       dirp->i_count++;        /* dirop eats the inode */
+       dentry = fhp->fh_handle.fh_dentry;
+       dirp = dentry->d_inode;
+
+       /* Get all the sanity checks out of the way before we lock the parent. */
+       if(!dirp->i_op || !dirp->i_op->lookup) {
+               return nfserrno(-ENOTDIR);
+       } else if(type == S_IFREG) {
+               if(!dirp->i_op->create)
+                       return nfserr_perm;
+       } else if(type == S_IFDIR) {
+               if(!dirp->i_op->mkdir)
+                       return nfserr_perm;
+       } else if((type == S_IFCHR) || (type == S_IFBLK) || (type == S_IFIFO)) {
+               if(!dirp->i_op->mknod)
+                       return nfserr_perm;
+       } else {
+               return nfserr_perm;
+       }
 
+       if(!resfhp->fh_dverified) {
+               dchild = lookup_dentry(fname, dget(dentry), 0);
+               err = PTR_ERR(dchild);
+               if(IS_ERR(dchild))
+                       return nfserrno(-err);
+       } else
+               dchild = resfhp->fh_handle.fh_dentry;
+
+       /* Looks good, lock the directory. */
+       fh_lock(fhp);
        switch (type) {
        case S_IFREG:
-               if (!dirp->i_op || !dirp->i_op->create)
-                       return nfserr_perm;
-               err = dirp->i_op->create(dirp, fname, flen,
-                                        iap->ia_mode, &inode);
+               err = dirp->i_op->create(dirp, dchild, iap->ia_mode);
                break;
        case S_IFDIR:
-               if (!dirp->i_op || !dirp->i_op->mkdir)
-                       return nfserr_perm;
-               err = dirp->i_op->mkdir(dirp, fname, flen, iap->ia_mode);
+               err = dirp->i_op->mkdir(dirp, dchild, iap->ia_mode);
                break;
        case S_IFCHR:
        case S_IFBLK:
        case S_IFIFO:
-               if (!dirp->i_op || !dirp->i_op->mknod)
-                       return nfserr_perm;
-               err = dirp->i_op->mknod(dirp, fname, flen, iap->ia_mode, rdev);
+               err = dirp->i_op->mknod(dirp, dchild, iap->ia_mode, rdev);
                break;
-       default:
-               iput(dirp);
-               err = -EACCES;
        }
-
        fh_unlock(fhp);
 
        if (err < 0)
                return nfserrno(-err);
-
-       /*
-        * If the VFS call doesn't return the inode, look it up now.
-        */
-       if (inode == NULL) {
-               dirp->i_count++;
-               err = dirp->i_op->lookup(dirp, fname, flen, &inode);
-               if (err < 0)
-                       return -nfserrno(err);  /* Huh?! */
-       }
-
        if (EX_ISSYNC(fhp->fh_export))
-               nfsd_write_inode(dirp);
+               write_inode_now(dirp);
 
-       /* Assemble the file handle for the newly created file */
-       fh_compose(resfhp, fhp->fh_export, inode);
+       /* If needed, assemble the file handle for the newly created file.  */
+       if(!resfhp->fh_dverified)
+               fh_compose(resfhp, fhp->fh_export, dchild);
 
        /* Set file attributes. Mode has already been set and
         * setting uid/gid works only for root. Irix appears to
         * send along the gid when it tries to implement setgid
         * directories via NFS.
         */
-       if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) {
-               if ((err = nfsd_setattr(rqstp, resfhp, iap)) != 0) {
-                       fh_put(resfhp);
-                       return err;
-               }
-       }
+       err = 0;
+       if ((iap->ia_valid &= (ATTR_UID|ATTR_GID|ATTR_MODE)) != 0)
+               err = nfsd_setattr(rqstp, resfhp, iap);
 
-       return 0;
+       return err;
 }
 
 /*
  * Truncate a file.
  * The calling routines must make sure to update the ctime
  * field and call notify_change.
+ *
+ * XXX Nobody calls this thing? -DaveM
  */
 int
 nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size)
 {
+       struct dentry   *dentry;
        struct inode    *inode;
+       struct iattr    newattrs;
        int             err;
 
-       if ((err = fh_lookup(rqstp, fhp, S_IFREG, MAY_WRITE|MAY_TRUNC)) != 0)
+       if ((err = fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE|MAY_TRUNC)) != 0)
                return err;
 
-       fh_lock(fhp);                   /* lock inode if not yet locked */
-       inode = fhp->fh_inode;
+       dentry = fhp->fh_handle.fh_dentry;
+       inode = dentry->d_inode;
 
        if ((err = get_write_access(inode)) != 0)
-               return nfserrno(-err);
-       inode->i_size = size;
-       if (inode->i_op && inode->i_op->truncate)
-               inode->i_op->truncate(inode);
-       inode->i_dirt = 1;
+               goto out;
+
+       /* Things look sane, lock and do it. */
+       fh_lock(fhp);
+       newattrs.ia_size = size;
+       newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+       err = notify_change(inode, &newattrs);
+       if (!err) {
+               vmtruncate(inode, size);
+               if (inode->i_op && inode->i_op->truncate)
+                       inode->i_op->truncate(inode);
+       }
        put_write_access(inode);
-
        fh_unlock(fhp);
-
-       return 0;
+out:
+       return (err ? nfserrno(-err) : 0);
 }
 
 /*
@@ -632,18 +619,21 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size)
 int
 nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
 {
+       struct dentry   *dentry;
        struct inode    *inode;
        unsigned long   oldfs;
        int             err;
 
-       if ((err = fh_lookup(rqstp, fhp, S_IFLNK, MAY_READ)) != 0)
+       if ((err = fh_verify(rqstp, fhp, S_IFLNK, MAY_READ)) != 0)
                return err;
-       inode = fhp->fh_inode;
+
+       dentry = fhp->fh_handle.fh_dentry;
+       inode = dentry->d_inode;
 
        if (!inode->i_op || !inode->i_op->readlink)
                return nfserr_io;
 
-       inode->i_count++;
+       UPDATE_ATIME(inode);
        oldfs = get_fs(); set_fs(KERNEL_DS);
        err = inode->i_op->readlink(inode, buf, *lenp);
        set_fs(oldfs);
@@ -664,42 +654,45 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
                                char *path,  int plen,
                                struct svc_fh *resfhp)
 {
-       struct inode    *dirp, *inode;
+       struct dentry   *dentry, *dnew;
+       struct inode    *dirp;
        int             err;
 
        if (!flen || !plen)
                return nfserr_noent;
 
-       if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
+       if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE)) != 0)
                return err;
 
-       dirp = fhp->fh_inode;
-       if (nfsd_iscovered(dirp))
-               return nfserr_perm;
-       if (!dirp->i_op || !dirp->i_op->symlink)
-               return nfserr_perm;
+       dentry = fhp->fh_handle.fh_dentry;
+       dirp = dentry->d_inode;
 
-       fh_lock(fhp);                   /* lock inode */
-       dirp->i_count++;
-       err = dirp->i_op->symlink(dirp, fname, flen, path);
-       fh_unlock(fhp);                 /* unlock inode */
+       if (nfsd_iscovered(dentry, fhp->fh_export)      ||
+           !dirp->i_op                                 ||
+           !dirp->i_op->symlink)
+               return nfserr_perm;
 
-       if (err)
-               return nfserrno(-err);
+       dnew = lookup_dentry(fname, dget(dentry), 0);
+       err = PTR_ERR(dnew);
+       if (IS_ERR(dnew))
+               goto out;
 
-       if (EX_ISSYNC(fhp->fh_export))
-               nfsd_write_inode(dirp);
+       err = -EEXIST;
+       if(dnew->d_inode)
+               goto compose_and_out;
 
-       /*
-        * Okay, now look up the inode of the new symlink.
-        */
-       dirp->i_count++;        /* lookup eats the dirp inode */
-       err = dirp->i_op->lookup(dirp, fname, flen, &inode);
-       if (err)
-               return nfserrno(-err);
+       fh_lock(fhp);
+       err = dirp->i_op->symlink(dirp, dnew, path);
+       fh_unlock(fhp);
 
-       fh_compose(resfhp, fhp->fh_export, inode);
-       return 0;
+       if (!err) {
+               if (EX_ISSYNC(fhp->fh_export))
+                       write_inode_now(dirp);
+       }
+compose_and_out:
+       fh_compose(resfhp, fhp->fh_export, dnew);
+out:
+       return (err ? nfserrno(-err) : 0);
 }
 
 /*
@@ -709,37 +702,69 @@ int
 nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
                                char *fname, int len, struct svc_fh *tfhp)
 {
+       struct dentry   *ddir, *dnew;
        struct inode    *dirp, *dest;
        int             err;
 
-       if ((err = fh_lookup(rqstp, ffhp, S_IFDIR, MAY_CREATE) != 0) ||
-           (err = fh_lookup(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0)
+       if ((err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE) != 0) ||
+           (err = fh_verify(rqstp, tfhp, S_IFREG, MAY_NOP)) != 0)
                return err;
-       dirp = ffhp->fh_inode;
-       dest = tfhp->fh_inode;
 
+       ddir = ffhp->fh_handle.fh_dentry;
+       dirp = ddir->d_inode;
+
+       dnew = lookup_dentry(fname, dget(ddir), 1);
+       err = PTR_ERR(dnew);
+       if (IS_ERR(dnew))
+               return nfserrno(-err);
+
+       err = -EEXIST;
+       if (dnew->d_inode)
+               goto dput_and_out;
+       dest = tfhp->fh_handle.fh_dentry->d_inode;
+
+       err = -EPERM;
        if (!len)
-               return nfserr_perm;
-       if (nfsd_iscovered(dirp))
-               return nfserr_acces;
+               goto dput_and_out;
+
+       err = -EACCES;
+       if (nfsd_iscovered(ddir, ffhp->fh_export))
+               goto dput_and_out;
        if (dirp->i_dev != dest->i_dev)
-               return nfserr_acces;    /* FIXME: nxdev for NFSv3 */
+               goto dput_and_out;      /* FIXME: nxdev for NFSv3 */
+
+       err = -EPERM;
        if (IS_IMMUTABLE(dest) /* || IS_APPEND(dest) */ )
-               return nfserr_perm;
+               goto dput_and_out;
        if (!dirp->i_op || !dirp->i_op->link)
-               return nfserr_perm;
+               goto dput_and_out;
 
-       fh_lock(ffhp);                  /* lock directory inode */
-       dirp->i_count++;
-       err = dirp->i_op->link(dest, dirp, fname, len);
-       fh_unlock(ffhp);                /* unlock inode */
+       fh_lock(ffhp);
+       err = dirp->i_op->link(dest, dirp, dnew);
+       fh_unlock(ffhp);
 
        if (!err && EX_ISSYNC(ffhp->fh_export)) {
-               nfsd_write_inode(dirp);
-               nfsd_write_inode(dest);
+               write_inode_now(dirp);
+               write_inode_now(dest);
        }
+dput_and_out:
+       dput(dnew);
+       return (err ? nfserrno(-err) : 0);
+}
 
-       return err? nfserrno(-err) : 0;
+/* More "hidden treasure" from the generic VFS. -DaveM */
+static inline void nfsd_double_down(struct semaphore *s1, struct semaphore *s2)
+{
+       if((unsigned long) s1 < (unsigned long) s2) {
+               down(s1);
+               down(s2);
+       } else if(s1 == s2) {
+               down(s1);
+               atomic_dec(&s1->count);
+       } else {
+               down(s2);
+               down(s1);
+       }
 }
 
 /*
@@ -749,14 +774,19 @@ int
 nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
                            struct svc_fh *tfhp, char *tname, int tlen)
 {
+       struct dentry   *fdentry, *tdentry, *odentry, *ndentry;
        struct inode    *fdir, *tdir;
        int             err;
 
-       if ((err = fh_lookup(rqstp, ffhp, S_IFDIR, MAY_REMOVE) != 0)
-        || (err = fh_lookup(rqstp, tfhp, S_IFDIR, MAY_CREATE)) != 0)
+       if ((err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_REMOVE) != 0)
+        || (err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE)) != 0)
                return err;
-       fdir = ffhp->fh_inode;
-       tdir = tfhp->fh_inode;
+
+       fdentry = ffhp->fh_handle.fh_dentry;
+       fdir = fdentry->d_inode;
+
+       tdentry = tfhp->fh_handle.fh_dentry;
+       tdir = tdentry->d_inode;
 
        if (!flen || (fname[0] == '.' && 
            (flen == 1 || (flen == 2 && fname[1] == '.'))) ||
@@ -764,23 +794,37 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
            (tlen == 1 || (tlen == 2 && tname[1] == '.'))))
                return nfserr_perm;
 
-       if (fdir->i_dev != tdir->i_dev)
-               return nfserr_acces;    /* nfserr_nxdev */
-       if (!fdir->i_op || !fdir->i_op->rename)
-               return nfserr_perm;
+       odentry = lookup_dentry(fname, dget(fdentry), 1);
+       err = PTR_ERR(odentry);
+       if (IS_ERR(odentry))
+               goto out_no_unlock;
 
-       fh_lock(tfhp);                  /* lock destination directory */
-       tdir->i_count++;
-       fdir->i_count++;
-       err = fdir->i_op->rename(fdir, fname, flen, tdir, tname, tlen);
-       fh_unlock(tfhp);                /* unlock inode */
+       ndentry = lookup_dentry(tname, dget(tdentry), 1);
+       err = PTR_ERR(ndentry);
+       if (IS_ERR(ndentry))
+               goto out_dput_old;
 
+       nfsd_double_down(&tdir->i_sem, &fdir->i_sem);
+       err = -EXDEV;
+       if (fdir->i_dev != tdir->i_dev)
+               goto out_unlock;
+       err = -EPERM;
+       if (!fdir->i_op || !fdir->i_op->rename)
+               goto out_unlock;
+       err = fdir->i_op->rename(fdir, odentry, tdir, ndentry);
        if (!err && EX_ISSYNC(tfhp->fh_export)) {
-               nfsd_write_inode(fdir);
-               nfsd_write_inode(tdir);
+               write_inode_now(fdir);
+               write_inode_now(tdir);
        }
-
-       return err? nfserrno(-err) : 0;
+out_unlock:
+       up(&tdir->i_sem);
+       up(&fdir->i_sem);
+
+       dput(ndentry);
+out_dput_old:
+       dput(odentry);
+out_no_unlock:
+       return (err ? nfserrno(-err) : 0);
 }
 
 /*
@@ -790,35 +834,40 @@ int
 nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
                                char *fname, int flen)
 {
+       struct dentry   *dentry, *rdentry;
        struct inode    *dirp;
        int             err;
 
        if (!flen || isdotent(fname, flen))
                return nfserr_acces;
-
-       if ((err = fh_lookup(rqstp, fhp, S_IFDIR, MAY_REMOVE)) != 0)
+       if ((err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE)) != 0)
                return err;
 
-       fh_lock(fhp);                   /* lock inode */
-       dirp = fhp->fh_inode;
+       dentry = fhp->fh_handle.fh_dentry;
+       dirp = dentry->d_inode;
+
+       rdentry = lookup_dentry(fname, dget(dentry), 0);
+       err = PTR_ERR(rdentry);
+       if (IS_ERR(rdentry))
+               goto out;
 
+       fh_lock(fhp);
        if (type == S_IFDIR) {
-               if (!dirp->i_op || !dirp->i_op->rmdir)
-                       return nfserr_notdir;
-               dirp->i_count++;
-               err = dirp->i_op->rmdir(dirp, fname, flen);
-       } else {        /* other than S_IFDIR */
-               if (!dirp->i_op || !dirp->i_op->unlink)
-                       return nfserr_perm;
-               dirp->i_count++;
-               err = dirp->i_op->unlink(dirp, fname, flen);
+               err = -ENOTDIR;
+               if (dirp->i_op && dirp->i_op->rmdir)
+                       err = dirp->i_op->rmdir(dirp, rdentry);
+       } else {
+               err = -EPERM;
+               if (dirp->i_op && dirp->i_op->unlink)
+                       err = dirp->i_op->unlink(dirp, rdentry);
        }
+       fh_unlock(fhp);
 
-       fh_unlock(fhp);                 /* unlock inode */
+       dput(rdentry);
        if (!err && EX_ISSYNC(fhp->fh_export))
-               nfsd_write_inode(dirp);
-
-       return err? nfserrno(-err) : 0;
+               write_inode_now(dirp);
+out:
+       return (err ? nfserrno(-err) : 0);
 }
 
 /*
@@ -829,6 +878,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
                        encode_dent_fn func, u32 *buffer, int *countp)
 {
        struct readdir_cd cd;
+       struct inode    *inode;
        struct file     file;
        u32             *p;
        int             oldlen, eof, err;
@@ -856,6 +906,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
         * readdir() is not guaranteed to fill up the entire buffer, but
         * may choose to do less.
         */
+       inode = file.f_dentry->d_inode;
        do {
                oldlen = cd.buflen;
 
@@ -864,7 +915,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
                        file.f_inode->i_dev, file.f_inode->i_ino,
                        (int) file.f_pos, (int) oldlen, (int) cd.buflen);
                 */
-               err = file.f_op->readdir(file.f_inode, &file,
+               err = file.f_op->readdir(inode, &file,
                                         &cd, (filldir_t) func);
 
                if (err < 0) {
@@ -904,14 +955,16 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
 int
 nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat)
 {
+       struct dentry           *dentry;
        struct inode            *inode;
        struct super_block      *sb;
        unsigned long           oldfs;
        int                     err;
 
-       if ((err = fh_lookup(rqstp, fhp, 0, MAY_NOP)) != 0)
+       if ((err = fh_verify(rqstp, fhp, 0, MAY_NOP)) != 0)
                return err;
-       inode = fhp->fh_inode;
+       dentry = fhp->fh_handle.fh_dentry;
+       inode = dentry->d_inode;
 
        if (!(sb = inode->i_sb) || !sb->s_op->statfs)
                return nfserr_io;
@@ -928,8 +981,9 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat)
  * Check for a user's access permissions to this inode.
  */
 int
-nfsd_permission(struct svc_export *exp, struct inode *inode, int acc)
+nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc)
 {
+       struct inode    *inode = dentry->d_inode;
        int             err;
 
        if (acc == MAY_NOP)
@@ -954,7 +1008,7 @@ nfsd_permission(struct svc_export *exp, struct inode *inode, int acc)
        if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) {
                if (EX_RDONLY(exp) || IS_RDONLY(inode))
                        return nfserr_rofs;
-               if (S_ISDIR(inode->i_mode) && nfsd_iscovered(inode))
+               if (S_ISDIR(inode->i_mode) && nfsd_iscovered(dentry, exp))
                        return nfserr_perm;
                if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode))
                        return nfserr_perm;
@@ -983,80 +1037,6 @@ nfsd_permission(struct svc_export *exp, struct inode *inode, int acc)
        return err? nfserrno(-err) : 0;
 }
 
-/*
- * Look up the inode for a given FH.
- */
-struct inode *
-nfsd_iget(dev_t dev, ino_t ino)
-{
-       struct super_block      *sb;
-
-       if (!(sb = get_super(dev)) || fs_off_limits(sb))
-               return NULL;
-       return __iget(sb, ino, 0);
-}
-
-/*
- * Write the inode if dirty (copy of fs/inode.c:write_inode)
- */
-void
-nfsd_write_inode(struct inode *inode)
-{
-       struct super_block      *sb = inode->i_sb;
-
-       if (!inode->i_dirt)
-               return;
-       while (inode->i_lock) {
-               sleep_on(&inode->i_wait);
-               if (!inode->i_dirt)
-                       return;
-       }
-       if (!sb || !sb->s_op || !sb->s_op->write_inode) {
-               inode->i_dirt = 0;
-               return;
-       }
-       inode->i_lock = 1;
-       sb->s_op->write_inode(inode);
-       inode->i_lock = 0;
-       wake_up(&inode->i_wait);
-}
-
-/*
- * Look up the root inode of the parent fs.
- * We have to go through iget in order to allow for wait_on_inode.
- */
-int
-nfsd_parentdev(dev_t* devp)
-{
-       struct super_block      *sb;
-
-       if (!(sb = get_super(*devp)) || !sb->s_covered)
-               return 0;
-       if (*devp == sb->s_covered->i_dev)
-               return 0;
-       *devp = sb->s_covered->i_dev;
-       return 1;
-}
-
-/*
- * This is a copy from fs/inode.c because it wasn't exported.
- */
-int
-nfsd_notify_change(struct inode *inode, struct iattr *attr)
-{
-       int retval;
-
-       if (inode->i_sb && inode->i_sb->s_op &&
-           inode->i_sb->s_op->notify_change)
-               return inode->i_sb->s_op->notify_change(inode, attr);
-
-       if ((retval = inode_change_ok(inode, attr)) != 0)
-               return retval;
-
-       inode_setattr(inode, attr);
-       return 0;
-}
-
 /*
  * Initialize readahead param cache
  */
index 3408fb2a6de87338289996eaf6be493823daa0ec..36669e4a6d44d9fbe037699de7df0c401747dfef 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -543,44 +543,21 @@ out:
        return error;
 }
 
-asmlinkage int sys_lchown(const char * filename, uid_t user, gid_t group)
-{
-       struct dentry * dentry;
-       int error;
-
-       lock_kernel();
-       dentry = lnamei(filename);
-
-       error = PTR_ERR(dentry);
-       if (IS_ERR(dentry))
-               goto out;
-
-       error = chown_common(dentry, user, group);
-
-       dput(dentry);
-out:
-       unlock_kernel();
-       return(error);
-}
-
 asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group)
 {
        struct dentry * dentry;
        int error;
 
        lock_kernel();
-       dentry = namei(filename);
+       dentry = lnamei(filename);
 
        error = PTR_ERR(dentry);
-       if (IS_ERR(dentry))
-               goto out;
-
-       error = chown_common(dentry, user, group);
-
-       dput(dentry);
-out:
+       if (!IS_ERR(dentry)) {
+               error = chown_common(dentry, user, group);
+               dput(dentry);
+       }
        unlock_kernel();
-       return(error);
+       return error;
 }
 
 asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group)
index 5d936ee59ba39e97d474290f2db61176e80e3c42..d9a334f8519bf96ed16bca24a73e25ce36838045 100644 (file)
  * Changes
  *                                     Changed for 2.1.19 modules
  *     Jan 1997                        Initial release
+ *     Jun 1997                        2.1.43+ changes
+ *     Jul 1997                        proper page locking in readpage
+ *                                     Changed to work with 2.1.45+ fs
+ *                                     Fixed follow_link
  */
 
 /* todo:
- *     use malloced memory for file names?
- *     considering write access...
- *     network (tftp) files?
+ *     - see Documentation/filesystems/romfs.txt
+ *     - use malloced memory for file names?
+ *     - considering write access...
+ *     - network (tftp) files?
+ *     - in the ancient times something leaked to made umounts
+ *       impossible, but I've not seen it in the last months
  */
 
 /*
@@ -74,7 +81,7 @@ romfs_read_super(struct super_block *s, void *data, int silent)
 
        MOD_INC_USE_COUNT;
 
-       /* I would parse the options, but there are none.. :) */
+       /* I would parse the options here, but there are none.. :) */
 
        lock_super(s);
        set_blocksize(dev, ROMBSIZE);
@@ -82,6 +89,7 @@ romfs_read_super(struct super_block *s, void *data, int silent)
        s->s_blocksize_bits = ROMBSBITS;
        bh = bread(dev, 0, ROMBSIZE);
        if (!bh) {
+               /* XXX merge with other printk? */
                 printk ("romfs: unable to read superblock\n");
                goto outnobh;
        }
@@ -113,10 +121,11 @@ romfs_read_super(struct super_block *s, void *data, int silent)
        brelse(bh);
 
        s->s_op = &romfs_ops;
+       s->s_root = d_alloc_root(iget(s, sz), NULL);
 
        unlock_super(s);
 
-       if (!(s->s_mounted = iget(s, sz)))
+       if (!s->s_root)
                goto outnobh;
 
        /* Ehrhm; sorry.. :)  And thanks to Hans-Joachim Widmaier  :) */
@@ -145,10 +154,9 @@ romfs_put_super(struct super_block *sb)
        return;
 }
 
-
 /* That's simple too. */
 
-static void
+static int
 romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
 {
        struct statfs tmp;
@@ -157,9 +165,12 @@ romfs_statfs(struct super_block *sb, struct statfs *buf, int bufsize)
        tmp.f_type = ROMFS_MAGIC;
        tmp.f_bsize = ROMBSIZE;
        tmp.f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
-       copy_to_user(buf, &tmp, bufsize);
+       /* XXX tmp.f_namelen = relevant? */
+       return copy_to_user(buf, &tmp, bufsize) ? -EFAULT : 0;
 }
 
+/* some helper routines */
+
 static int
 romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count)
 {
@@ -238,8 +249,6 @@ romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long
        return res;
 }
 
-/* Directory operations */
-
 static int
 romfs_readdir(struct inode *i, struct file *filp, void *dirent, filldir_t filldir)
 {
@@ -295,14 +304,16 @@ romfs_readdir(struct inode *i, struct file *filp, void *dirent, filldir_t filldi
 }
 
 static int
-romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
+romfs_lookup(struct inode *dir, struct dentry *dentry)
 {
        unsigned long offset, maxoff;
        int fslen, res;
+       struct inode *inode;
        char fsname[ROMFS_MAXFN];       /* XXX dynamic? */
        struct romfs_inode ri;
+       const char *name;               /* got from dentry */
+       int len;
 
-       *result = NULL;
        if (!dir || !S_ISDIR(dir->i_mode)) {
                res = -EBADF;
                goto out;
@@ -317,6 +328,12 @@ romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result
        maxoff = dir->i_sb->u.romfs_sb.s_maxsize;
        offset = ntohl(ri.spec) & ROMFH_MASK;
 
+       /* ok, now find the file, whose name is in "dentry", in the
+        * directory specified by "dir".  */
+
+       name = dentry->d_name.name;
+       len = dentry->d_name.len;
+
        for(;;) {
                if (!offset || offset >= maxoff
                    || romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0) {
@@ -350,20 +367,19 @@ romfs_lookup(struct inode *dir, const char *name, int len, struct inode **result
        if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
                offset = ntohl(ri.spec) & ROMFH_MASK;
 
-       res = 0;
-       if (!(*result = iget(dir->i_sb, offset)))
-               res = -EACCES;
+       res = -EACCES;
+       if ((inode = iget(dir->i_sb, offset))!=NULL) {
+               res = 0;
+               d_add(dentry, inode);
+       }
 
 out:
-       iput(dir);
        return res;
 }
 
 /*
  * Ok, we do readpage, to be able to execute programs.  Unfortunately,
- * bmap is not applicable, since we have looser alignments.
- *
- * XXX I'm not quite sure that I need to muck around the PG_xx bits..
+ * we can't use bmap, since we have looser alignments.
  */
 
 static int
@@ -373,8 +389,12 @@ romfs_readpage(struct inode * inode, struct page * page)
        unsigned long offset, avail, readlen;
        int result = -EIO;
 
-       buf = page_address(page);
        atomic_inc(&page->count);
+       set_bit(PG_locked, &page->flags);
+
+       buf = page_address(page);
+       clear_bit(PG_uptodate, &page->flags);
+       clear_bit(PG_error, &page->flags);
        offset = page->offset;
        if (offset < inode->i_size) {
                avail = inode->i_size-offset;
@@ -383,13 +403,19 @@ romfs_readpage(struct inode * inode, struct page * page)
                        if (readlen < PAGE_SIZE) {
                                memset((void *)(buf+readlen),0,PAGE_SIZE-readlen);
                        }
-                       result = 0;
                        set_bit(PG_uptodate, &page->flags);
-               } else {
-                       memset((void *)buf, 0, PAGE_SIZE);
+                       result = 0;
                }
        }
+       if (result) {
+               set_bit(PG_error, &page->flags);
+               memset((void *)buf, 0, PAGE_SIZE);
+       }
+
+       clear_bit(PG_locked, &page->flags);
+       wake_up(&page->wait);
        free_page(buf);
+
        return result;
 }
 
@@ -417,6 +443,46 @@ out:
        return mylen;
 }
 
+static struct dentry *romfs_follow_link(struct inode *inode, struct dentry *base)
+{
+       char *link;
+       int len, cnt;
+       struct dentry *dentry;
+
+       /* Note: 2.1.46+ calls this for our strange directories...
+        * What I do is not really right, but I like it better for now,
+        * than a separate i_op table.  Anyway, our directories won't
+        * have multiple "real" links to them, so it maybe loses nothing.  */
+       if (!S_ISLNK(inode->i_mode)) {
+               dentry = dget(i_dentry(inode));
+               goto outnobuf;
+       }
+
+       len = inode->i_size;
+
+       dentry = ERR_PTR(-EAGAIN);                      /* correct? */
+       if (!(link = kmalloc(len+1, GFP_KERNEL)))
+               goto outnobuf;
+
+       cnt = romfs_copyfrom(inode, link, inode->u.romfs_i.i_dataoffset, len);
+       if (len != cnt) {
+               dentry = ERR_PTR(-EIO);
+               goto out;
+       } else
+               link[len] = 0;
+
+       dentry = lookup_dentry(link, base, 1);
+       kfree(link);
+
+       if (0) {
+out:
+               kfree(link);
+outnobuf:
+               dput(base);
+       }
+       return dentry;
+}
+
 /* Mapping from our types to the kernel */
 
 static struct file_operations romfs_file_operations = {
@@ -447,6 +513,7 @@ static struct inode_operations romfs_file_inode_operations = {
        NULL,                   /* mknod */
        NULL,                   /* rename */
        NULL,                   /* readlink */
+       NULL,                   /* follow_link */
        romfs_readpage,         /* readpage */
        NULL,                   /* writepage */
        NULL,                   /* bmap -- not really */
@@ -471,7 +538,7 @@ static struct file_operations romfs_dir_operations = {
        NULL                    /* revalidate */
 };
 
-/* Merged dir/symlink op table.  readdir/lookup/readlink
+/* Merged dir/symlink op table.  readdir/lookup/readlink/follow_link
  * will protect from type mismatch.
  */
 
@@ -487,6 +554,7 @@ static struct inode_operations romfs_dirlink_inode_operations = {
        NULL,                   /* mknod */
        NULL,                   /* rename */
        romfs_readlink,         /* readlink */
+       romfs_follow_link,      /* follow_link */
        NULL,                   /* readpage */
        NULL,                   /* writepage */
        NULL,                   /* bmap */
@@ -519,9 +587,9 @@ romfs_read_inode(struct inode *i)
        int nextfh, ino;
        struct romfs_inode ri;
 
-       i->i_op = NULL;
-
        ino = i->i_ino & ROMFH_MASK;
+       i->i_op = NULL;
+       i->i_mode = 0;
 
        /* Loop for finding the real hard link */
        for(;;) {
@@ -549,6 +617,7 @@ romfs_read_inode(struct inode *i)
                ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK);
        else
                ino = 0;
+
        i->u.romfs_i.i_metasize = ino;
        i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK);
 
@@ -573,9 +642,10 @@ romfs_read_inode(struct inode *i)
 
 static struct super_operations romfs_ops = {
        romfs_read_inode,       /* read inode */
-       NULL,                   /* notify change */
        NULL,                   /* write inode */
        NULL,                   /* put inode */
+       NULL,                   /* delete inode */
+       NULL,                   /* notify change */
        romfs_put_super,        /* put super */
        NULL,                   /* write super */
        romfs_statfs,           /* statfs */
index f143f93487a7f1942a7c555ff138ed9d376487bf..a7a2d434e908e0448316fec3c9f84ee036e107b9 100644 (file)
@@ -637,6 +637,7 @@ static int umount_dev(kdev_t dev)
        if (MAJOR(dev) >= MAX_BLKDEV)
                return -ENXIO;
 
+       fsync_dev(dev);
        retval = do_umount(dev,0);
        if (!retval) {
                fsync_dev(dev);
index 1ca8af0d7b24243ed0fcd3df3ba1f290594802dc..5a1dc90bf7476e386ba73b84148fa26c069f1451 100644 (file)
@@ -98,6 +98,10 @@ extern void d_move(struct dentry * entry, struct dentry * newparent, struct qstr
 /* appendix may either be NULL or be used for transname suffixes */
 extern struct dentry * d_lookup(struct dentry * dir, struct qstr * name);
 
+/* validate "insecure" dentry pointer */
+extern int d_validate(struct dentry *dentry, struct dentry *dparent,
+                     unsigned int hash, unsigned int len);
+
 /* write full pathname into buffer and return length */
 extern int d_path(struct dentry * entry, struct dentry * chroot, char * buf);
 
index b459e966c446eb768f3bdbeb05f28150b151ed8e..4ccb6d569dff03a3dbed96bdd1835fa262bfea78 100644 (file)
@@ -677,6 +677,7 @@ extern void invalidate_inode_pages(struct inode *);
 extern void invalidate_buffers(kdev_t dev);
 extern int floppy_is_wp(int minor);
 extern void sync_inodes(kdev_t dev);
+extern void write_inode_now(struct inode *inode);
 extern void sync_dev(kdev_t dev);
 extern int fsync_dev(kdev_t dev);
 extern void sync_supers(kdev_t dev);
@@ -812,6 +813,7 @@ extern int dcache_lookup(struct inode *, const char *, int, unsigned long *);
 
 extern int inode_change_ok(struct inode *, struct iattr *);
 extern void inode_setattr(struct inode *, struct iattr *);
+extern int notify_change(struct inode * inode, struct iattr * attr);
 
 /* kludge to get SCSI modules working */
 #include <linux/minix_fs.h>
index 18076c1afcc6707b7154444b5c4d842953c7a812..5554600a2ade8d4d44dbca86279aff34c6b2fa06 100644 (file)
@@ -113,7 +113,7 @@ struct lp_stats {
 };
 
 struct lp_struct {
-       struct ppd *dev;
+       struct pardevice *dev;
        int flags;
        unsigned int chars;
        unsigned int time;
index 0badd9ebe148037eba56d850d18ace364a315474..fb17117a0dd936edd62041da8574f82c586a5505 100644 (file)
@@ -211,6 +211,7 @@ int get_cluster(struct inode *inode,int cluster);
 extern int fat_bmap(struct inode *inode,int block);
 extern int fat_notify_change(struct inode *,struct iattr *);
 extern void fat_put_inode(struct inode *inode);
+extern void fat_delete_inode(struct inode *inode);
 extern void fat_put_super(struct super_block *sb);
 extern void fat_read_inode(struct inode *inode, struct inode_operations *dir_ops);
 extern struct super_block *fat_read_super(struct super_block *s, void *data, int silent);
@@ -252,34 +253,30 @@ extern struct super_block *msdos_read_super(struct super_block *sb,void *data, i
 
 /* msdos.c - these are for Umsdos */
 extern void msdos_read_inode(struct inode *inode);
-extern int msdos_lookup(struct inode *dir,const char *name,int len, 
-                       struct inode **result);
-extern int msdos_create(struct inode *dir,const char *name,int len,int mode,
-                       struct inode **result);
-extern int msdos_rmdir(struct inode *dir,const char *name,int len);
-extern int msdos_mkdir(struct inode *dir,const char *name,int len,int mode);
-extern int msdos_unlink(struct inode *dir,const char *name,int len);
-extern int msdos_unlink_umsdos(struct inode *dir,const char *name,int len);
-extern int msdos_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 msdos_lookup(struct inode *dir,struct dentry *);
+extern int msdos_create(struct inode *dir,struct dentry *dentry,int mode);
+extern int msdos_rmdir(struct inode *dir,struct dentry *dentry);
+extern int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode);
+extern int msdos_unlink(struct inode *dir,struct dentry *dentry);
+extern int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry);
+extern int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
+                       struct inode *new_dir,struct dentry *new_dentry);
 
 /* fatfs_syms.c */
 extern int init_fat_fs(void);
 
 /* vfat/namei.c - these are for dmsdos */
-extern int vfat_create(struct inode *dir,const char *name,int len,int mode,
-                      struct inode **result);
-extern int vfat_unlink(struct inode *dir,const char *name,int len);
-extern int vfat_mkdir(struct inode *dir,const char *name,int len,int mode);
-extern int vfat_rmdir(struct inode *dir,const char *name,int len);
-extern int vfat_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 vfat_create(struct inode *dir,struct dentry *dentry,int mode);
+extern int vfat_unlink(struct inode *dir,struct dentry *dentry);
+extern int vfat_mkdir(struct inode *dir,struct dentry *dentry,int mode);
+extern int vfat_rmdir(struct inode *dir,struct dentry *dentry);
+extern int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
+                      struct inode *new_dir,struct dentry *new_dentry);
 extern void vfat_put_super(struct super_block *sb);
 extern struct super_block *vfat_read_super(struct super_block *sb,void *data,
                                           int silent);
 extern void vfat_read_inode(struct inode *inode);
-extern int vfat_lookup(struct inode *dir,const char *name,int len,
-                      struct inode **result);
+extern int vfat_lookup(struct inode *dir,struct dentry *);
 
 #endif /* __KERNEL__ */
 
index b0e3499ecd460a3bb20eb42f04b1557b130a7b0f..355e96251ff5ce832f3e057f70549480b30a4065 100644 (file)
@@ -59,7 +59,7 @@ struct svc_export {
        struct svc_export *     ex_parent;
        struct svc_client *     ex_client;
        int                     ex_flags;
-       struct inode *          ex_inode;
+       struct dentry *         ex_dentry;
        dev_t                   ex_dev;
        ino_t                   ex_ino;
        uid_t                   ex_anon_uid;
index ed42bb042cd0f703d305d3ba6d7a94551255bca4..4869c7ef82185c93ec6688364d04f7a53c388639 100644 (file)
@@ -109,10 +109,7 @@ int                nfsd_readdir(struct svc_rqst *, struct svc_fh *,
 int            nfsd_statfs(struct svc_rqst *, struct svc_fh *,
                                struct statfs *);
 int            nfsd_notify_change(struct inode *, struct iattr *);
-int            nfsd_permission(struct svc_export *, struct inode *, int);
-void           nfsd_write_inode(struct inode *);
-struct inode * nfsd_iget(dev_t dev, ino_t ino);
-int            nfsd_parentdev(dev_t *devp);
+int            nfsd_permission(struct svc_export *, struct dentry *, int);
 
 /*
  * lockd binding
index 75c2c91b2b2677812389cf12ce0a699bc4f0c8aa..ae88f161d9b83b4e3bb964c0eee3708ade8c62dd 100644 (file)
 #include <linux/nfsd/debug.h>
 
 /*
- * This is our NFSv2 file handle.
+ * This is the new "dentry style" Linux NFSv2 file handle.
  *
- * The xdev and xino fields are currently used to transport the dev/ino
- * of the exported inode. The xdev field is redundant, though, because
- * we do not allow mount point crossing.
+ * The xino and xdev fields are currently used to transport the
+ * ino/dev of the exported inode.
  */
 struct nfs_fhbase {
-       dev_t                   fb_dev;
-       dev_t                   fb_xdev;
-       ino_t                   fb_ino;
+       struct dentry           *fb_dentry;
+       struct dentry           *fb_dparent;
+       unsigned int            fb_dhash;
+       unsigned int            fb_dlen;
        ino_t                   fb_xino;
-       __u32                   fb_version;
+       dev_t                   fb_xdev;
 };
 
 #define NFS_FH_PADDING         (NFS_FHSIZE - sizeof(struct nfs_fhbase))
@@ -41,11 +41,12 @@ struct knfs_fh {
        __u8                    fh_cookie[NFS_FH_PADDING];
 };
 
-#define fh_dev                 fh_base.fb_dev
-#define fh_xdev                        fh_base.fb_xdev
-#define fh_ino                 fh_base.fb_ino
+#define fh_dentry              fh_base.fb_dentry
+#define fh_dparent             fh_base.fb_dparent
+#define fh_dhash               fh_base.fb_dhash
+#define fh_dlen                        fh_base.fb_dlen
 #define fh_xino                        fh_base.fb_xino
-#define fh_version             fh_base.fb_version
+#define fh_xdev                        fh_base.fb_xdev
 
 
 #ifdef __KERNEL__
@@ -57,26 +58,24 @@ struct knfs_fh {
 typedef struct svc_fh {
        struct knfs_fh          fh_handle;      /* FH data */
        struct svc_export *     fh_export;      /* export pointer */
-       struct dentry *         fh_dentry;      /* file */
        size_t                  fh_pre_size;    /* size before operation */
        time_t                  fh_pre_mtime;   /* mtime before oper */
        time_t                  fh_pre_ctime;   /* ctime before oper */
        unsigned long           fh_post_version;/* inode version after oper */
        unsigned char           fh_locked;      /* inode locked by us */
+       unsigned char           fh_dverified;   /* dentry has been checked */
 } svc_fh;
 
 /*
- * Shorthands for dprintk()'s
+ * Shorthand for dprintk()'s
  */
-#define SVCFH_INO(f)           ((f)->fh_handle.fh_ino)
-#define SVCFH_DEV(f)           ((f)->fh_handle.fh_dev)
+#define SVCFH_DENTRY(f)                ((f)->fh_handle.fh_dentry)
 
 /*
  * Function prototypes
  */
-u32             fh_lookup(struct svc_rqst *, struct svc_fh *, int, int);
-void            fh_compose(struct svc_fh *, struct svc_export *,
-                                struct inode *);
+u32             fh_verify(struct svc_rqst *, struct svc_fh *, int, int);
+void            fh_compose(struct svc_fh *, struct svc_export *, struct dentry *);
 
 static __inline__ struct svc_fh *
 fh_copy(struct svc_fh *dst, struct svc_fh *src)
@@ -98,7 +97,7 @@ fh_init(struct svc_fh *fhp)
 static inline void
 fh_lock(struct svc_fh *fhp)
 {
-       struct inode    *inode = fhp->fh_dentry->d_inode;
+       struct inode    *inode = fhp->fh_handle.fh_dentry->d_inode;
 
        /*
        dfprintk(FILEOP, "nfsd: fh_lock(%x/%ld) locked = %d\n",
@@ -118,7 +117,7 @@ fh_lock(struct svc_fh *fhp)
 static inline void
 fh_unlock(struct svc_fh *fhp)
 {
-       struct inode    *inode = fhp->fh_dentry->d_inode;
+       struct inode    *inode = fhp->fh_handle.fh_dentry->d_inode;
 
        if (fhp->fh_locked) {
                if (!fhp->fh_post_version)
@@ -135,9 +134,9 @@ fh_unlock(struct svc_fh *fhp)
 static inline void
 fh_put(struct svc_fh *fhp)
 {
-       if (fhp->fh_dentry) {
+       if (fhp->fh_dverified) {
                fh_unlock(fhp);
-               dput(fhp->fh_dentry);
+               dput(fhp->fh_handle.fh_dentry);
        }
 }
 #else
@@ -148,9 +147,10 @@ __fh_put(struct svc_fh *fhp, char *file, int line)
 {
        struct dentry   *dentry;
 
-       if (!(dentry = fhp->fh_dentry))
+       if (!fhp->fh_dverified)
                return;
 
+       dentry = fhp->fh_handle.fh_dentry;
        if (!dentry->d_count) {
                printk("nfsd: trying to free free dentry in %s:%d\n"
                       "      file %s/%s\n",
index 98c5d5a6e3adddcc4643aa598ade79a366a9b4e7..690f0095f0b8ba8825840329047a4d87ef30e22a 100644 (file)
@@ -111,11 +111,11 @@ union nfsctl_res {
 /*
  * Kernel syscall implementation.
  */
-#ifdef CONFIG_NFSD
+#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
 extern asmlinkage int  sys_nfsservctl(int, struct nfsctl_arg *,
                                            union nfsctl_res *);
 #else
-# define sys_nfsservctl        sys_ni_syscall
+#define sys_nfsservctl         sys_ni_syscall
 #endif
 extern int             exp_addclient(struct nfsctl_client *ncp);
 extern int             exp_delclient(struct nfsctl_client *ncp);
index 3ff82382eab2684dfdaf995315a70c0faa530264..bc47b8bace0a50f6af28ed0c18f7bd7a96cf0d47 100644 (file)
@@ -150,9 +150,10 @@ int nfssvc_encode_readlinkres(struct svc_rqst *, u32 *, struct nfsd_readlinkres
 int nfssvc_encode_readres(struct svc_rqst *, u32 *, struct nfsd_readres *);
 int nfssvc_encode_statfsres(struct svc_rqst *, u32 *, struct nfsd_statfsres *);
 int nfssvc_encode_readdirres(struct svc_rqst *, u32 *, struct nfsd_readdirres *);
-int nfssvc_release_fhandle(struct svc_rqst *, u32 *, struct nfsd_fhandle *);
 
 int nfssvc_encode_entry(struct readdir_cd *, const char *name,
                                int namlen, off_t offset, ino_t ino);
 
+int nfssvc_release_fhandle(struct svc_rqst *, u32 *, struct nfsd_fhandle *);
+
 #endif /* LINUX_NFSD_H */
index 93cc5bd511a2603c22ee315b1e6b8fc8cd973a68..45c55f5a3f5f2d815b8ea383cce88fd39d1dee7b 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: parport.h,v 1.2.6.3 1997/04/16 21:21:03 phil Exp $ */
+/* $Id: parport.h,v 1.2.6.3.2.2 1997/04/18 15:03:53 phil Exp $ */
 
 #ifndef _PARPORT_H_
 #define _PARPORT_H_
 #define PARPORT_MAX  8 
 
 /* Magic numbers */
-#define PARPORT_IRQ_NONE  -1
-#define PARPORT_DMA_NONE  -1
+#define PARPORT_IRQ_NONE  -2
+#define PARPORT_DMA_NONE  -2
+#define PARPORT_IRQ_AUTO  -1
+#define PARPORT_DMA_AUTO  -1
 #define PARPORT_DISABLE   -2
 
-/* Type classes for Plug-and-Play probe */
+/* Define this later. */
+struct parport;
+
+struct pc_parport_state {
+       unsigned int ctr;
+       unsigned int ecr;
+};
+
+struct parport_state {
+       union {
+               struct pc_parport_state pc;
+               /* ARC has no state. */
+               void *misc; 
+       } u;
+};
 
+/* Generic operations vector through the dispatch table. */
+#define parport_write_data(p,x)            (p)->ops->write_data(p,x)
+#define parport_read_data(p)               (p)->ops->read_data(p)
+#define parport_write_control(p,x)         (p)->ops->write_control(p,x)
+#define parport_read_control(p)            (p)->ops->read_control(p)
+#define parport_frob_control(p,m,v)        (p)->ops->frob_control(p,m,v)
+#define parport_write_econtrol(p,x)        (p)->ops->write_econtrol(p,x)
+#define parport_read_econtrol(p)           (p)->ops->read_econtrol(p)
+#define parport_frob_econtrol(p,m,v)       (p)->ops->frob_econtrol(p,m,v)
+#define parport_write_status(p,v)          (p)->ops->write_status(p,v)
+#define parport_read_status(p)             (p)->ops->read_status(p)
+#define parport_write_fifo(p,v)            (p)->ops->write_fifo(p,v)
+#define parport_read_fifo(p)               (p)->ops->read_fifo(p)
+#define parport_change_mode(p,m)           (p)->ops->change_mode(p,m)
+#define parport_release_resources(p)       (p)->ops->release_resources(p)
+#define parport_claim_resources(p)         (p)->ops->claim_resources(p)
+
+struct parport_operations {
+       void (*write_data)(struct parport *, unsigned int);
+       unsigned int (*read_data)(struct parport *);
+       void (*write_control)(struct parport *, unsigned int);
+       unsigned int (*read_control)(struct parport *);
+       unsigned int (*frob_control)(struct parport *, unsigned int mask, unsigned int val);
+       void (*write_econtrol)(struct parport *, unsigned int);
+       unsigned int (*read_econtrol)(struct parport *);
+       unsigned int (*frob_econtrol)(struct parport *, unsigned int mask, unsigned int val);
+       void (*write_status)(struct parport *, unsigned int);
+       unsigned int (*read_status)(struct parport *);
+       void (*write_fifo)(struct parport *, unsigned int);
+       unsigned int (*read_fifo)(struct parport *);
+
+       void (*change_mode)(struct parport *, int);
+
+       void (*release_resources)(struct parport *);
+       int (*claim_resources)(struct parport *);
+
+       unsigned int (*epp_write_block)(struct parport *, void *, unsigned int);
+       unsigned int (*epp_read_block)(struct parport *, void *, unsigned int);
+
+       unsigned int (*ecp_write_block)(struct parport *, void *, unsigned int, void (*fn)(struct parport *, void *, unsigned int), void *);
+       unsigned int (*ecp_read_block)(struct parport *, void *, unsigned int, void (*fn)(struct parport *, void *, unsigned int), void *);
+
+       void (*save_state)(struct parport *, struct parport_state *);
+       void (*restore_state)(struct parport *, struct parport_state *);
+
+       void (*enable_irq)(struct parport *);
+       void (*disable_irq)(struct parport *);
+       int (*examine_irq)(struct parport *);
+};
+
+#define PARPORT_CONTROL_STROBE    0x1
+#define PARPORT_CONTROL_AUTOFD    0x2
+#define PARPORT_CONTROL_INIT      0x4
+#define PARPORT_CONTROL_SELECT    0x8
+#define PARPORT_CONTROL_INTEN     0x10
+#define PARPORT_CONTROL_DIRECTION 0x20
+
+#define PARPORT_STATUS_ERROR      0x8
+#define PARPORT_STATUS_SELECT     0x10
+#define PARPORT_STATUS_PAPEROUT   0x20
+#define PARPORT_STATUS_ACK        0x40
+#define PARPORT_STATUS_BUSY       0x80
+
+/* Type classes for Plug-and-Play probe.  */
 typedef enum {
        PARPORT_CLASS_LEGACY = 0,       /* Non-IEEE1284 device */
        PARPORT_CLASS_PRINTER,
@@ -41,12 +121,6 @@ struct parport_device_info {
        char *description;
 };
 
-/* Definitions for parallel port sharing */
-
-/* Forward declare some stuff so we can use mutually circular structures */
-struct ppd;
-struct parport;
-
 /* Each device can have two callback functions:
  *  1) a preemption function, called by the resource manager to request
  *     that the driver relinquish control of the port.  The driver should
@@ -59,37 +133,27 @@ struct parport;
  *     the port, it should call parport_claim() here.  The return value from
  *     this function is ignored.
  */
-typedef int (*callback_func) (void *);
-
-/* This is an ordinary kernel IRQ handler routine.
- * The dev_id field (void *) will point the the port structure
- * associated with the interrupt request (to allow IRQ sharing)
- * Please make code IRQ sharing as this function may be called
- * when it isn't meant for you...
- */
-typedef void (*irq_handler_func) (int, void *, struct pt_regs *);
 
 /* A parallel port device */
-struct ppd {
+struct pardevice {
        char *name;
-       struct parport *port;   /* The port this is associated with */
-       callback_func preempt;  /* preemption function */
-       callback_func wakeup;   /* kick function */
+       struct parport *port;
+       int (*preempt)(void *);
+       void (*wakeup)(void *);
        void *private;
-       irq_handler_func irq_func;
+       void (*irq_func)(int, void *, struct pt_regs *);
        int flags;
-       unsigned char ctr;      /* SPP CTR register */
-       unsigned char ecr;      /* ECP ECR register */
-       struct ppd *next;
-       struct ppd *prev;
+       struct pardevice *next;
+       struct pardevice *prev;
+       struct parport_state *state;     /* saved status over preemption */
 };
 
-struct parport_dir{
+struct parport_dir {
        struct proc_dir_entry *entry;    /* Directory /proc/parport/X     */
        struct proc_dir_entry *irq;      /* IRQ entry /proc/parport/X/irq */
        struct proc_dir_entry *devices;  /* /proc/parport/X/devices       */
        struct proc_dir_entry *hardware; /* /proc/parport/X/hardware      */
-    char name[4]; /* /proc/parport/"XXXX" */
+       char name[4];                    /* /proc/parport/"XXXX" */
 };
 
 /* A parallel port */
@@ -100,16 +164,19 @@ struct parport {
        int irq;                /* interrupt (or -1 for none) */
        int dma;
        unsigned int modes;
-       struct ppd *devices;
-       struct ppd *cad;        /* port owner */
-       struct ppd *lurker;
-       unsigned int ctr;       /* SPP CTR register */
-       unsigned int ecr;       /* ECP ECR register */
+
+       struct pardevice *devices;
+       struct pardevice *cad;  /* port owner */
+       struct pardevice *lurker;
+       
        struct parport *next;
-        unsigned int flags; 
+       unsigned int flags; 
+
        struct parport_dir pdir;
        struct parport_device_info probe_info; 
-       int speed;  /* Max Write in Bytes/s */
+
+       struct parport_operations *ops;
+       void *private_data;     /* for lowlevel driver */
 };
 
 /* parport_register_port registers a new parallel port at the given address (if
@@ -117,15 +184,15 @@ struct parport {
  * claiming the I/O region, IRQ and DMA.
  * NULL is returned if initialisation fails. 
  */
-struct parport *parport_register_port(unsigned long base, int irq, int dma);
+struct parport *parport_register_port(unsigned long base, int irq, int dma,
+                                     struct parport_operations *ops);
 
 /* parport_in_use returns nonzero if there are devices attached to a port. */
 #define parport_in_use(x)  ((x)->devices != NULL)
 
-/* parport_destroy blows away a parallel port.  This fails if any devices are
- * registered.
- */
-void parport_destroy(struct parport *);
+/* Put a parallel port to sleep; release its hardware resources.  Only possible
+ * if no devices are registered.  */
+void parport_quiesce(struct parport *);
 
 /* parport_enumerate returns a pointer to the linked list of all the ports
  * in this machine.
@@ -140,19 +207,20 @@ struct parport *parport_enumerate(void);
  * Only one lurking driver can be used on a given port. 
  * handle is a user pointer that gets handed to callback functions. 
  */
-struct ppd *parport_register_device(struct parport *port, const char *name,
-                                   callback_func pf, callback_func kf,
-                                   irq_handler_func irq_func, int flags,
-                                   void *handle);
+struct pardevice *parport_register_device(struct parport *port, 
+                         const char *name,
+                         int (*pf)(void *), int (*kf)(void *),
+                         void (*irq_func)(int, void *, struct pt_regs *), 
+                         int flags, void *handle);
 
-/* parport_deregister causes the kernel to forget about a device */
-void parport_unregister_device(struct ppd *dev);
+/* parport_unregister unlinks a device from the chain. */
+void parport_unregister_device(struct pardevice *dev);
 
 /* parport_claim tries to gain ownership of the port for a particular driver.
  * This may fail (return non-zero) if another driver is busy.  If this
  * driver has registered an interrupt handler, it will be enabled. 
  */
-int parport_claim(struct ppd *dev);
+int parport_claim(struct pardevice *dev);
 
 /* parport_release reverses a previous parport_claim.  This can never fail, 
  * though the effects are undefined (except that they are bad) if you didn't
@@ -162,25 +230,24 @@ int parport_claim(struct ppd *dev);
  * If you mess with the port state (enabling ECP for example) you should
  * clean up before releasing the port. 
  */
-void parport_release(struct ppd *dev);
+void parport_release(struct pardevice *dev);
 
 /* The "modes" entry in parport is a bit field representing the following
  * modes.
  * Note that LP_ECPEPP is for the SMC EPP+ECP mode which is NOT
  * 100% compatible with EPP.
  */
-#define PARPORT_MODE_SPP               0x0001
-#define PARPORT_MODE_PS2               0x0002
-#define PARPORT_MODE_EPP               0x0004
-#define PARPORT_MODE_ECP               0x0008
-#define PARPORT_MODE_ECPEPP            0x0010
-#define PARPORT_MODE_ECR               0x0020  /* ECR Register Exists */
-#define PARPORT_MODE_ECPPS2            0x0040
-
-/* Flags used to identify what a device does
- */
-#define PARPORT_DEV_TRAN               0x0000
-#define PARPORT_DEV_LURK               0x0001
+#define PARPORT_MODE_PCSPP             0x0001
+#define PARPORT_MODE_PCPS2             0x0002
+#define PARPORT_MODE_PCEPP             0x0004
+#define PARPORT_MODE_PCECP             0x0008
+#define PARPORT_MODE_PCECPEPP          0x0010
+#define PARPORT_MODE_PCECR             0x0020  /* ECR Register Exists */
+#define PARPORT_MODE_PCECPPS2          0x0040
+
+/* Flags used to identify what a device does. */
+#define PARPORT_DEV_TRAN               0x0000  /* We're transient. */
+#define PARPORT_DEV_LURK               0x0001  /* We lurk. */
 
 #define PARPORT_FLAG_COMA              1
 
@@ -201,30 +268,4 @@ extern void inc_parport_count(void);
 extern int parport_probe(struct parport *port, char *buffer, int len);
 extern void parport_probe_one(struct parport *port);
 
-/* Primitive port access functions */
-extern inline void parport_w_ctrl(struct parport *port, int val) 
-{
-       outb(val, port->base+2);
-}
-
-extern inline int parport_r_ctrl(struct parport *port)
-{
-       return inb(port->base+2);
-}
-
-extern inline void parport_w_data(struct parport *port, int val)
-{
-       outb(val, port->base);
-}
-
-extern inline int parport_r_data(struct parport *port)
-{
-       return inb(port->base);
-}
-
-extern inline int parport_r_status(struct parport *port)
-{
-       return inb(port->base+1);
-}
-
 #endif /* _PARPORT_H_ */
diff --git a/include/linux/rocket.h b/include/linux/rocket.h
new file mode 100644 (file)
index 0000000..2019d4d
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * This file contains the exported interface of the rocket driver to
+ * its configuration program.
+ */
+
+struct rocket_config {
+       int     line;
+       int     flags;
+       int     closing_wait;
+       int     close_delay;
+       int     port;
+       int     reserved[32];
+};
+
+struct rocket_ports {
+       int     tty_major;
+       int     callout_major;
+       int     port_bitmap[4];
+       int     reserved[32];
+};
+
+/*
+ * Rocketport flags
+ */
+#define ROCKET_CALLOUT_NOHUP    0x00000001
+#define ROCKET_FORCE_CD                0x00000002
+#define ROCKET_HUP_NOTIFY      0x00000004
+#define ROCKET_SPLIT_TERMIOS   0x00000008
+#define ROCKET_SPD_MASK                0x00000070
+#define ROCKET_SPD_HI          0x00000010 /* Use 56000 instead of 38400 bps */
+#define ROCKET_SPD_VHI         0x00000020 /* Use 115200 instead of 38400 bps*/
+#define ROCKET_SPD_SHI         0x00000030 /* Use 230400 instead of 38400 bps*/
+#define ROCKET_SPD_WARP                0x00000040 /* Use 460800 instead of 38400 bps*/
+#define ROCKET_SAK             0x00000080
+#define ROCKET_SESSION_LOCKOUT 0x00000100
+#define ROCKET_PGRP_LOCKOUT    0x00000200
+       
+#define ROCKET_FLAGS           0x000003FF
+
+#define ROCKET_USR_MASK 0x0071 /* Legal flags that non-privileged
+                                * users can set or reset */
+
+/*
+ * For closing_wait and closing_wait2
+ */
+#define ROCKET_CLOSING_WAIT_NONE       65535
+#define ROCKET_CLOSING_WAIT_INF                0
+
+/*
+ * Rocketport ioctls -- "RP"
+ */
+#define RCKP_GET_STRUCT                0x00525001
+#define RCKP_GET_CONFIG                0x00525002
+#define RCKP_SET_CONFIG                0x00525003
+#define RCKP_GET_PORTS         0x00525004
index b922d0dbb57002819edff01bf4adf9844047b6e1..00ae5082f2c810c2e57836841bf975d4fa5b0185 100644 (file)
@@ -54,6 +54,7 @@
 
 #define HD_TIMER2      24
 #define GSCD_TIMER     25
+#define COMTROL_TIMER  26
 
 #define DIGI_TIMER     29
 
index f8e517583a3d2802b8ae7cf688eaccc7ed08ab7e..03efd8d01c8b0c649d22baffb4e483ceb5629c56 100644 (file)
@@ -62,6 +62,7 @@ extern int console_loglevel;
 static int init(void *);
 extern int bdflush(void *);
 extern int kswapd(void *);
+extern void kswapd_setup(void);
 
 extern void init_IRQ(void);
 extern void init_modules(void);
@@ -966,6 +967,7 @@ static int init(void * unused)
        /* Launch bdflush from here, instead of the old syscall way. */
        kernel_thread(bdflush, NULL, 0);
        /* Start the background pageout daemon. */
+       kswapd_setup();
        kernel_thread(kswapd, NULL, 0);
 
 #if CONFIG_AP1000
index fff97a75a7472f7170bd302eff08dfdfe8135dd6..8e55af454aa4bf1f1a37eed317543361f066e805 100644 (file)
@@ -136,6 +136,7 @@ EXPORT_SYMBOL(max_mapnr);
 EXPORT_SYMBOL(num_physpages);
 EXPORT_SYMBOL(high_memory);
 EXPORT_SYMBOL(update_vm_cache);
+EXPORT_SYMBOL(vmtruncate);
 
 /* filesystem internal functions */
 EXPORT_SYMBOL(getname);
@@ -150,6 +151,7 @@ EXPORT_SYMBOL(sys_close);
 EXPORT_SYMBOL(close_fp);
 EXPORT_SYMBOL(d_alloc_root);
 EXPORT_SYMBOL(d_delete);
+EXPORT_SYMBOL(d_validate);
 EXPORT_SYMBOL(d_add);
 EXPORT_SYMBOL(d_move);
 EXPORT_SYMBOL(d_instantiate);
@@ -163,6 +165,8 @@ EXPORT_SYMBOL(fsync_dev);
 EXPORT_SYMBOL(permission);
 EXPORT_SYMBOL(inode_setattr);
 EXPORT_SYMBOL(inode_change_ok);
+EXPORT_SYMBOL(write_inode_now);
+EXPORT_SYMBOL(notify_change);
 EXPORT_SYMBOL(get_hardblocksize);
 EXPORT_SYMBOL(set_blocksize);
 EXPORT_SYMBOL(getblk);
index 5d801958b146d0da624415d422247270994eb6cc..1a43e682daae13d9304563aefdec6245fa66fa38 100644 (file)
@@ -43,18 +43,7 @@ struct page * page_hash_table[PAGE_HASH_SIZE];
  * Simple routines for both non-shared and shared mappings.
  */
 
-/*
- * This is a special fast page-free routine that _only_ works
- * on page-cache pages that we are currently using. We can
- * just decrement the page count, because we know that the page
- * has a count > 1 (the page cache itself counts as one, and
- * we're currently using it counts as one). So we don't need
- * the full free_page() stuff..
- */
-static inline void release_page(struct page * page)
-{
-       atomic_dec(&page->count);
-}
+#define release_page(page) __free_page((page))
 
 /*
  * Invalidate the pages of an inode, removing all pages that aren't
@@ -767,6 +756,9 @@ page_read_error:
  * The goto's are kind of ugly, but this streamlines the normal case of having
  * it in the page cache, and handles the special cases reasonably without
  * having a lot of duplicated code.
+ *
+ * WSH 06/04/97: fixed a memory leak and moved the allocation of new_page
+ * ahead of the wait if we're sure to need it.
  */
 static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long address, int no_share)
 {
@@ -791,8 +783,15 @@ static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long
 found_page:
        /*
         * Ok, found a page in the page cache, now we need to check
-        * that it's up-to-date
+        * that it's up-to-date.  First check whether we'll need an
+        * extra page -- better to overlap the allocation with the I/O.
         */
+       if (no_share && !new_page) {
+               new_page = __get_free_page(GFP_KERNEL);
+               if (!new_page)
+                       goto failure;
+       }
+
        if (PageLocked(page))
                goto page_locked_wait;
        if (!PageUptodate(page))
@@ -817,13 +816,8 @@ success:
        }
 
        /*
-        * Check that we have another page to copy it over to..
+        * No sharing ... copy to the new page.
         */
-       if (!new_page) {
-               new_page = __get_free_page(GFP_KERNEL);
-               if (!new_page)
-                       goto failure;
-       }
        copy_page(new_page, old_page);
        flush_page_to_ram(new_page);
        release_page(page);
@@ -887,6 +881,8 @@ page_read_error:
         */
 failure:
        release_page(page);
+       if (new_page)
+               free_page(new_page);
 no_page:
        return 0;
 }
@@ -935,6 +931,10 @@ static int filemap_write_page(struct vm_area_struct * vma,
                /* whee.. just mark the buffer heads dirty */
                struct buffer_head * tmp = bh;
                do {
+                       /*
+                        * WSH: There's a race here: mark_buffer_dirty()
+                        * could block, and the buffers aren't pinned down.
+                        */
                        mark_buffer_dirty(tmp, 0);
                        tmp = tmp->b_this_page;
                } while (tmp != bh);
@@ -953,6 +953,9 @@ static int filemap_write_page(struct vm_area_struct * vma,
        file.f_pos = offset;
        file.f_reada = 0;
 
+       /*
+        * WSH: could vm_area struct (and inode) be released while writing?
+        */
        down(&inode->i_sem);
        result = do_write_page(inode, &file, (const char *) page, offset);
        up(&inode->i_sem);
@@ -1295,7 +1298,7 @@ generic_file_write(struct inode *inode, struct file *file, const char *buf, unsi
        unsigned long   ppos, offset;
        unsigned int    bytes, written;
        unsigned long   pos;
-       int             status, sync, didread = 0;
+       int             status, sync, didread;
 
        if (!inode->i_op || !inode->i_op->updatepage)
                return -EIO;
@@ -1334,9 +1337,14 @@ generic_file_write(struct inode *inode, struct file *file, const char *buf, unsi
                        page_cache = 0;
                }
 
-lockit:
-               while (test_and_set_bit(PG_locked, &page->flags))
-                       wait_on_page(page);
+               /*
+                * WSH 06/05/97: restructured slightly to make sure we release
+                * the page on an error exit.  Removed explicit setting of
+                * PG_locked, as that's handled below the i_op->xxx interface.
+                */
+               didread = 0;
+page_wait:
+               wait_on_page(page);
 
                /*
                 * If the page is not uptodate, and we're writing less
@@ -1345,28 +1353,24 @@ lockit:
                 * after the current end of file.
                 */
                if (!PageUptodate(page)) {
-                       /* Already tried to read it twice... too bad */
-                       if (didread > 1) {
-                               status = -EIO;
-                               break;
-                       }
                        if (bytes < PAGE_SIZE && ppos < inode->i_size) {
-                               /* readpage implicitly unlocks the page */
-                               status = inode->i_op->readpage(inode, page);
+                               if (didread < 2)
+                                   status = inode->i_op->readpage(inode, page);
+                               else 
+                                   status = -EIO; /* two tries ... error out */
                                if (status < 0)
-                                       break;
+                                       goto done_with_page;
                                didread++;
-                               goto lockit;
+                               goto page_wait;
                        }
                        set_bit(PG_uptodate, &page->flags);
                }
-               didread = 0;
 
-               /* Alright, the page is there, and we've locked it. Now
-                * update it. */
+               /* Alright, the page is there.  Now update it. */
                status = inode->i_op->updatepage(inode, page, buf,
                                                        offset, bytes, sync);
-               free_page(page_address(page));
+done_with_page:
+               __free_page(page);
                if (status < 0)
                        break;
 
index 21b4907a055b7c0b68a93988c60c73780bf86f49..d8f98214edf9b2b4472070c93938eceff3469c5e 100644 (file)
@@ -405,15 +405,31 @@ int try_to_free_page(int priority, int dma, int wait)
        return retval;
 }
 
+/*
+ * Before we start the kernel thread, print out the 
+ * kswapd initialization message (otherwise the init message 
+ * may be printed in the middle of another driver's init 
+ * message).  It looks very bad when that happens.
+ */
+void kswapd_setup(void)
+{
+       int i;
+       char *revision="$Revision: 1.23 $", *s, *e;
+
+       if ((s = strchr(revision, ':')) &&
+           (e = strchr(s, '$')))
+               s++, i = e - s;
+       else
+               s = revision, i = -1;
+       printk ("Starting kswapd v%.*s\n", i, s);
+}
+
 /*
  * The background pageout daemon.
  * Started as a kernel thread from the init process.
  */
 int kswapd(void *unused)
 {
-       int i;
-       char *revision="$Revision: 1.23 $", *s, *e;
-       
        current->session = 1;
        current->pgrp = 1;
        sprintf(current->comm, "kswapd");
@@ -434,13 +450,6 @@ int kswapd(void *unused)
 
        init_swap_timer();
        
-       if ((s = strchr(revision, ':')) &&
-           (e = strchr(s, '$')))
-               s++, i = e - s;
-       else
-               s = revision, i = -1;
-       printk ("Started kswapd v%.*s\n", i, s);
-
        while (1) {
                kswapd_awake = 0;
                current->signal = 0;
@@ -497,7 +506,6 @@ void swap_tick(void)
        timer_active |= (1<<SWAP_TIMER);
 }
 
-
 /* 
  * Initialise the swap timer
  */