]> git.neil.brown.name Git - history.git/commitdiff
Import 1.3.46 1.3.46
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:25 +0000 (15:10 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:10:25 +0000 (15:10 -0500)
135 files changed:
CREDITS
Documentation/cdrom/aztcd
Documentation/devices.tex
Documentation/devices.txt
Documentation/networking/arcnet-hardware.txt
Documentation/networking/arcnet.txt
MAGIC
Makefile
arch/alpha/kernel/entry.S
arch/alpha/kernel/osf_sys.c
arch/i386/boot/setup.S
arch/i386/config.in
arch/i386/defconfig
arch/i386/kernel/head.S
arch/i386/kernel/process.c
arch/i386/kernel/setup.c
arch/i386/kernel/time.c
arch/ppc/kernel/misc.S
arch/ppc/kernel/test.S
drivers/block/Config.in
drivers/block/MAKEDEV.ide
drivers/block/Makefile
drivers/block/README.ide
drivers/block/cmd640.c
drivers/block/ide-cd.c
drivers/block/ide-tape.c [new file with mode: 0644]
drivers/block/ide-tape.h [new file with mode: 0644]
drivers/block/ide.c
drivers/block/ide.h
drivers/block/ll_rw_blk.c
drivers/block/triton.c
drivers/cdrom/aztcd.c
drivers/char/Config.in
drivers/char/Makefile
drivers/char/apm_bios.c [new file with mode: 0644]
drivers/char/console.c
drivers/char/lp.c
drivers/net/3c501.c
drivers/net/arcnet.c
drivers/net/de4x5.c
drivers/net/ppp.c
drivers/net/slhc.c
drivers/net/tulip.c
drivers/pci/pci.c
drivers/scsi/eata_dma.c
drivers/scsi/eata_dma.h
drivers/scsi/eata_generic.h
drivers/scsi/hosts.c
drivers/scsi/scsi.c
drivers/scsi/scsi_syms.c
drivers/scsi/scsicam.c
drivers/scsi/sg.c
fs/Makefile
fs/buffer.c
fs/dquot.c [new file with mode: 0644]
fs/exec.c
fs/ext2/balloc.c
fs/ext2/ialloc.c
fs/ext2/inode.c
fs/ext2/namei.c
fs/ext2/super.c
fs/ext2/truncate.c
fs/file_table.c
fs/hpfs/hpfs_fs.c
fs/inode.c
fs/isofs/inode.c
fs/msdos/inode.c
fs/namei.c
fs/open.c
fs/proc/array.c
fs/proc/inode.c
fs/proc/root.c
fs/smbfs/dir.c
fs/smbfs/proc.c
fs/super.c
include/asm-alpha/mman.h
include/asm-alpha/page.h
include/asm-alpha/pgtable.h
include/asm-alpha/socket.h
include/asm-alpha/unistd.h
include/asm-i386/bitops.h
include/asm-i386/mman.h
include/asm-i386/page.h
include/asm-i386/pgtable.h
include/linux/apm_bios.h [new file with mode: 0644]
include/linux/ax25.h
include/linux/blk.h
include/linux/ext2_fs.h
include/linux/ext_fs.h
include/linux/fs.h
include/linux/if.h
include/linux/igmp.h
include/linux/iso_fs.h
include/linux/lp.h
include/linux/major.h
include/linux/minix_fs.h
include/linux/mount.h [new file with mode: 0644]
include/linux/msdos_fs.h
include/linux/pci.h
include/linux/proc_fs.h
include/linux/quota.h [new file with mode: 0644]
include/linux/sched.h
include/linux/smb.h
include/linux/sys.h
include/linux/sysv_fs.h
include/linux/tty.h
include/net/ax25.h
include/net/netlink.h
init/main.c
kernel/ksyms.c
kernel/module.c
kernel/printk.c
lib/Makefile
lib/_exit.c [deleted file]
lib/close.c [deleted file]
lib/dup.c [deleted file]
lib/execve.c [deleted file]
lib/open.c [deleted file]
lib/setsid.c [deleted file]
lib/wait.c [deleted file]
lib/write.c [deleted file]
mm/mmap.c
net/Changes
net/Config.in
net/ax25/af_ax25.c
net/ax25/ax25_route.c
net/core/datagram.c
net/ipv4/arp.c
net/ipv4/igmp.c
net/ipv4/raw.c
net/ipv4/route.c
net/ipx/af_ipx.c
net/netlink.c
net/netrom/af_netrom.c
net/netrom/nr_route.c

diff --git a/CREDITS b/CREDITS
index 83a8d21264efca5ad3414537820fe3ecf2d3961f..e2138ee667dec1daa298cedf97ebbcd9a92cfc95 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -2,7 +2,8 @@
        contributed to the linux project.  It is sorted by name, and
        formatted in a format that allows for easy grepping and
        beautification by scripts.  The fields are: name (N), email (E),
-       description (D) and snail-mail address (S).  Thanks,
+       web-address (W), description (D) and snail-mail address (S).
+       Thanks,
 
                        Linus
 ----------
@@ -286,7 +287,10 @@ D: Wrote Xenix FS (part of standard kernel since 0.99.15)
 
 N: Rik Faith
 E: faith@cs.unc.edu
-D: Future Domain TMC-16x0 SCSI driver
+E: r.faith@ieee.org
+D: Author: Future Domain TMC-16x0 SCSI driver
+D: Debugging: SCSI code; Cyclades serial driver; APM driver
+D: Debugging: XFree86 Mach 32 server, accelerated server code
 
 N: Juergen Fischer
 E: fischer@server.et-inf.fho-emden.de
@@ -420,6 +424,13 @@ S: Panoramastrasse 18
 S: D-69126 Heidelberg
 S: Germany
 
+N: Miguel de Icaza Amozurrutia
+E: miguel@nuclecu.unam.mx
+D: Linux/SPARC team, Midnight Commander maintainer
+S: Avenida Copilco 162, 22-1003
+S: Mexico, DF
+S: Mexico
+
 N: Ian Jackson
 E: iwj10@cus.cam.ac.uk
 E: ijackson@nyx.cs.du.edu
@@ -794,6 +805,18 @@ S: 816 Saratoga Avenue, Apartment M208
 S: San Jose, California 95129
 S: USA
 
+N: Eric S. Raymond
+E: esr@thyrsus.com
+W: http://www.ccil.org/~esr/home.html
+S: 22 South Warren Avenue
+S: Malvern, PA 19355 USA
+D: ncurses library co-maintainer
+D: terminfo master file maintainer
+D: Distributions HOWTO editor
+D: Instigator, FHS standard
+D: Keeper of the Jargon File and curator of the Retrocomputing Museum
+D: Author, Emacs VC and GUD modes
+
 N: Florian La Roche
 E: rzsfl@rz.uni-sb.de
 E: flla@stud.uni-sb.de
@@ -803,8 +826,9 @@ S: 7000 Stuttgart 50
 S: Germany
 
 N: Stephen Rothwell
-E: sfr@pdact.pd.necisa.oz.au
+E: Stephen.Rothwell@nec.com.au
 D: Boot/setup/build work for setup > 2K
+D: Author, APM driver
 S: 59 Bugden Avenue
 S: Gowrie ACT 2904
 S: Australia
@@ -1099,3 +1123,21 @@ D: Miscellaneous kernel fixes
 S: 3078 Sulphur Spring Court
 S: San Jose, California 95148
 S: USA
+
+N: Chih-Jen Chang     
+E: chihjenc@scf.usc.edu
+E: chihjen@iis.sinica.edu.tw
+D: IGMP(Internet Group Management Protocol) version 2
+S: 3F, 65 Tajen street
+S: Tamsui town, Taipei county,
+S: Taiwan 251, Republic of China
+
+N: Tsu-Sheng Tsao
+E: tsusheng@scf.usc.edu
+D: IGMP(Internet Group Management Protocol) version 2
+S: 2F 14 ALY 31 LN 166 SEC 1 SHIH-PEI RD
+S: Taipei 
+S: Taiwan 112, Republic of China
+S: 24335 Delta Drive
+S: Diamond Bar, CA 91765
+S: USA
index f2c6a92ecb5b8c3a8e772b282a0327acf1dce199..74e54c31472257933be08342cacf66d3ca0173c6 100644 (file)
@@ -1,4 +1,4 @@
-$Id: README.aztcd,v 2.0 1995/11/10 19:37:18 root Exp root $
+$Id: README.aztcd,v 2.10 1995/12/03 11:55:14 root Exp root $
           Readme-File /usr/src/Documentation/cdrom/aztcd
            for Aztech CD-ROM CDA268-01A, ORCHID CD-3110,
                   OKANO/WEARNES CDD110, Conrad TXC
@@ -460,7 +460,7 @@ and also were very patient with the problems which occurred.
 Reinhard Max delivered the information for the CDROM-interface of the
 SoundWave32 soundcards.
 
-Jochen Koch and Olaf Koluza delivered the information for supporting Conrad's TXC
+Jochen Koch and Olaf Kaluza delivered the information for supporting Conrad's TXC
 drive.
 
 Anybody, who is interested in these items should have a look at 'ftp.gwdg.de',
index b93c6e124abb75ef38d32b05bd375831c109192a..b05cb61a794038694bb8bb940de8acaaee22239c 100644 (file)
@@ -42,7 +42,7 @@ foo \kill}%
 %
 \title{{\bf Linux Allocated Devices}}
 \author{Maintained by H. Peter Anvin $<$hpa@storm.net$>$}
-\date{Last revised: November 17, 1995}
+\date{Last revised: December 4, 1995}
 \maketitle
 %
 \noindent
@@ -77,12 +77,12 @@ an unreasonable effort.
 \section{Major numbers}
 
 \begin{devicelist}
-\major{ 0}{}{     }{Unnamed devices (NFS mounts, loopback devices)}
+\major{ 0}{}{     }{Unnamed devices (e.g. non-device mounts)}
 \major{ 1}{}{char }{Memory devices}
 \major{  }{}{block}{RAM disk}
-\major{ 2}{}{char }{Reserved for PTY's $<$tytso@athena.mit.edu$>$}
+\major{ 2}{}{char }{Pseudo-TTY masters}
 \major{  }{}{block}{Floppy disks}
-\major{ 3}{}{char }{Reserved for PTY's $<$tytso@athena.mit.edu$>$}
+\major{ 3}{}{char }{Pseudo-TTY slaves}
 \major{  }{}{block}{First MFM, RLL and IDE hard disk/CD-ROM interface}
 \major{ 4}{}{char }{TTY devices}
 \major{ 5}{}{char }{Alternate TTY devices}
@@ -142,7 +142,8 @@ an unreasonable effort.
 \major{  }{}{block}{Modular RAM disk}
 \major{36}{}{char }{Netlink support}
 \major{  }{}{block}{MCA ESDI hard disk}
-\major{37}{--41}{}{Unallocated}
+\major{37}{}{char }{IDE tape}
+\major{38}{--41}{}{Unallocated}
 \major{42}{}{}{Demo/sample use}
 \major{43}{--223}{}{Unallocated}
 \major{224}{--239}{}{SEE NOTE}
@@ -154,7 +155,7 @@ an unreasonable effort.
 
 
 \begin{devicelist}
-\major{0}{}{}{Unnamed devices (NFS mounts, loopback devices)}
+\major{0}{}{}{Unnamed devices (e.g. non-device mounts)}
        \minor{0}{}{reserved as null device number}
 \end{devicelist}
 
@@ -175,7 +176,24 @@ an unreasonable effort.
 \end{devicelist}
 
 \begin{devicelist}
-\major{2}{}{char}{Reserved for PTY's $<$tytso@athena.mit.edu$>$}
+\major{2}{}{char}{Pseudo-TTY masters}
+       \minor{0}{/dev/ptyp0}{First PTY master}
+       \minor{1}{/dev/ptyp1}{Second PTY master}
+       \minordots
+       \minor{255}{/dev/ptyef}{256th PTY master}
+\end{devicelist}
+
+\noindent
+Pseudo-TTY's are named as follows:
+\begin{itemize}
+\item Masters are {\file pty}, slaves are {\file tty};
+\item the fourth letter is one of {\file pqrstuvwxyzabcde} indicating
+the 1st through 16th series of 16 pseudo-ttys each, and
+\item the fifth letter is one of {\file 0123456789abcdef} indicating
+the position within the series.
+\end{itemize}
+
+\begin{devicelist}
 \major{}{}{block}{Floppy disks}
        \minor{0}{/dev/fd0}{Controller 1, drive 1 autodetect}
        \minor{1}{/dev/fd1}{Controller 1, drive 2 autodetect}
@@ -235,7 +253,12 @@ letters {\file D}, {\file H}, or {\file E} for the 3.5" models have
 been deprecated, since the drive type is insignificant for these devices.
 
 \begin{devicelist}
-\major{3}{}{char}{Reserved for PTY's $<$tytso@athena.mit.edu$>$}
+\major{3}{}{char}{Pseudo-TTY slaves}
+       \minor{0}{/dev/ttyp0}{First PTY slave}
+       \minor{1}{/dev/ttyp1}{Second PTY slave}
+       \minordots
+       \minor{255}{/dev/ttyef}{256th PTY slave}
+\\
 \major{}{}{block}{First MFM, RLL and IDE hard disk/CD-ROM interface}
        \minor{0}{/dev/hda}{Master: whole disk (or CD-ROM)}
        \minor{64}{/dev/hdb}{Slave: whole disk (or CD-ROM)}
@@ -271,14 +294,10 @@ been deprecated, since the drive type is insignificant for these devices.
 \end{devicelist}
 
 \noindent
-Pseudo-tty's are named as follows:
-\begin{itemize}
-\item Masters are {\file pty}, slaves are {\file tty};
-\item the fourth letter is one of {\file pqrs} indicating
-the 1st, 2nd, 3rd, 4th series of 16 pseudo-ttys each, and
-\item the fifth letter is one of {\file 0123456789abcdef} indicating
-the position within the series.
-\end{itemize}
+For compatibility with previous versions of Linux, the first 64 PTYs
+are replicated under this device number.  This use will be obsolescent
+with the release of Linux 1.4 and may be removed in a future version
+of Linux.
 
 \begin{devicelist}
 \major{ 5}{}{char }{Alternate TTY devices}
@@ -358,6 +377,10 @@ physical disks.
        \minor{5}{/dev/atarimouse}{Atari Mouse (68k)}
        \minor{128}{/dev/beep}{Fancy beep device}
        \minor{129}{/dev/modreq}{Kernel module load request}
+       \minor{130}{/dev/watchdog}{Watchdog timer port}
+       \minor{131}{/dev/temperature}{Machine internal temperature}
+       \minor{132}{/dev/hwtrap}{Hardware fault trap}
+       \minor{133}{/dev/exttrp}{External device trap}
 \end{devicelist}
 
 \begin{devicelist}
@@ -730,16 +753,16 @@ major number 3).
 
 \begin{devicelist}
 \major{34}{}{char }{Z8530 HDLC driver}
-       \minor{0}{/dev/sc1}{First Z8530, first port}
-       \minor{1}{/dev/sc2}{First Z8530, second port}
-       \minor{2}{/dev/sc3}{Second Z8530, first port}
-       \minor{3}{/dev/sc4}{Second Z8530, second port}
+       \minor{0}{/dev/scc0}{First Z8530, first port}
+       \minor{1}{/dev/scc1}{First Z8530, second port}
+       \minor{2}{/dev/scc2}{Second Z8530, first port}
+       \minor{3}{/dev/scc3}{Second Z8530, second port}
        \minordots
 \end{devicelist}
 
 \noindent
-The author has indicated that the device names will change in a
-subsequent version; {\file /dev/sc1} will become {\file /dev/scc0} etc.
+In a previous version these files were named {\file /dev/sc1} for
+{\file /dev/scc0}, {\file /dev/sc2} for {\file /dev/scc1}, and so on.
 
 \begin{devicelist}
 \major{  }{}{block}{Fourth IDE hard disk/CD-ROM interface}
@@ -789,7 +812,16 @@ Partitions are handled the same way as for IDE disks (see major number
 3).
 
 \begin{devicelist}
-\major{37}{--41}{}{Unallocated}
+\major{37}{}{char }{IDE tape}
+       \minor{0}{/dev/ht0}{First IDE tape}
+       \minor{128}{/dev/nht0}{First IDE tape, no rewind-on-close}
+\end{devicelist}
+
+\noindent
+Currently, only one IDE tape drive is supported.
+
+\begin{devicelist}
+\major{38}{--41}{}{Unallocated}
 \end{devicelist}
 
 \begin{devicelist}
index 85f143d2f591cea4e6f8ff101831241d8cc30f67..442ed53621fda487344b0d4f80912159966beed2 100644 (file)
@@ -2,7 +2,7 @@
 
         Maintained by H. Peter Anvin <Peter.Anvin@linux.org>
 
-                  Last revised: September 20, 1995
+                   Last revised: December 4, 1995
 
 This list is the successor to Rick Miller's Linux Device List, which
 he stopped maintaining when he lost network access in 1993.  It is a
@@ -31,7 +31,7 @@ that semantically altered versions are not distributed without
 permission of the author, assuming the author can be contacted without
 an unreasonable effort.
 
-  0            Unnamed devices (NFS mounts, loopback devices)
+  0            Unnamed devices (e.g. non-device mounts)
                  0 = reserved as null device number
 
   1 char       Memory devices
@@ -46,8 +46,20 @@ an unreasonable effort.
                  9 = /dev/urandom      Faster, less secure random number gen.
     block      RAM disk
                  1 = /dev/ramdisk      RAM disk
+                   
+  2 char       Pseudo-TTY masters
+                 0 = /dev/ptyp0        First PTY master
+                 1 = /dev/ptyp1        Second PTY master
+                   ...
+               256 = /dev/ptyef        256th PTY master
 
-  2 char       Reserved for PTY's <tytso@athena.mit.edu>
+               Pseudo-tty's are named as follows:
+               * Masters are "pty", slaves are "tty";
+               * the fourth letter is one of pqrstuvwxyzabcde indicating
+                 the 1st through 16th series of 16 pseudo-ttys each, and
+               * the fifth letter is one of 0123456789abcdef indicating
+                 the position within the series.
+  
     block      Floppy disks
                  0 = /dev/fd0          First floppy disk autodetect
                  1 = /dev/fd1          Second floppy disk autodetect
@@ -100,7 +112,12 @@ an unreasonable effort.
                and E for the 3.5" models have been deprecated, since
                the drive type is insignificant for these devices.
 
-  3 char       Reserved for pty's <tytso@athena.mit.edu>
+  3 char       Pseudo-TTY slaves
+                 0 = /dev/ttyp0        First PTY slave
+                 1 = /dev/ttyp1        Second PTY slave
+                   ...
+               256 = /dev/ttyef        256th PTY slave
+
     block      First MFM, RLL and IDE hard disk/CD-ROM interface
                  0 = /dev/hda          Master: whole disk (or CD-ROM)
                 64 = /dev/hdb          Slave: whole disk (or CD-ROM)
@@ -133,12 +150,11 @@ an unreasonable effort.
                      ...
                255 = /dev/ttysf        64th pseudo-tty slave
 
-               Pseudo-tty's are named as follows:
-               * Masters are "pty", slaves are "tty";
-               * the fourth letter is one of p, q, r, s indicating
-                 the 1st, 2nd, 3rd, 4th series of 16 pseudo-ttys each, and
-               * the fifth letter is one of 0123456789abcdef indicating
-                 the position within the series.
+               For compatibility with previous versions of Linux, the
+               first 64 PTYs are replicated under this device
+               number.  This use will be obsolescent with the release
+               of Linux 1.4 and may be removed in a future version of
+               Linux.
 
   5 char       Alternate TTY devices
                  0 = /dev/tty          Current TTY device
@@ -203,9 +219,10 @@ an unreasonable effort.
                  5 = /dev/atarimouse   Atari Mouse (68k)
                128 = /dev/beep         Fancy beep device
                129 = /dev/modreq       Kernel module load request
-
-               The use of the suffix -mouse instead of -bm or -aux
-               has also been used.
+               130 = /dev/watchdog     Watchdog timer port
+               131 = /dev/temperature  Machine internal temperature
+               132 = /dev/hwtrap       Hardware fault trap
+               133 = /dev/exttrp       External device trap
 
  11 block      SCSI CD-ROM devices
                  0 = /dev/sr0          First SCSI CD-ROM
@@ -533,7 +550,13 @@ an unreasonable effort.
                Partitions are handled in the same way as IDE disks
                (see major number 3).
 
- 37-41         UNALLOCATED
+ 37 char       IDE tape
+                 0 = /dev/ht0          First IDE tape
+               128 = /dev/nht0         First IDE tape, no rewind-on-close
+
+               Currently, only one IDE tape drive is supported.
+
+ 38-41         UNALLOCATED
 
  42            Demo/sample use
 
index ae4b303a07efb190a6990f3b5307645dd5193c96..cb9598d119e9314476631079261372c84ac113bc 100644 (file)
@@ -39,7 +39,8 @@ which makes it completely incompatible with so-called "Token Ring" cards,
 but which makes transfers much more reliable than with ethernet.  In fact,
 ARCnet will guarantee that a packet arrives safely at the destination, and
 even if it can't possibly be delivered properly (ie. because of a cable
-break) it will at least tell the sender about it.
+break, or because the destination computer does not exist) it will at least
+tell the sender about it.
 
 In addition, all known ARCnet cards have an (almost) identical programming
 interface.  This means that with one "arcnet" driver you can support any
@@ -54,9 +55,9 @@ One thing that makes ARCnet cards difficult to program for, however, is the
 limit on their packet sizes; standard ARCnet can only send packets that are
 up to 508 bytes in length.  This is smaller than the internet "bare minimum"
 of 576 bytes, let alone the ethernet MTU of 1500.  To compensate, an extra
-level of encapsulation is defined by RFC1201 called "packet splitting" which
-allows "virtual packets" to grow as large as 64K each, although they are
-generally kept down to the ethernet-style 1500 bytes.
+level of encapsulation is defined by RFC1201, which I call "packet
+splitting," which allows "virtual packets" to grow as large as 64K each,
+although they are generally kept down to the ethernet-style 1500 bytes.
 
 
 CABLING ARCNET NETWORKS
@@ -69,7 +70,7 @@ CABLING ARCNET NETWORKS
 
  - I, Avery Pennarun, tried to arrange it into something that makes sense
    when all put together.  All mistakes, then, are most likely my fault.
-   Bug me about them.
+   Bug me about them, and they will probably get fixed.
 
 Ideally, according to documentation, ARCnet networks should be connected
 with 93 Ohm cables with 93 Ohm resistors as terminators.  I use TV cable and
@@ -86,7 +87,9 @@ Koenig (slight touchups by me):
                IBM Typ 3 100 Ohm up to 100 m
                
 So you can see that while 93 Ohms is ideal, you can still go half a
-kilometer with 75 Ohm TV cable.
+kilometer with 75 Ohm TV cable.  A rule of thumb might be handy here, to
+help you remember:
+       Basically any coax cable, up to a ridiculously large distance.
 
 The above applies to all known ARCnet cards.  Specific to STAR cards,
 though, Stephen A. Wood has some information:
@@ -131,7 +134,7 @@ above:
        
 According to Vojtech Pavlik, there shouldn't be more than one passive hub
 between two "active ends", an active end being an active hub or an ARCnet
-card.
+card.  That makes sense to me.
        
 As for BUS cards, they're even easier (for more than two cards, anyway; you
 can't get much simpler than direct-connecting two STAR cards with a TV
@@ -195,18 +198,20 @@ SETTING THE JUMPERS
 All ARCnet cards should have a total of four or five different settings:
 
   - the I/O address:  this is the "port" your ARCnet card is on.  Probed
-    values, as of v0.14, are only from 0x200 through 0x3F0. (If your card
-    has additional ones, which is possible, please tell me.) This should not
-    be the same as any other device on your system.  According to a doc I
-    got from Novell, MS Windows prefers values of 0x300 or more, eating
-    netconnections on my system otherwise.
+    values in the Linux ARCnet driver are only from 0x200 through 0x3F0. (If
+    your card has additional ones, which is possible, please tell me.) This
+    should not be the same as any other device on your system.  According to
+    a doc I got from Novell, MS Windows prefers values of 0x300 or more,
+    eating netconnections on my system (at least) otherwise.  My guess is
+    this may be because, if your card is at 0x2E0, probing for a serial port
+    at 0x2E8 will reset the card and probably mess things up royally.
        - Avery's favourite: 0x300.
 
-  - the IRQ:  on 8-bit cards, it might be 2 (9), 3, 4, 5, or 7.
+  - the IRQ: on  8-bit cards, it might be 2 (9), 3, 4, 5, or 7.
              on 16-bit cards, it might be 2 (9), 3, 4, 5, 7, or 10-15.  Make
     sure this is different from any other card on your system.  Note that
     IRQ2 is the same as IRQ9, as far as Linux is concerned.
-       - Avery's favourite: IRQ2.
+       - Avery's favourite: IRQ2 (actually IRQ9).
 
   - the memory address:  Unlike most cards, ARCnets use "shared memory" for
     copying buffers around.  Make SURE it doesn't conflict with any other
@@ -223,31 +228,33 @@ All ARCnet cards should have a total of four or five different settings:
 
   - the station address:  Every ARCnet card has its own "unique" network
     address from 0 to 255.  Unlike ethernet, you can set this address
-    yourself with a jumper.  Since it's only 8 bits, you can only have 254
-    ARCnet cards on a network.  DON'T use 0 or 255, since these are
-    reserved. (although neat stuff will probably happen if you DO use them). 
-    By the way, if you haven't already guessed, don't set this the same as
-    any other ARCnet on your network!
+    yourself with a jumper or switch (or on some cards, with special
+    software).  Since it's only 8 bits, you can only have 254 ARCnet cards
+    on a network.  DON'T use 0 or 255, since these are reserved. (although
+    neat stuff will probably happen if you DO use them).  By the way, if you
+    haven't already guessed, don't set this the same as any other ARCnet on
+    your network!
        - Avery's favourite:  3 and 4.  Not that it matters.
        
   - There may be ETS1 and ETS2 settings.  These may or may not make a
-    difference, but are used to change the delays used when powering up
-    a computer on the network.  This is only necessary when wiring VERY
-    long range ARCnet networks, on the order of 4km or so; in any case,
-    the only real requirement here is that all cards on the network with
-    ETS1 and ETS2 jumpers have them in the same position.
+    difference on your card, but are used to change the delays used when
+    powering up a computer on the network.  This is only necessary when
+    wiring VERY long range ARCnet networks, on the order of 4km or so; in
+    any case, the only real requirement here is that all cards on the
+    network with ETS1 and ETS2 jumpers have them in the same position.
 
 
 Here's the all the jumper information I could obtain for individual card
-types.  Unfortunately, there is a lot of duplicated information here. 
-Someday, I may get around to actually organizing it.  Until then... too much
-is better than too little, I say :)
+types.  The format of this list has changed somewhat; I finally got around
+to unduplicating some of the information and making a few other changes, but
+didn't get very far yet.  If you notice any problems with the info, it's now
+officially my fault :(
 
 The model # is listed right above specifics for that card, so you should be
 able to use your text viewer's "search" function to find the entry you want. 
 
-If your model isn't listed, and has different settings, PLEASE PLEASE tell
-me.  I had to figure mine out without the manual, and it WASN'T FUN!
+If your model isn't listed and/or has different settings, PLEASE PLEASE
+tell me.  I had to figure mine out without the manual, and it WASN'T FUN!
 
 Even if your ARCnet model isn't listed, but has the same jumpers as another
 model that is, please e-mail me to say so.
@@ -266,7 +273,7 @@ Cards Listed in this file (in this order, mostly):
        SMC             PC550Longboard  16
        SMC             PC600           16
        SMC?            LCS-8830-T      16?
-       Puredata        PDI507          16
+       Puredata        PDI507          8
        CNet Tech       CN120-Series    8
        CNet Tech       CN160-Series    16
        No Name         --              8/16
@@ -311,9 +318,6 @@ PC500, PC600 (16-bit cards)
     130, 500, and 600 all have the same switches as Avery's PC100. 
     PC500/600 have several extra, undocumented pins though. (?)
   - PC110 settings were verified by Stephen A. Wood <saw@cebaf.gov>
-  - On the other hand, John Edward Bauer <jbauer@badlands.NoDak.edu> said
-    the PC110 settings are all wrong.  In his case, you need to switch all
-    the 1's with 0's.  If you're having problems, try that.
   - Also, the JP- and S-numbers probably don't match your card exactly.  Try
     to find jumpers/switches with the same number of settings - it's
     probably more reliable.
@@ -323,12 +327,20 @@ PC500, PC600 (16-bit cards)
 (IRQ Setting)                IRQ2  IRQ3 IRQ4 IRQ5 IRQ7
                Put exactly one jumper on exactly one set of pins.
 
+
                           1  2   3  4  5  6   7  8  9 10
      S1                /----------------------------------\
 (I/O and Memory        |  1  1 * 0  0  0  0 * 1  1  0  1  |
  addresses)            \----------------------------------/
                           |--|   |--------|   |--------|
                           (a)       (b)           (m)
+                          
+                WARNING.  It's very important when setting these which way
+                you're holding the card, and which way you think is '1'!
+                
+                If you suspect that your settings are not being made
+               correctly, try reversing the direction or inverting the
+               switch positions.
 
                a: The first digit of the I/O address.
                        Setting         Value
@@ -473,40 +485,8 @@ Setting the Node ID
 -------------------
 
 The eight switches in group S2 are used to set the node ID.
-Each node attached to the network must have an unique node ID which
-must be diffrent from 0.
-Switch 1 serves as the least significant bit (LSB).
-
-The node ID is the sum of the values of all switches set to "1"  
-These values are:
-    Switch | Value
-    -------|-------
-      1    |   1
-      2    |   2
-      3    |   4
-      4    |   8
-      5    |  16
-      6    |  32
-      7    |  64
-      8    | 128
-
-Some Examples:
-
-    Switch         | Hex     | Decimal 
-   8 7 6 5 4 3 2 1 | Node ID | Node ID
-   ----------------|---------|---------
-   0 0 0 0 0 0 0 0 |    not allowed
-   0 0 0 0 0 0 0 1 |    1    |    1 
-   0 0 0 0 0 0 1 0 |    2    |    2
-   0 0 0 0 0 0 1 1 |    3    |    3
-       . . .       |         |
-   0 1 0 1 0 1 0 1 |   55    |   85
-       . . .       |         |
-   1 0 1 0 1 0 1 0 |   AA    |  170
-       . . .       |         |  
-   1 1 1 1 1 1 0 1 |   FD    |  253
-   1 1 1 1 1 1 1 0 |   FE    |  254
-   1 1 1 1 1 1 1 1 |   FF    |  255
+These switches work in a way similar to the PC100-series cards; see that
+entry for more information.
 
 
 Setting the I/O Base Address
@@ -556,7 +536,7 @@ positions, determined by the offset, switches 7 and 8 of group S1.
    0 1 0  1 0 |  CD000  |  CE000
    0 1 0  1 1 |  CD800  |  CE000
               |         |
-   0 1 1  0 0 |  D0000  |  D2000  (Manufactor's default)
+   0 1 1  0 0 |  D0000  |  D2000  (Manufacturer's default)
    0 1 1  0 1 |  D0800  |  D2000
    0 1 1  1 0 |  D1000  |  D2000
    0 1 1  1 1 |  D1800  |  D2000
@@ -590,10 +570,9 @@ Setting the Timeouts and Interrupt
 
 The jumpers labeled EXT1 and EXT2 are used to determine the timeout 
 parameters. These two jumpers are normally left open.
-Refer to the COM9026 Data Sheet for alternate configurations.
 
 To select a hardware interrupt level set one (only one!) of the jumpers
-IRQ2, IRQ3, IRQ4, IRQ5, IRQ7. The Manufactor's default is IRQ2.
+IRQ2, IRQ3, IRQ4, IRQ5, IRQ7. The Manufacturer's default is IRQ2.
  
 
 Configuring the PC130E for Star or Bus Topology
@@ -658,7 +637,7 @@ and for connection to bus networks.
 
 The PC550 is equipped with two modular RJ11-type jacks for connection
 to twisted pair wiring.
-It can be used in a star or a daisy-chained network.
+It can be used in a star or a daisy-chained (BUS) network.
 
        1 
        0 9 8 7 6 5 4 3 2 1     6 5 4 3 2 1
@@ -847,7 +826,7 @@ board activity:
 ** Possibly SMC **
 LCS-8830-T (16-bit card)
 ------------------------
-       - from Mathias Katzer <mkatzer@HRZ.Uni-Bielefeld.DE>
+  - from Mathias Katzer <mkatzer@HRZ.Uni-Bielefeld.DE>
        
 This is a LCS-8830-T made by SMC, I think ('SMC' only appears on one PLCC,
 nowhere else, not even on the few xeroxed sheets from the manual).
@@ -954,11 +933,9 @@ Switches        Ram           Rom
 *****************************************************************************
 
 ** PureData Corp **
-PDI507 (16-bit card)
+PDI507 (8-bit card)
 --------------------
-       - from Mark Rejhon <mdrejhon@magi.com> (slight modifications by
-         Avery)
-       - Send questions/suggestions/etc about this text to Mark.
+  - from Mark Rejhon <mdrejhon@magi.com> (slight modifications by Avery)
 
 Jumpers:
 
@@ -1868,7 +1845,7 @@ Setting the Timeouts
 ** No Name **
 8-bit cards ("Made in Taiwan R.O.C.")
 -----------
- - from Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz>
 - from Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz>
 
 I have named this ARCnet card "NONAME", since I got only the card with
 no manual at all and the only text identifying the manufacturer is 
index c623e853c7aa3df15cae105015080568cb5b8c97..2ecf033233551163a684215cd6257b32f46a8995 100644 (file)
@@ -58,8 +58,15 @@ Where do I discuss these drivers?
 BOINGY - the linux-arcnet@807-city.on.ca mailing list is now so unstable
 that I can't recommend people even bother with it.  I no longer have an
 account on 807-CITY (though they still graciously forward my mail to me) so
-there's not much I can do.  If there's sufficient interest (e-mail me!) I
-will set one up at my new address, Foxnet.
+there's not much I can do.
+
+However, Tomasz Motylewski has been so kind as to set up a new and improved
+mailing list; subscribe by sending a message with the BODY "subscribe
+linux-arcnet YOUR REAL NAME" to listserv@tichy.ch.uj.edu.pl.  Then, to
+submit messages to the list, mail to linux-arcnet@tichy.ch.uj.edu.pl.
+
+There are mailing list archives at:
+       http://tichy.ch.uj.edu.pl/lists/linux-arcnet
 
 Send all bug (or success) reports to me, then, not the list, since (as I
 mentioned) the list doesn't work.
diff --git a/MAGIC b/MAGIC
index bca7fd04bd3ffb6db1ea835b371b042319ef8f05..7a5830e6f28a3d01bee7e780112dd1d49b8c9c76 100644 (file)
--- a/MAGIC
+++ b/MAGIC
@@ -50,6 +50,7 @@ an ftp site, where 'X.Y.ZZ' is the kernel version for the ioctl list.
 
 Magic Name          Number  Structure            File
 ===========================================================================
+APM_BIOS_MAGIC      0x4101  struct apm_struct    include/linux/apm_bios.h
 CYCLADES_MAGIC      0x4359  struct cyclades_port include/linux/cyclades.h
 FASYNC_MAGIC        0x4601  struct fasync_struct include/linux/fs.h
 PTY_MAGIC           0x5001  struct pty_struct    drivers/char/pty.c
@@ -73,6 +74,7 @@ Ioctl Include File            Comments
 0x06   linux/lp.h
 0x12   linux/fs.h
 0x20   linux/cm206.h
+'A'    linux/apm_bios.h
 'C'    linux/soundcard.h
 'K'    linux/kd.h
 'M'    linux/soundcard.h
index 3af07f1695e196153ea43290d146acba6a32cbe7..6fc09d0b36bcd50eb720c230e5642b744d6040e7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 45
+SUBLEVEL = 46
 
 ARCH = i386
 
@@ -171,9 +171,11 @@ symlinks:
 oldconfig: symlinks
        $(CONFIG_SHELL) scripts/Configure -d arch/$(ARCH)/config.in
 
-xconfig: symlinks
-       ( cd scripts ; make kconfig.tk)
-       ./scripts/kconfig.tk
+xconfig: symlinks scripts/kconfig.tk
+       wish -f scripts/kconfig.tk
+
+scripts/kconfig.tk:
+       $(MAKE) -C scripts kconfig.tk
 
 config: symlinks
        $(CONFIG_SHELL) scripts/Configure arch/$(ARCH)/config.in
@@ -294,7 +296,8 @@ mrproper: clean
        rm -f $(TOPDIR)/include/linux/modules/*
 
 distclean: mrproper
-       rm -f core `find . -name '*.orig' -print`
+       rm -f core `find . \( -name '*.orig' -o -name '*~' -o -name '*.bak' \
+               -o -name '#*#' -o -name '.*.orig' \) -print`
 
 
 backup: mrproper
index 28a8e9154b91abf79a376382482dcfee0203dd11..a9879e0ed6ff0b1872649fc1336a692895b3e935 100644 (file)
@@ -624,7 +624,7 @@ sys_call_table:
        .quad sys_ftruncate, do_entSys, sys_setgid, sys_sendto, sys_shutdown
        .quad sys_socketpair, sys_mkdir, sys_rmdir, sys_utimes, do_entSys
        .quad do_entSys, sys_getpeername, do_entSys, do_entSys, sys_getrlimit
-       .quad sys_setrlimit, do_entSys, sys_setsid, do_entSys, do_entSys
+       .quad sys_setrlimit, do_entSys, sys_setsid, sys_quotactl, do_entSys
 /*150*/        .quad sys_getsockname, do_entSys, do_entSys, do_entSys, do_entSys
        .quad do_entSys, sys_sigaction, do_entSys, do_entSys, osf_getdirentries
        .quad osf_statfs, osf_fstatfs, do_entSys, do_entSys, do_entSys
index e58982b72b5927e4ca722102b1332d211414ef9a..4cad7f07ed4cb9ed76cc96c2798faf50ac682efe 100644 (file)
@@ -32,7 +32,7 @@
 #include <asm/system.h>
 #include <asm/io.h>
 
-extern int do_mount(kdev_t, const char *, char *, int, void *);
+extern int do_mount(kdev_t, const char *, const char *, char *, int, void *);
 extern int do_pipe(int *);
 
 extern struct file_operations * get_blkfops(unsigned int);
@@ -308,7 +308,7 @@ static int osf_ufs_mount(char * dirname, struct ufs_args * args, int flags)
        retval = getdev(tmp.devname, 0, &inode);
        if (retval)
                return retval;
-       retval = do_mount(inode->i_rdev, dirname, "ext2", flags, NULL);
+       retval = do_mount(inode->i_rdev, tmp.devname, dirname, "ext2", flags, NULL);
        if (retval)
                putdev(inode);
        iput(inode);
@@ -328,7 +328,7 @@ static int osf_cdfs_mount(char * dirname, struct cdfs_args * args, int flags)
        retval = getdev(tmp.devname, 1, &inode);
        if (retval)
                return retval;
-       retval = do_mount(inode->i_rdev, dirname, "iso9660", flags, NULL);
+       retval = do_mount(inode->i_rdev, tmp.devname, dirname, "iso9660", flags, NULL);
        if (retval)
                putdev(inode);
        iput(inode);
@@ -348,7 +348,7 @@ static int osf_procfs_mount(char * dirname, struct procfs_args * args, int flags
        dev = get_unnamed_dev();
        if (!dev)
                return -ENODEV;
-       retval = do_mount(dev, dirname, "proc", flags, NULL);
+       retval = do_mount(dev, "", dirname, "proc", flags, NULL);
        if (retval)
                put_unnamed_dev(dev);
        return retval;
index c1a327c9a43a2b09d31ccab643c0af269ca5a717..e79fb151d9ccb97147de58506e01e27b79d14e25 100644 (file)
@@ -1,32 +1,34 @@
 !
 !      setup.S         Copyright (C) 1991, 1992 Linus Torvalds
 !
-! This code performs all initialization procedures that should be done
-! before entering the protected mode. It's responsible for getting of all
-! system data offered by BIOS and for detection / selection of video
-! mode. All information is put in a "safe" place: 0x90000-0x901FF, i. e.
-! where the boot-block used to be.  It is then up to the protected mode
+! setup.s is responsible for getting the system data from the BIOS,
+! and putting them into the appropriate places in system memory.
+! both setup.s and system has been loaded by the bootblock.
+!
+! This code asks the bios for memory/disk/other parameters, and
+! puts them in a "safe" place: 0x90000-0x901FF, ie where the
+! boot-block used to be. It is then up to the protected mode
 ! system to read them from there before the area is overwritten
 ! for buffer-blocks.
 !
 ! Move PS/2 aux init code to psaux.c
 ! (troyer@saifr00.cfsat.Honeywell.COM) 03Oct92
 !
-! Some changes and additional features by Christoph Niemann,
+! some changes and additional features by Christoph Niemann,
 ! March 1993/June 1994 (Christoph.Niemann@linux.org)
 !
-! Completely new video-mode handling code, VESA mode detection, support
-! for new Cirrus Logic cards and some additional changes
-! by Martin Mares <mj@k332.feld.cvut.cz>, October 1995.
+! add APM BIOS checking by Stephen Rothwell, May 1994
+! (Stephen.Rothwell@pd.necisa.oz.au)
 !
 
-! NOTE! These had better be the same as in bootsect.S!
+! NOTE! These had better be the same as in bootsect.s!
 #define __ASSEMBLY__
 #include <linux/config.h>
 #include <asm/segment.h>
 
-! Uncomment this if you want the BIOS mode numbers to be listed
-!#define SHOW_BIOS_MODES
+#ifndef SVGA_MODE
+#define SVGA_MODE ASK_VGA
+#endif
 
 ! Signature words to ensure LILO loaded us right
 #define SIG1   0xAA55
@@ -47,7 +49,6 @@ begbss:
 
 entry start
 start:
-
 ! Bootlin depends on this being done early
        mov     ax,#0x01500
        mov     dl,#0x81
@@ -74,14 +75,12 @@ fin:        ret
 ! Part of above routine, this one just prints ascii al
 
 prnt1: push    ax
-       push    bx
        push    cx
        xor     bh,bh
        mov     cx,#0x01
        mov     ah,#0x0e
        int     0x10
        pop     cx
-       pop     bx
        pop     ax
        ret
 
@@ -149,10 +148,43 @@ good_sig:
        xor     bx,bx           ! clear bx
        int     0x16
 
-! Check for video adapter and its parameters
-! Video mode selection is also handled here
+! check for EGA/VGA and some config parameters
 
-       call    video
+       mov     ah,#0x12
+       mov     bl,#0x10
+       int     0x10
+       mov     [8],ax
+       mov     [10],bx
+       mov     [12],cx
+       mov     ax,#0x5019
+       cmp     bl,#0x10
+       je      novga
+       mov     ax,#0x1a00      ! Added check for EGA/VGA discrimination
+       int     0x10
+       mov     bx,ax
+       mov     ax,#0x5019
+       movb    [15],#0         ! by default, no VGA
+       cmp     bl,#0x1a        ! 1a means VGA, anything else EGA or lower
+       jne     novga
+       movb    [15],#1         ! we've detected a VGA
+       call    chsvga
+novga: mov     [14],al
+       mov     ah,#0x03        ! read cursor pos
+       xor     bh,bh           ! clear bh
+       int     0x10            ! save it in known place, con_init fetches
+       mov     [0],dx          ! it from 0x90000.
+       
+! Get video-card data:
+       
+       mov     ah,#0x0f
+       int     0x10
+       mov     [4],bx          ! bh = display page
+       mov     [6],ax          ! al = video mode, ah = window width
+       xor     ax,ax
+       mov     es,ax           ! Access low memory
+       seg es
+       mov     ax,[0x485]      ! POINTS - Height of character matrix
+       mov     [16],ax
 
 ! Get hd0 data
 
@@ -209,6 +241,48 @@ is_disk1:
        jz      no_psmouse
        mov     [0x1ff],#0xaa   ! device present
 no_psmouse:
+
+#ifdef CONFIG_APM
+! check for APM BIOS
+
+       mov     [64],#0         ! version == 0 means no APM BIOS
+
+       mov     ax,#0x05300     ! APM BIOS installation check
+       xor     bx,bx
+       int     0x15
+       jc      done_apm_bios   ! error -> no APM BIOS
+
+       cmp     bx,#0x0504d     ! check for "PM" signature
+       jne     done_apm_bios   ! no signature -> no APM BIOS
+
+       mov     [64],ax         ! record the APM BIOS version
+       mov     [76],cx         !       and flags
+       and     cx,#0x02        ! Is 32 bit supported?
+       je      done_apm_bios   !       no ...
+
+       mov     ax,#0x05304     ! Disconnect first just in case
+       xor     bx,bx
+       int     0x15            ! ignore return code
+
+       mov     ax,#0x05303     ! 32 bit connect
+       xor     bx,bx
+       int     0x15
+       jc      no_32_apm_bios  ! error
+
+       mov     [66],ax         ! BIOS code segment
+       mov     [68],ebx        ! BIOS entry point offset
+       mov     [72],cx         ! BIOS 16 bit code segment
+       mov     [74],dx         ! BIOS data segment
+       mov     [78],si         ! BIOS code segment length
+       mov     [80],di         ! BIOS data segment length
+       jmp     done_apm_bios
+
+no_32_apm_bios:
+       and     [76], #0xfffd   ! remove 32 bit support bit
+
+done_apm_bios:
+#endif
+
 ! now we want to move to protected mode ...
 
        cli                     ! no interrupts allowed !
@@ -305,16 +379,17 @@ end_move:
 ! Well, now's the time to actually move into protected mode. To make
 ! things as simple as possible, we do no register set-up or anything,
 ! we let the gnu-compiled 32-bit programs do that. We just jump to
-! absolute address 0x10000, in 32-bit protected mode.
+! absolute address 0x00000, in 32-bit protected mode.
 !
 ! Note that the short jump isn't strictly needed, although there are
 ! reasons why it might be a good idea. It won't hurt in any case.
 !
-       mov     ax,#1           ! protected mode (PE) bit
+       xor     ax,ax
+       inc     ax              ! protected mode (PE) bit
        lmsw    ax              ! This is it!
        jmp     flush_instr
 flush_instr:
-       xor     bx,bx           ! Flag to indicate a boot
+       mov     bx,#0           ! Flag to indicate a boot
        jmpi    0x1000,KERNEL_CS        ! jmp offset 1000 of segment 0x10 (cs)
 
 ! This routine checks that the keyboard command queue is empty
@@ -334,6 +409,61 @@ no_output:
        test    al,#2           ! is input buffer full?
        jnz     empty_8042      ! yes - loop
        ret
+!
+! Read a key and return the (US-)ascii code in al, scan code in ah
+!
+getkey:
+       xor     ah,ah
+       int     0x16
+       ret
+
+!
+! Read a key with a timeout of 30 seconds. The cmos clock is used to get
+! the time.
+!
+getkt:
+       call    gettime
+       add     al,#30          ! wait 30 seconds
+       cmp     al,#60
+       jl      lminute
+       sub     al,#60
+lminute:
+       mov     cl,al
+again: mov     ah,#0x01
+       int     0x16
+       jnz     getkey          ! key pressed, so get it
+       call    gettime
+       cmp     al,cl
+       jne     again
+       mov     al,#0x20        ! timeout, return default char `space'
+       ret
+
+!
+! Flush the keyboard buffer
+!
+flush: mov     ah,#0x01
+       int     0x16
+       jz      empty
+       xor     ah,ah
+       int     0x16
+       jmp     flush
+empty: ret
+
+!
+! Read the cmos clock. Return the seconds in al
+!
+gettime:
+       push    cx
+       mov     ah,#0x02
+       int     0x1a
+       mov     al,dh                   ! dh contains the seconds
+       and     al,#0x0f
+       mov     ah,dh
+       mov     cl,#0x04
+       shr     ah,cl
+       aad
+       pop     cx
+       ret
 
 !
 ! Delay is needed after doing i/o
@@ -342,199 +472,43 @@ delay:
        .word   0x00eb                  ! jmp $+2
        ret
 
-!
-! Video card / mode detection. We do some hardware testing and build
-! a video mode list (placed directly after our code and data). Then we
-! choose the right mode given in the configuration or ask the user if
-! we are requested to do so. After all, all video parameters are stored
-! for later perusal by the kernel.
-!
+! Routine trying to recognize type of SVGA-board present (if any)
+! and if it recognize one gives the choices of resolution it offers.
+! If one is found the resolution chosen is given by al,ah (rows,cols).
 
-video: movb    [15],#0         ! Default is no VGA
-       mov     ax,[0x01fa]
-       push    ds
+chsvga:        cld
        push    ds
-       pop     fs              ! In this routine: FS=orig. DS
-       push    cs
-       pop     ds              ! ES=DS=CS
        push    cs
-       pop     es
+       mov     ax,[0x01fa]
+       pop     ds
        mov     modesave,ax
-       lea     di,modelist     ! ES:DI points to current item in our mode list
-       mov     eax,#0x50190000 ! Store current mode: 80x25
-       cld
-       stosd
-
-       mov     ah,#0x12        ! Check EGA/VGA
-       mov     bl,#0x10
-       int     0x10
-       seg     fs
-       mov     [10],bx         ! Used for identification of VGA in the kernel
-       cmp     bl,#0x10        ! Not EGA nor VGA -> 80x25 only
-       je      selmd1
-
-       mov     eax,#0x5032FFFF ! EGA or VGA: 80x50 supported
-       stosd
-
-       mov     ax,#0x1a00      ! Added check for EGA/VGA discrimination
-       int     0x10
-       cmp     al,#0x1a        ! 1a means VGA, anything else EGA
-       jne     selmd1
-       seg     fs
-       movb    [15],#1         ! We've detected a VGA
-
-       mov     eax,#0x501cFFFE ! VGA: 80x28 supported
-       stosd
-
-       lea     si,vgatable     ! Test all known SVGA adapters
-dosvga:        lodsw
-       mov     bp,ax           ! Default mode table
-       or      ax,ax
-       jz      didsv
-       lodsw                   ! Pointer to test routine
-       push    si
-       push    di
-       push    es
-       mov     bx,#0xc000
-       mov     es,bx
-       call    ax              ! Call test routine
-       pop     es
-       pop     di
-       pop     si
-       or      bp,bp
-       jz      dosvga
-       mov     si,bp           ! Found, copy the modes
-       mov     ah,#0
-cpsvga:        lodsb
-       or      al,al
-       jz      didsv
-       stosw
-       movsw
-       jmp     cpsvga
-
-selmd1:        jmp     selmd
-
-didsv: mov     ax,#0x4f00      ! Fetch VESA information to ES:DI+0x400
-       add     di,#0x400
-       int     0x10
-       sub     di,#0x400
-       cmp     al,#0x4f
-       jne     selmd
-       lgs     bx,(di+0x40e)
-       cmp     (di+0x400),#0x4556
-       jne     selmd
-       cmp     (di+0x402),#0x4153
-       jne     selmd
-
-vesa1: seg     gs
-       mov     cx,(bx)
-       add     bx,#2
-       cmp     cx,#0xffff
-       je      selmd
-       mov     ax,#0x4f01
-       add     di,#0xc00
-       int     0x10
-       sub     di,#0xc00
-       cmp     al,#0x4f
-       jne     selmd
-       testb   (di+0xc00),#0x10        ! Is it a text mode?
-       jne     vesa1
-       testb   (di+0xc00),#0x08        ! Has it colors?
-       je      vesa1
-       mov     dh,(di+0xc12)   ! DX=dimensions, CX=mode
-       mov     dl,(di+0xc14)
-
-       lea     si,modelist     ! Check if it's already on the list
-vesa2: lodsw
-       lodsw
-       cmp     ax,dx
-       je      vesa1
-       cmp     si,di
-       jc      vesa2
-
-       mov     ax,cx           ! New mode, store it
-       stosw
-       mov     ax,dx
-       stosw
-       jmp     vesa1
-
-!
-! Video mode table built. Determine the mode we should use and set it.
-!
-selmd: mov     ax,modesave
-       cmp     ax,#NORMAL_VGA  ! Current mode (80x25)
-       je      defmd1
-       cmp     ax,#EXTENDED_VGA ! 80x50 mode
-       je      try50
+       mov     ax,#0xc000
+       mov     es,ax
+       mov     ax,modesave
+       cmp     ax,#NORMAL_VGA
+       je      defvga
+       cmp     ax,#EXTENDED_VGA
+       je      vga50
        cmp     ax,#ASK_VGA
-       jne     usemd
-banner:        lea     si,keymsg
+       jne     svga
+       lea     si,msg1
        call    prtstr
        call    flush
 nokey: call    getkt
-       cmp     al,#0x0d        ! ENTER ?
-       je      listm           ! yes - manual mode selection
-       cmp     al,#0x20        ! SPACE ?
-       je      defmd1          ! no - repeat
+       cmp     al,#0x0d                ! enter ?
+       je      svga                    ! yes - svga selection
+       cmp     al,#0x20                ! space ?
+       je      defvga                  ! no - repeat
        call    beep
        jmp     nokey
-
-defmd1:        br      defmd
-
-listm: call    listmodes       ! List all available modes
-keymd: call    getkey          ! Get key representing mode ID
-       xor     ah,ah
-       sub     al,#0x30
-       jc      keymd
-       cmp     al,#10
-       jc      usemd
-       sub     al,#39
-       cmp     al,#10
-       jc      keymd
-       cmp     al,#26
-       jnc     keymd
-       jmp     usemd
-
-try50: mov     ax,#1           ! 80x50 is mode #1
-usemd: shl     ax,#2           ! We're requested to set mode in AX
-       lea     si,modelist
-       add     si,ax
-       cmp     si,di
-       jc      mdok
-       cmp     modesave,#ASK_VGA
-       je      keymd
-       lea     si,undefd
-       call    prtstr
-       jmp     banner
-
-mdok:  lodsw                   ! AX=mode number
-       cmp     ah,#0xff
-       jz      mdspec
-       or      ax,ax
-       jz      mdsetd
-       or      ah,ah
-       jz      mdset
-       mov     bx,ax
-       mov     ax,#0x4f02
-mdset: int     0x10
-mdsetd:        lodsb                   ! AL=number of lines
-       jmp     getpar
-
-mdspec:        inc     ax              ! Special modes
-       jz      m80x50
-
-m80x28: mov    ax,#0x1111      ! Setting 80x28 (VGA with EGA font)
-       xor     bl,bl
-       int     0x10            ! use 9x14 fontset (28 lines on VGA)
-       mov     ah,#0x01
-       mov     cx,#0x0b0c
-       int     0x10            ! turn on cursor (scan lines 11 to 12)
-       mov     al,#28
-       jmp     getpar
-
-m80x50: mov    ax,#0x1112      ! Setting 80x50 (EGA/VGA)
+defvga:        mov     ax,#0x5019
+       pop     ds
+       ret
+/* extended vga mode: 80x50 */
+vga50:
+       mov     ax,#0x1112
        xor     bl,bl
-       int     0x10            ! use 8x8 font set
+       int     0x10            ! use 8x8 font set (50 lines on VGA)
        mov     ax,#0x1200
        mov     bl,#0x20
        int     0x10            ! use alternate print screen
@@ -544,65 +518,28 @@ m80x50: mov       ax,#0x1112      ! Setting 80x50 (EGA/VGA)
        mov     ah,#0x01
        mov     cx,#0x0607
        int     0x10            ! turn on cursor (scan lines 6 to 7)
-       mov     al,#50
-       jmp     getpar
-
-defmd: mov     al,#25          ! Default is 25 lines
-
-!
-! Correct video mode set. Determine the remaining parameters.
-!
-
-getpar:        pop     ds              ! Restore original DS
-       mov     [14],al         ! Number of lines
-
-       mov     ah,#0x03        ! read cursor pos
-       xor     bh,bh           ! clear bh
-       int     0x10            ! save it in known place, con_init fetches
-       mov     [0],dx          ! it from 0x90000.
-       
-       mov     ah,#0x0f
-       int     0x10
-       mov     [4],bx          ! bh = display page
-       mov     [6],ax          ! al = video mode, ah = window width
-       xor     ax,ax
-       mov     es,ax           ! Access low memory
-       seg es
-       mov     ax,[0x485]      ! POINTS - Height of character matrix
-       mov     [16],ax
-
-       ret                     ! Well done...
-
-!
-! Table of all known SVGA cards. For each card, we store a pointer to
-! a table of video modes supported by the card and a pointer to a routine
-! used for testing of presence of the card.
-!
-
-vgatable:
-       .word   s3_md, s3_test
-       .word   ati_md, ati_test
-       .word   ahead_md, ahead_test
-       .word   chips_md, chips_test
-       .word   cirrus2_md, cirrus2_test
-       .word   cirrus1_md, cirrus1_test
-       .word   everex_md, everex_test
-       .word   genoa_md, genoa_test
-       .word   oak_md, oak_test
-       .word   paradise_md, paradise_test
-       .word   trident_md, trident_test
-       .word   tseng_md, tseng_test
-       .word   video7_md, video7_test
-       .word   0
-
+       pop     ds
+       mov     ax,#0x5032      ! return 80x50
+       ret
+/* extended vga mode: 80x28 */
+vga28:
+       pop     ax              ! clean the stack
+       mov     ax,#0x1111
+       xor     bl,bl
+       int     0x10            ! use 9x14 fontset (28 lines on VGA)
+       mov     ah, #0x01
+       mov     cx,#0x0b0c
+       int     0x10            ! turn on cursor (scan lines 11 to 12)
+       pop     ds
+       mov     ax,#0x501c      ! return 80x28
+       ret
+/* svga modes */
 !
-! Test routines and mode tables:
+!      test for presence of an S3 VGA chip. The algorithm was taken
+!      from the SuperProbe package of XFree86 1.2.1
+!      report bugs to Christoph.Niemann@linux.org
 !
-
-! S3 - The test algorithm was taken from the SuperProbe package
-! for XFree86 1.2.1. Report bugs to Christoph.Niemann@linux.org
-
-s3_test:
+svga:   cld
        mov     cx,#0x0f35      ! we store some constants in cl/ch
        mov     dx,#0x03d4
        movb    al,#0x38
@@ -667,64 +604,29 @@ s3_1:     mov     ax,#0x4838      ! allow writing to special regs by putting
        repne
        scasb
        je      no_s31
+       lea     si,dsc_S3       ! table of descriptions of video modes for BIOS
+       lea     di,mo_S3        ! table of sizes of video modes for my BIOS
        movb    ah,bh
        movb    al,#0x38
-       jmp     s3rest
+       call    outidx          ! restore old value of CRT register 0x38
+       br      selmod          ! go ask for video mode
 no_s3: movb    al,#0x35        ! restore CRT register 0x35
        movb    ah,bl
        call    outidx
-no_s31:        xor     bp,bp           ! Detection failed
-s3rest:        movb    ah,bh
-       movb    al,#0x38        ! restore old value of CRT register 0x38
-outidx:        out     dx,al           ! Write to indexed VGA register
-       push    ax              ! AL=index, AH=data, DX=index reg port
-       mov     al,ah
-       inc     dx
-       out     dx,al
-       dec     dx
-       pop     ax
-       ret
-
-tstidx:        out     dx,ax           ! OUT DX,AX and inidx
-inidx: out     dx,al           ! Read from indexed VGA register
-       inc     dx              ! AL=index, DX=index reg port -> AL=data
-       in      al,dx
-       dec     dx
-       ret
-
-idS3:  .byte   0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95
-       .byte   0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0
-
-s3_md: .byte   0x54, 0x2b, 0x84
-       .byte   0x55, 0x19, 0x84
-       .byte   0
-
-! ATI cards.
+no_s31:        movb    ah,bh
+       movb    al,#0x38
+       call    outidx          ! restore old value of CRT register 0x38
 
-ati_test:
-       lea     si,idati
+       lea     si,idati                ! Check ATI 'clues'
        mov     di,#0x31
        mov     cx,#0x09
        repe
        cmpsb
-       je      atiok
-       xor     bp,bp
-atiok: ret
-
-idati: .ascii  "761295520"
-
-ati_md:        .byte   0x23, 0x19, 0x84
-       .byte   0x33, 0x2c, 0x84
-       .byte   0x22, 0x1e, 0x64
-       .byte   0x21, 0x19, 0x64
-       .byte   0x58, 0x21, 0x50
-       .byte   0x5b, 0x1e, 0x50
-       .byte   0
-
-! AHEAD
-
-ahead_test:
-       mov     ax,#0x200f
+       jne     noati
+       lea     si,dscati
+       lea     di,moati
+       br      selmod
+noati: mov     ax,#0x200f              ! Check Ahead 'clues'
        mov     dx,#0x3ce
        out     dx,ax
        inc     dx
@@ -732,23 +634,11 @@ ahead_test:
        cmp     al,#0x20
        je      isahed
        cmp     al,#0x21
-       je      isahed
-       xor     bp,bp
-isahed:        ret
-
-ahead_md:
-       .byte   0x22, 0x2c, 0x84
-       .byte   0x23, 0x19, 0x84
-       .byte   0x24, 0x1c, 0x84
-       .byte   0x2f, 0x32, 0xa0
-       .byte   0x32, 0x22, 0x50
-       .byte   0x34, 0x42, 0x50
-       .byte   0
-
-! Chips & Tech.
-
-chips_test:
-       mov     dx,#0x3c3
+       jne     noahed
+isahed:        lea     si,dscahead
+       lea     di,moahead
+       br      selmod
+noahed:        mov     dx,#0x3c3               ! Check Chips & Tech. 'clues'
        in      al,dx
        or      al,#0x10
        out     dx,al
@@ -759,20 +649,12 @@ chips_test:
        in      al,dx
        and     al,#0xef
        out     dx,al
-       cmp     bl,#0xa5
-       je      cantok
-       xor     bp,bp
-cantok:        ret
-
-chips_md:
-       .byte   0x60, 0x19, 0x84
-       .byte   0x61, 0x32, 0x84
-       .byte   0
-
-! Cirrus Logic 5X0
-
-cirrus1_test:
-       mov     dx,#0x3d4
+       cmp     bl,[idcandt]
+       jne     nocant
+       lea     si,dsccandt
+       lea     di,mocandt
+       br      selmod
+nocant:        mov     dx,#0x3d4               ! Check Cirrus 'clues'
        mov     al,#0x0c
        out     dx,al
        inc     dx
@@ -805,76 +687,19 @@ cirrus1_test:
        out     dx,al
        in      al,dx
        cmp     al,#0x01
-       je      iscirr
-nocirr:        xor     bp,bp
-iscirr: mov    dx,#0x3d4
+       jne     nocirr
+       call    rst3d4  
+       lea     si,dsccirrus
+       lea     di,mocirrus
+       br      selmod
+rst3d4:        mov     dx,#0x3d4
        mov     al,bl
        xor     ah,ah
        shl     ax,#8
        add     ax,#0x0c
        out     dx,ax
-       ret
-
-cirrus1_md:
-       .byte   0x1f, 0x19, 0x84
-       .byte   0x20, 0x2c, 0x84
-       .byte   0x22, 0x1e, 0x84
-       .byte   0x31, 0x25, 0x64
-       .byte   0
-
-! Cirrus Logic 54XX
-
-cirrus2_test:
-       mov     dx,#0x3c4
-       mov     al,#6
-       call    inidx
-       mov     bl,al                   ! BL=backup
-       mov     al,#6
-       xor     ah,ah
-       call    tstidx
-       cmp     al,#0x0f
-       jne     c2fail
-       mov     ax,#0x1206
-       call    tstidx
-       cmp     al,#0x12
-       jne     c2fail
-       mov     al,#0x1e
-       call    inidx
-       mov     bh,al
-       and     bh,#0xc0
-       mov     ah,bh
-       mov     al,#0x1e
-       call    tstidx
-       xor     al,bh
-       and     al,#0x3f
-       jne     c2xx
-       mov     al,#0x1e
-       mov     ah,bh
-       or      ah,#0x3f
-       call    tstidx
-       xor     al,bh
-       xor     al,#0x3f
-       and     al,#0x3f
-c2xx:  pushf
-       mov     al,#0x1e
-       mov     ah,bh
-       out     dx,ax
-       popf
-       je      c2done
-c2fail:        xor     bp,bp
-c2done:        mov     al,#6
-       mov     ah,bl
-       out     dx,ax
-       ret
-
-cirrus2_md:
-       .byte   0x14, 0x19, 0x84
-       .byte   0x54, 0x2b, 0x84
-       .byte   0
-
-! Everex / Trident
-
-everex_test:
+       ret     
+nocirr:        call    rst3d4                  ! Check Everex 'clues'
        mov     ax,#0x7000
        xor     bx,bx
        int     0x10
@@ -882,32 +707,15 @@ everex_test:
        jne     noevrx
        shr     dx,#4
        cmp     dx,#0x678
-       je      evtrid
+       je      istrid
        cmp     dx,#0x236
-       jne     evrxok
-evtrid:        lea     bp,trident_md
-evrxok:        ret
-
-noevrx:        xor     bp,bp
-       ret
-
-everex_md:
-       .byte   0x03, 0x22, 0x50
-       .byte   0x04, 0x3c, 0x50
-       .byte   0x07, 0x2b, 0x64
-       .byte   0x08, 0x4b, 0x64
-       .byte   0x0a, 0x19, 0x84
-       .byte   0x0b, 0x2c, 0x84
-       .byte   0x16, 0x1e, 0x50
-       .byte   0x18, 0x1b, 0x64
-       .byte   0x21, 0x40, 0xa0
-       .byte   0x40, 0x1e, 0x84
-       .byte   0
-
-! Genoa.
-
-genoa_test:
-       lea     si,idgenoa              ! Check Genoa 'clues'
+       je      istrid
+       lea     si,dsceverex
+       lea     di,moeverex
+       br      selmod
+istrid:        lea     cx,ev2tri
+       jmp     cx
+noevrx:        lea     si,idgenoa              ! Check Genoa 'clues'
        xor     ax,ax
        seg es
        mov     al,[0x37]
@@ -923,72 +731,32 @@ l1:       inc     si
        seg es
        cmp     al,(di)
 l2:    loope   l1
-       or      cx,cx
-       je      isgen
-       xor     bp,bp
-isgen: ret
-
-idgenoa: .byte 0x77, 0x00, 0x99, 0x66
-
-genoa_md:
-       .byte   0x58, 0x20, 0x50
-       .byte   0x5a, 0x2a, 0x64
-       .byte   0x60, 0x19, 0x84
-       .byte   0x61, 0x1d, 0x84
-       .byte   0x62, 0x20, 0x84
-       .byte   0x63, 0x2c, 0x84
-       .byte   0x64, 0x3c, 0x84
-       .byte   0x6b, 0x4f, 0x64
-       .byte   0x72, 0x3c, 0x50
-       .byte   0x74, 0x42, 0x50
-       .byte   0x78, 0x4b, 0x64
-       .byte   0
-
-! OAK
-
-oak_test:
+       cmp     cx,#0x00
+       jne     nogen
+       lea     si,dscgenoa
+       lea     di,mogenoa
+       br      selmod
+nogen: cld
        lea     si,idoakvga
        mov     di,#0x08
        mov     cx,#0x08
        repe
        cmpsb
-       je      isoak
-       xor     bp,bp
-isoak: ret
-
-idoakvga: .ascii  "OAK VGA "
-
-oak_md: .byte  0x4e, 0x3c, 0x50
-       .byte   0x4f, 0x3c, 0x84
-       .byte   0x50, 0x19, 0x84
-       .byte   0x51, 0x2b, 0x84
-       .byte   0
-
-! WD Paradise.
-
-paradise_test:
-       lea     si,idparadise
+       jne     nooak
+       lea     si,dscoakvga
+       lea     di,mooakvga
+       br      selmod
+nooak: cld
+       lea     si,idparadise           ! Check Paradise 'clues'
        mov     di,#0x7d
        mov     cx,#0x04
        repe
        cmpsb
-       je      ispara
-       xor     bp,bp
-ispara:        ret
-
-idparadise:    .ascii  "VGA="
-
-paradise_md:
-       .byte   0x41, 0x22, 0x50
-       .byte   0x47, 0x1c, 0x84
-       .byte   0x55, 0x19, 0x84
-       .byte   0x54, 0x2c, 0x84
-       .byte   0
-
-! Trident.
-
-trident_test:
-       mov     dx,#0x3c4
+       jne     nopara
+       lea     si,dscparadise
+       lea     di,moparadise
+       br      selmod
+nopara:        mov     dx,#0x3c4               ! Check Trident 'clues'
        mov     al,#0x0e
        out     dx,al
        inc     dx
@@ -1007,24 +775,11 @@ setb2:   or      al,#0x02        !
 clrb2: out     dx,al
        and     ah,#0x0f
        cmp     ah,#0x02
-       je      istrid
-       xor     bp,bp
-istrid:        ret
-
-trident_md:
-       .byte   0x50, 0x1e, 0x50
-       .byte   0x51, 0x2b, 0x50
-       .byte   0x52, 0x3c, 0x50
-       .byte   0x57, 0x19, 0x84
-       .byte   0x58, 0x1e, 0x84
-       .byte   0x59, 0x2b, 0x84
-       .byte   0x5a, 0x3c, 0x84
-       .byte   0
-
-! Tseng.
-
-tseng_test:
-       mov     dx,#0x3cd
+       jne     notrid
+ev2tri:        lea     si,dsctrident
+       lea     di,motrident
+       jmp     selmod
+notrid:        mov     dx,#0x3cd               ! Check Tseng 'clues'
        in      al,dx                   ! Could things be this simple ! :-)
        mov     bl,al
        mov     al,#0x55
@@ -1034,22 +789,11 @@ tseng_test:
        mov     al,bl
        out     dx,al
        cmp     ah,#0x55
-       je      istsen
-       xor     bp,bp
-istsen:        ret
-
-tseng_md:
-       .byte   0x26, 0x3c, 0x50
-       .byte   0x2a, 0x28, 0x64
-       .byte   0x23, 0x19, 0x84
-       .byte   0x24, 0x1c, 0x84
-       .byte   0x22, 0x2c, 0x84
-       .byte   0
-
-! Video7.
-
-video7_test:
-       mov     dx,#0x3cc
+       jne     notsen
+       lea     si,dsctseng
+       lea     di,motseng
+       jmp     selmod
+notsen:        mov     dx,#0x3cc               ! Check Video7 'clues'
        in      al,dx
        mov     dx,#0x3b4
        and     al,#0x01
@@ -1078,78 +822,115 @@ even7:  mov     al,#0x0c
        mov     al,#0x55
        xor     al,#0xea
        cmp     al,bh
-       je      isvid7
-       xor     bp,bp
-isvid7:        ret
-
-video7_md:
-       .byte   0x40, 0x2b, 0x50
-       .byte   0x43, 0x3c, 0x50
-       .byte   0x44, 0x3c, 0x64
-       .byte   0x41, 0x19, 0x84
-       .byte   0x42, 0x2c, 0x84
-       .byte   0x45, 0x1c, 0x84
-       .byte   0
-
-!
-! Displaying of the mode list.
-!
-
-listmodes:
-       lea     si,listhdr
+       jne     novid7
+       lea     si,dscvideo7
+       lea     di,movideo7
+       jmp     selmod
+novid7:        lea     si,dsunknown
+       lea     di,mounknown
+selmod:        xor     cx,cx
+       mov     cl,(di)
+       mov     ax,modesave
+       cmp     ax,#ASK_VGA
+       je      askmod
+       cmp     ax,#NORMAL_VGA
+       je      askmod
+       cmp     al,cl
+       jl      gotmode
+       push    si
+       lea     si,msg4
        call    prtstr
-       lea     bx,modelist
-       mov     cl,#0x30
-listm1:        mov     modenr,cl
-       lea     si,modestring
+       pop     si
+askmod:        push    si
+       lea     si,msg2
        call    prtstr
-       mov     al,(bx+3)
+       pop     si
+       push    si
+       push    cx
+tbl:   pop     bx
+       push    bx
+       mov     al,bl
+       sub     al,cl
+       call    modepr
+       lodsw
+       xchg    al,ah
        call    dprnt
+       xchg    ah,al
+       push    ax
        mov     al,#0x78
        call    prnt1
-       mov     al,(bx+2)
+       pop     ax
        call    dprnt
-#ifdef SHOW_BIOS_MODES
-       mov     al,#0x20
-       call    prnt1
-       mov     al,#0x28
-       call    prnt1
-       mov     al,(bx+1)
-       call    prthex
-       mov     al,(bx)
-       call    prthex
-       mov     al,#0x29
-       call    prnt1
-#endif
-       lea     si,crlf
+       push    si
+       lea     si,crlf         ! print CR+LF
        call    prtstr
-       add     bx,#4
-       inc     cl
-       cmp     cl,#0x3a
-       jnz     listm2
-       mov     cl,#0x61
-listm2:        cmp     bx,di
-       jc      listm1
-       lea     si,prompt
-       br      prtstr
-
-! Routine to print a hexadecimal byte (AL) on screen.
-
-#ifdef SHOW_BIOS_MODES
-prthex:        push    ax
-       shr     al,#4
-       call    prth1
+       pop     si
+       loop    tbl
+       pop     cx
+       lea     si,msg3
+       call    prtstr
+       pop     si
+       add     cl,#0x30
+       jmp     nonum
+nonumb:        call    beep
+nonum: call    getkey
+       cmp     al,#0x30        ! ascii `0'
+       jb      nonumb
+       cmp     al,#0x3a        ! ascii `9'
+       jbe     number
+       cmp     al,#0x61        ! ascii `a'
+       jb      nonumb
+       cmp     al,#0x7a        ! ascii `z'
+       ja      nonumb
+       sub     al,#0x27
+       cmp     al,cl
+       jae     nonumb
+       sub     al,#0x30
+       jmp     gotmode
+number: cmp    al,cl
+       jae     nonumb
+       sub     al,#0x30
+gotmode:       xor     ah,ah
+       or      al,al
+       beq     vga50
+       push    ax
+       dec     ax
+       beq     vga28
+       add     di,ax
+       mov     al,(di)
+       int     0x10
        pop     ax
-prth1: and     al,#15
-       cmp     al,#10
-       jc      prth2
-       add     al,#7
-prth2: add     al,#0x30
-       br      prnt1
-#endif
+       shl     ax,#1
+       add     si,ax
+       lodsw
+       pop     ds
+       ret
+
+! Routine to write al into a VGA-register that is
+! accessed via an index register
+!
+! dx contains the address of the index register
+! al contains the index
+! ah contains the value to write to the data register (dx + 1)
+!
+! no registers are changed
+
+outidx:        out     dx,al
+       push    ax
+       mov     al,ah
+       inc     dx
+       out     dx,al
+       dec     dx
+       pop     ax
+       ret
+inidx: out     dx,al
+       inc     dx
+       in      al,dx
+       dec     dx
+       ret
 
 ! Routine to print a decimal value on screen, the value to be
-! printed is put in AL (i.e 0-255). 
+! printed is put in al (i.e 0-255). 
 
 dprnt: push    ax
        push    cx
@@ -1170,65 +951,25 @@ skip10:  mov     al,ah
        ret
 
 !
-! Read a key and return the (US-)ascii code in al, scan code in ah
+! Routine to print the mode number key on screen. Mode numbers
+! 0-9 print the ascii values `0' to '9', 10-35 are represented by
+! the letters `a' to `z'. This routine prints some spaces around the
+! mode no.
 !
-getkey:
-       xor     ah,ah
-       int     0x16
-       ret
 
-!
-! Read a key with a timeout of 30 seconds. The cmos clock is used to get
-! the time.
-!
-getkt:
-       call    gettime
-       add     al,#30          ! wait 30 seconds
-       cmp     al,#60
-       jl      lminute
-       sub     al,#60
-lminute:
-       mov     cl,al
-again: mov     ah,#0x01
-       int     0x16
-       jnz     getkey          ! key pressed, so get it
-       call    gettime
-       cmp     al,cl
-       jne     again
-       mov     al,#0x20        ! timeout, return default char `space'
-       ret
-
-!
-! Flush the keyboard buffer
-!
-flush: mov     ah,#0x01
-       int     0x16
-       jz      empty
-       xor     ah,ah
-       int     0x16
-       jmp     flush
-empty: ret
-
-!
-! Read the cmos clock. Return the seconds in al
-!
-gettime:
-       push    cx
-       mov     ah,#0x02
-       int     0x1a
-       mov     al,dh                   ! dh contains the seconds
-       and     al,#0x0f
-       mov     ah,dh
-       mov     cl,#0x04
-       shr     ah,cl
-       aad
-       pop     cx
+modepr:        push    ax
+       cmp     al,#0x0a
+       jb      digit           ! Here is no check for number > 35
+       add     al,#0x27
+digit: add     al,#0x30
+       mov     modenr, al
+       push    si
+       lea     si, modestring
+       call    prtstr
+       pop     si
+       pop     ax
        ret
 
-!
-! Descriptor table for our protected mode transition.
-!
-
 gdt:
        .word   0,0,0,0         ! dummy
 
@@ -1252,37 +993,69 @@ gdt_48:
        .word   0x800           ! gdt limit=2048, 256 GDT entries
        .word   512+gdt,0x9     ! gdt base = 0X9xxxx
 
-!
-! Assorted messages.
-!
-
-keymsg:                .ascii  "Press <RETURN> to see video modes available, <SPACE> to continue or wait 30 secs"
-               db      0x0d, 0x0a, 0x00
-listhdr:       .ascii  "Mode:  COLSxROWS:"
+msg1:          .ascii  "Press <RETURN> to see SVGA-modes available, <SPACE> to continue or wait 30 secs."
+               db      0x0d, 0x0a, 0x0a, 0x00
+msg2:          .ascii  "Mode:  COLSxROWS:"
                db      0x0d, 0x0a, 0x0a, 0x00
-prompt:                db      0x0d, 0x0a
+msg3:          db      0x0d, 0x0a
                .ascii  "Choose mode by pressing the corresponding number or letter."
 crlf:          db      0x0d, 0x0a, 0x00
-undefd:                .ascii  "You passed an undefined mode number to setup. Please choose a new mode."
+msg4:          .ascii  "You passed an undefined mode number to setup. Please choose a new mode."
                db      0x0d, 0x0a, 0x0a, 0x07, 0x00
 modestring:    .ascii  "   "
 modenr:                db      0x00    ! mode number
                .ascii  ":    "
                db      0x00
-
-modesave:      .word   0       ! Requsted mode ID.
+               
+idati:         .ascii  "761295520"
+idcandt:       .byte   0xa5
+idgenoa:       .byte   0x77, 0x00, 0x99, 0x66
+idparadise:    .ascii  "VGA="
+idoakvga:      .ascii  "OAK VGA "
+idS3:          .byte   0x81, 0x82, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95
+               .byte   0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xb0
+
+! Manufacturer:          Numofmodes+2: Mode:
+! Number of modes is the number of chip-specific svga modes plus the extended
+! modes available on any vga (currently 2)
+
+moati:         .byte   0x06,   0x23, 0x33, 0x22, 0x21
+moahead:       .byte   0x07,   0x22, 0x23, 0x24, 0x2f, 0x34
+mocandt:       .byte   0x04,   0x60, 0x61
+mocirrus:      .byte   0x06,   0x1f, 0x20, 0x22, 0x31
+moeverex:      .byte   0x0c,   0x03, 0x04, 0x07, 0x08, 0x0a, 0x0b, 0x16, 0x18, 0x21, 0x40
+mogenoa:       .byte   0x0c,   0x58, 0x5a, 0x60, 0x61, 0x62, 0x63, 0x64, 0x72, 0x74, 0x78
+moparadise:    .byte   0x04,   0x55, 0x54
+motrident:     .byte   0x09,   0x50, 0x51, 0x52, 0x57, 0x58, 0x59, 0x5a
+motseng:       .byte   0x07,   0x26, 0x2a, 0x23, 0x24, 0x22
+movideo7:      .byte   0x08,   0x40, 0x43, 0x44, 0x41, 0x42, 0x45
+mooakvga:      .byte   0x08,   0x00, 0x07, 0x4e, 0x4f, 0x50, 0x51
+mo_S3:         .byte   0x04,   0x54, 0x55
+mounknown:     .byte   0x02
+
+!                      msb = Cols lsb = Rows:
+! The first two modes are standard vga modes available on any vga.
+! mode 0 is 80x50 and mode 1 is 80x28
+
+dscati:                .word   0x5032, 0x501c, 0x8419, 0x842c, 0x641e, 0x6419
+dscahead:      .word   0x5032, 0x501c, 0x842c, 0x8419, 0x841c, 0xa032, 0x5042
+dsccandt:      .word   0x5032, 0x501c, 0x8419, 0x8432
+dsccirrus:     .word   0x5032, 0x501c, 0x8419, 0x842c, 0x841e, 0x6425
+dsceverex:     .word   0x5032, 0x501c, 0x5022, 0x503c, 0x642b, 0x644b, 0x8419, 0x842c, 0x501e, 0x641b, 0xa040, 0x841e
+dscgenoa:      .word   0x5032, 0x501c, 0x5020, 0x642a, 0x8419, 0x841d, 0x8420, 0x842c, 0x843c, 0x503c, 0x5042, 0x644b
+dscparadise:   .word   0x5032, 0x501c, 0x8419, 0x842c
+dsctrident:    .word   0x5032, 0x501c, 0x501e, 0x502b, 0x503c, 0x8419, 0x841e, 0x842b, 0x843c
+dsctseng:      .word   0x5032, 0x501c, 0x503c, 0x6428, 0x8419, 0x841c, 0x842c
+dscvideo7:     .word   0x5032, 0x501c, 0x502b, 0x503c, 0x643c, 0x8419, 0x842c, 0x841c
+dscoakvga:     .word   0x5032, 0x501c, 0x2819, 0x5019, 0x503c, 0x843c, 0x8419, 0x842b
+dsc_S3:                .word   0x5032, 0x501c, 0x842b, 0x8419
+dsunknown:     .word   0x5032, 0x501c
+modesave:      .word   SVGA_MODE
 
 ! This must be last
 setup_sig1:    .word   SIG1
 setup_sig2:    .word   SIG2
 
-! After our code and data, we'll store the mode list.
-! Mode record: .word   modenr
-!              .byte   lines
-!              .byte   columns
-! Mode numbers used: 0=current, >=0x100=VESA, -1=80x50, -2=80x28
-modelist:
-
 .text
 endtext:
 .data
index 4ae35e1eda845c5d1b585eee8d8bfa6ebc90939a..cffc930109e3c8bf7f479058767e754e9ce4f173 100644 (file)
@@ -56,7 +56,7 @@ fi
 mainmenu_option next_comment
 comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)'
 
-bool 'Support non-SCSI/IDE/ATAPI drives' CONFIG_CD_NO_IDESCSI
+bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI
 if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then
   source drivers/cdrom/Config.in
 fi
index c3e4cff756709d691ca3fef230045f4d0d98ca12..c6ec6bb9156f5ec605ba2f8458a9b8833f05ed26 100644 (file)
@@ -31,7 +31,9 @@ CONFIG_ST506=y
 #
 # CONFIG_BLK_DEV_HD is not set
 CONFIG_BLK_DEV_IDE=y
+CONFIG_BLK_DEV_IDEATAPI=y
 CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
 # CONFIG_BLK_DEV_TRITON is not set
 # CONFIG_BLK_DEV_XD is not set
 
@@ -119,6 +121,11 @@ CONFIG_ISO9660_FS=y
 # CONFIG_MS_BUSMOUSE is not set
 # CONFIG_ATIXL_BUSMOUSE is not set
 # CONFIG_QIC02_TAPE is not set
+# CONFIG_APM is not set
+# CONFIG_APM_IGNORE_USER_SUSPEND is not set
+# CONFIG_APM_DO_ENABLE is not set
+CONFIG_APM_CPU_IDLE=y
+# CONFIG_APM_DISPLAY_BLANK is not set
 
 #
 # Sound
index a355b25702e6c3fd0991c493050e32f90864f7f8..96905629c4ac4eeaa69d9165a2d42383c11ca128 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/tasks.h>
 #include <linux/linkage.h>
 #include <asm/segment.h>
+#include <linux/config.h>
 
 #define CL_MAGIC_ADDR  0x90020
 #define CL_MAGIC       0xA33F
@@ -382,7 +383,11 @@ ENTRY(idt)
        ALIGN
 .word 0
 gdt_descr:
+#ifdef CONFIG_APM
+       .word (11+2*NR_TASKS)*8-1
+#else
        .word (8+2*NR_TASKS)*8-1
+#endif
        .long 0xc0000000+SYMBOL_NAME(gdt)
 
 /*
@@ -399,3 +404,8 @@ ENTRY(gdt)
        .quad 0x0000000000000000        /* not used */
        .quad 0x0000000000000000        /* not used */
        .fill 2*NR_TASKS,8,0            /* space for LDT's and TSS's etc */
+#ifdef CONFIG_APM
+       .quad 0x00c09a0000000000        /* APM CS    code */
+       .quad 0x00809a0000000000        /* APM CS 16 code (16 bit) */
+       .quad 0x00c0920000000000        /* APM DS    data */
+#endif
index a5a9a62927afc659497260dbdf3a4ed5b9063576..9075a4f4822bf5056656424e10937ec78554b137 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/ldt.h>
 #include <linux/user.h>
 #include <linux/a.out.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
 
 #include <asm/segment.h>
 #include <asm/pgtable.h>
 
 asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");
 
+#ifdef CONFIG_APM
+extern int  apm_do_idle(void);
+extern void apm_do_busy(void);
+#endif
+
 static int hlt_counter=0;
 
+#define HARD_IDLE_TIMEOUT (HZ / 3)
+
 void disable_hlt(void)
 {
        hlt_counter++;
@@ -41,11 +50,41 @@ void enable_hlt(void)
        hlt_counter--;
 }
 
+static void hard_idle(void)
+{
+       while (!need_resched) {
+               if (hlt_works_ok && !hlt_counter) {
+#ifdef CONFIG_APM
+                               /* If the APM BIOS is not enabled, or there
+                                is an error calling the idle routine, we
+                                should hlt if possible.  We need to check
+                                need_resched again because an interrupt
+                                may have occured in apm_do_idle(). */
+                       start_bh_atomic();
+                       if (!apm_do_idle() && !need_resched)
+                               __asm__("hlt");
+                       end_bh_atomic();
+#else
+                       __asm__("hlt");
+#endif
+               }
+               if (need_resched) break;
+               schedule();
+       }
+#ifdef CONFIG_APM
+       apm_do_busy();
+#endif
+}
+
 /*
  * The idle loop on a i386..
  */
 asmlinkage int sys_idle(void)
 {
+#ifndef __SMP__
+        unsigned long start_idle = 0;
+#endif
+               
        if (current->pid != 0)
        {
        /*      printk("Wrong process idled\n");        SMP bug check */
@@ -78,10 +117,17 @@ asmlinkage int sys_idle(void)
        for (;;) {
 #ifdef __SMP__
                if (cpu_data[smp_processor_id()].hlt_works_ok && !hlt_counter && !need_resched)
-#else  
-               if (hlt_works_ok && !hlt_counter && !need_resched)
-#endif         
                        __asm__("hlt");
+#else  
+               if (!start_idle) start_idle = jiffies;
+               if (jiffies - start_idle > HARD_IDLE_TIMEOUT) {
+                       hard_idle();
+               } else {
+                       if (hlt_works_ok && !hlt_counter && !need_resched)
+                               __asm__("hlt");
+               }
+               if (need_resched) start_idle = 0;
+#endif
                schedule();
        }
 }
index 1f976e4b8a41072a7b42a02ca2c1423e32966140..6450c4359095ee537f67be463bb664cde49768ca 100644 (file)
@@ -23,6 +23,9 @@
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/config.h>
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+#endif
 
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -54,6 +57,9 @@ int EISA_bus = 0;
  */
 struct drive_info_struct { char dummy[32]; } drive_info;
 struct screen_info screen_info;
+#ifdef CONFIG_APM
+struct apm_bios_info apm_bios_info;
+#endif
 
 unsigned char aux_device_present;
 extern int ramdisk_size;
@@ -67,6 +73,9 @@ extern char empty_zero_page[PAGE_SIZE];
  */
 #define PARAM  empty_zero_page
 #define EXT_MEM_K (*(unsigned short *) (PARAM+2))
+#ifdef CONFIG_APM
+#define APM_BIOS_INFO (*(struct apm_bios_info *) (PARAM+64))
+#endif
 #define DRIVE_INFO (*(struct drive_info_struct *) (PARAM+0x80))
 #define SCREEN_INFO (*(struct screen_info *) (PARAM+0))
 #define MOUNT_ROOT_RDONLY (*(unsigned short *) (PARAM+0x1F2))
@@ -95,6 +104,9 @@ void setup_arch(char **cmdline_p,
        ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV);
        drive_info = DRIVE_INFO;
        screen_info = SCREEN_INFO;
+#ifdef CONFIG_APM
+       apm_bios_info = APM_BIOS_INFO;
+#endif
        aux_device_present = AUX_DEVICE_INFO;
        memory_end = (1<<20) + (EXT_MEM_K<<10);
        memory_end &= PAGE_MASK;
index b6190ca107dbe621534e326389c6acaf3bc16e0a..a63927964595127d892a9b3049a495cd251cf51b 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <linux/mc146818rtc.h>
 #include <linux/timex.h>
+#include <linux/config.h>
 
 #define TIMER_IRQ 0
 
@@ -313,9 +314,8 @@ static inline unsigned long mktime(unsigned int year, unsigned int mon,
          )*60 + sec; /* finally seconds */
 }
 
-void time_init(void)
+unsigned long get_cmos_time(void)
 {
-       void (*irq_handler)(int, struct pt_regs *);
        unsigned int year, mon, day, hour, min, sec;
        int i;
 
@@ -350,11 +350,21 @@ void time_init(void)
          }
        if ((year += 1900) < 1970)
                year += 100;
-       xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
+       return mktime(year, mon, day, hour, min, sec);
+}
+
+void time_init(void)
+{
+       void (*irq_handler)(int, struct pt_regs *);
+       xtime.tv_sec = get_cmos_time();
        xtime.tv_usec = 0;
 
        /* If we have the CPU hardware time counters, use them */
-       irq_handler = timer_interrupt;  
+       irq_handler = timer_interrupt;
+#ifndef CONFIG_APM
+                               /* Don't use them if a suspend/resume could
+                                   corrupt the timer value.  This problem
+                                   needs more debugging. */
        if (x86_capability & 16) {
                irq_handler = pentium_timer_interrupt;
                do_gettimeoffset = do_fast_gettimeoffset;
@@ -363,6 +373,7 @@ void time_init(void)
                        :"=a" (((unsigned long *) &init_timer_cc)[0]),
                         "=d" (((unsigned long *) &init_timer_cc)[1]));
        }
+#endif
        if (request_irq(TIMER_IRQ, irq_handler, 0, "timer") != 0)
                panic("Could not allocate timer IRQ!");
 }
index 0951e4bb652afc06a778b759ff50e466a0360f4f..46a8c983abb26b8d3bc1b32170b8635c48fe19d4 100644 (file)
@@ -623,4 +623,4 @@ sys_call_table:
        .globl  floppy_track_buffer
 floppy_track_buffer:
        .space  512*2*38                /* Space for one entire cylinder! */    
-#endif
\ No newline at end of file
+#endif
index ce1c620e9d4728449c7235593e56d44b652a6822..ea7f31a8364691fa7e52d59ab9f9682ab25f5f8b 100644 (file)
@@ -97,4 +97,4 @@ bptr: ori r0, r0, 0
        BEEF
        BEEF
 #endif
-_END:  
\ No newline at end of file
+_END:  
index 5a79ecdeab26523b425acc4081d387741bb960ad..fcf0186013aad1e2356131911503740a5d521c1f 100644 (file)
@@ -15,7 +15,11 @@ if [ "$CONFIG_ST506" = "y" ]; then
     bool '   Use new IDE driver for primary/secondary i/f' CONFIG_BLK_DEV_IDE
   fi
   if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
-    bool '   Include support for IDE/ATAPI CDROMs' CONFIG_BLK_DEV_IDECD
+    bool '   Include support for IDE/ATAPI CDROM or TAPE' CONFIG_BLK_DEV_IDEATAPI
+    if [ "$CONFIG_BLK_DEV_IDEATAPI" = "y" ]; then
+      bool '      Include support for IDE/ATAPI CDROM' CONFIG_BLK_DEV_IDECD
+      bool '      Include ALPHA support for IDE/ATAPI TAPE' CONFIG_BLK_DEV_IDETAPE
+    fi
     if [ "$CONFIG_PCI" = "y" ]; then
       bool '   PCI Triton IDE Bus Master DMA support' CONFIG_BLK_DEV_TRITON
     fi
index a3aa534a24a18da84cf766d02b57a19a0e77ab9e..2a591cd9eec6df79b395fa6bef6be4f2b44eec99 100644 (file)
@@ -27,3 +27,19 @@ makedevs hde 33 0
 makedevs hdf 33 64
 makedevs hdg 34 0
 makedevs hdh 34 64
+
+# Create the ide-tape rewinding character device.
+
+rm -f /dev/ht0
+echo mknod /dev/ht0 c 37 0
+     mknod /dev/ht0 c 37 0
+chown root:disk /dev/ht0
+chmod 660 /dev/ht0
+
+# Create the ide-tape non rewinding character device.
+
+rm -f /dev/nht0
+echo mknod /dev/nht0 c 37 128
+     mknod /dev/nht0 c 37 128
+chown root:disk /dev/nht0
+chmod 660 /dev/nht0
index 0a8fcd90419710d92fac25b346e8aa3d4751ef3f..4ff973086b34c19f355cf799447f139e52e7701b 100644 (file)
@@ -44,6 +44,10 @@ ifeq ($(CONFIG_BLK_DEV_IDECD),y)
 L_OBJS += ide-cd.o
 endif
 
+ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
+L_OBJS += ide-tape.o
+endif
+
 ifeq ($(CONFIG_BLK_DEV_XD),y)
 L_OBJS += xd.o
 endif
index adee5a4b2655dc22b9899ed9de38e8d846b58560..b57c4cbded5a1ff7b8c7ab000a7a4d50ef97128e 100644 (file)
@@ -1,4 +1,4 @@
-README.ide -- Information regarding ide.c and ide-cd.c (IDE driver in 1.3.x)
+README.ide -- Information regarding ide.c and ide-cd.c (IDE driver in 1.3.xx)
 ================================================================================
 Supported by:  mlord@bnr.ca           -- disks, interfaces, probing
                snyder@fnald0.fnal.gov -- cdroms, ATAPI, audio
index 99572dde6a77a3c4129305fda400508967d89277..8a55caca4c23335ff847b46076d50ecd42d9e384 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/cmd640.c       Version 0.01  Nov 16, 1995
+ *  linux/drivers/block/cmd640.c       Version 0.02  Nov 30, 1995
  *
  *  Copyright (C) 1995  Linus Torvalds & author (see below)
  */
  *  Version 0.01       Initial version, hacked out of ide.c,
  *                     and #include'd rather than compiled separately.
  *                     This will get cleaned up in a subsequent release.
+ *
+ *  Version 0.02       Fixes for vlb initialization code, enable
+ *                     read-ahead for versions 'B' and 'C' of chip by
+ *                     default, some code cleanup.
+ *
  */
 
+/*
+ * CMD640 specific registers definition.
+ */
+
+#define VID            0x00
+#define DID            0x02
+#define PCMD           0x04
+#define PSTTS          0x06
+#define REVID          0x08
+#define PROGIF         0x09
+#define SUBCL          0x0a
+#define BASCL          0x0b
+#define BaseA0         0x10
+#define BaseA1         0x14
+#define BaseA2         0x18
+#define BaseA3         0x1c
+#define INTLINE                0x3c
+#define INPINE         0x3d
+
+#define        CFR             0x50
+#define   CFR_DEVREV           0x03
+#define   CFR_IDE01INTR                0x04
+#define          CFR_DEVID             0x18
+#define          CFR_AT_VESA_078h      0x20
+#define          CFR_DSA1              0x40
+#define          CFR_DSA0              0x80
+
+#define CNTRL          0x51
+#define          CNTRL_DIS_RA0         0x40
+#define   CNTRL_DIS_RA1                0x80
+#define          CNTRL_ENA_2ND         0x08
+
+#define        CMDTIM          0x52
+#define        ARTTIM0         0x53
+#define        DRWTIM0         0x54
+#define ARTTIM1        0x55
+#define DRWTIM1                0x56
+#define ARTTIM23       0x57
+#define   DIS_RA2              0x04
+#define   DIS_RA3              0x08
+#define DRWTIM23       0x58
+#define BRST           0x59
+
 /* Interface to access cmd640x registers */
 static void (*put_cmd640_reg)(int key, int reg_no, int val);
 static byte (*get_cmd640_reg)(int key, int reg_no);
@@ -24,6 +72,7 @@ static int    bus_type = none;
 static int     cmd640_chip_version;
 static int     cmd640_key;
 static byte    is_cmd640[MAX_HWIFS];
+static int     bus_speed; /* MHz */
 
 /*
  * For some unknown reasons pcibios functions which read and write registers
@@ -51,7 +100,7 @@ static byte get_cmd640_reg_pci1(int key, int reg_no)
        save_flags(flags);
        cli();
        outl_p((reg_no & 0xfc) | key, 0xcf8);
-       b = inb(0xcfc + (reg_no & 3));
+       b = inb_p(0xcfc + (reg_no & 3));
        restore_flags(flags);
        return b;
 }
@@ -78,7 +127,7 @@ static byte get_cmd640_reg_pci2(int key, int reg_no)
        save_flags(flags);
        cli();
        outb_p(0x10, 0xcf8);
-       b = inb(key + reg_no);
+       b = inb_p(key + reg_no);
        outb_p(0, 0xcf8);
        restore_flags(flags);
        return b;
@@ -92,8 +141,8 @@ static void put_cmd640_reg_vlb(int key, int reg_no, int val)
 
        save_flags(flags);
        cli();
-       outb(reg_no, key + 8);
-       outb(val, key + 0xc);
+       outb_p(reg_no, key + 8);
+       outb_p(val, key + 0xc);
        restore_flags(flags);
 }
 
@@ -104,8 +153,8 @@ static byte get_cmd640_reg_vlb(int key, int reg_no)
 
        save_flags(flags);
        cli();
-       outb(reg_no, key + 8);
-       b = inb(key + 0xc);
+       outb_p(reg_no, key + 8);
+       b = inb_p(key + 0xc);
        restore_flags(flags);
        return b;
 }
@@ -164,12 +213,12 @@ static int probe_for_cmd640_pci2(void)
 static int probe_for_cmd640_vlb(void) {
        byte b;
 
-       outb(0x50, 0x178);
+       outb(CFR, 0x178);
        b = inb(0x17c);
-       if (b == 0xff || b == 0 || (b & 0x20)) {
-               outb(0x50, 0xc78);
+       if (b == 0xff || b == 0 || (b & CFR_AT_VESA_078h)) {
+               outb(CFR, 0x78);
                b = inb(0x7c);
-               if (b == 0xff || b == 0 || !(b & 0x20))
+               if (b == 0xff || b == 0 || !(b & CFR_AT_VESA_078h))
                        return 0;
                cmd640_key = 0x70;
        } else {
@@ -186,7 +235,10 @@ static int probe_for_cmd640_vlb(void) {
 
 int ide_probe_for_cmd640x(void)
 {
-       int i;
+       int  i;
+       int  second_port;
+       int  read_ahead;
+       byte b;
 
        for (i = 0; i < MAX_HWIFS; i++)
                is_cmd640[i] = 0;
@@ -217,19 +269,60 @@ int ide_probe_for_cmd640x(void)
         * Documented magic.
         */
 
-       cmd640_chip_version = get_cmd640_reg(cmd640_key, 0x50) & 3;
+       cmd640_chip_version = get_cmd640_reg(cmd640_key, CFR) & CFR_DEVREV;
        if (cmd640_chip_version == 0) {
                printk ("ide: wrong CMD640 version -- 0\n");
                return 0;
        }
 
-       put_cmd640_reg(cmd640_key, 0x51, get_cmd640_reg(cmd640_key, 0x51) | 0xc8);
-       put_cmd640_reg(cmd640_key, 0x57, 0);
-       put_cmd640_reg(cmd640_key, 0x57, get_cmd640_reg(cmd640_key, 0x57) | 0x0c);
+       /*
+        * Do not initialize secondary controller for vlbus
+        */
+       second_port = (bus_type != vlb);
+
+       /*
+        * Set the maximum allowed bus speed (it is safest until we
+        *                                    find how detect bus speed)
+        * Normally PCI bus runs at 33MHz, but often works overclocked to 40
+        */
+       bus_speed = (bus_type == vlb) ? 50 : 40; 
+
+#if 0  /* don't know if this is reliable yet */
+       /*
+        * Enable readahead for versions above 'A'
+        */
+       read_ahead = (cmd640_chip_version > 1);
+#else
+       read_ahead = 0;
+#endif
+       /*
+        * Setup Control Register
+        */
+       b = get_cmd640_reg(cmd640_key, CNTRL);  
+       if (second_port)
+               b |= CNTRL_ENA_2ND;
+       else
+               b &= ~CNTRL_ENA_2ND;
+       if (read_ahead)
+               b &= ~(CNTRL_DIS_RA0 | CNTRL_DIS_RA1);
+       else
+               b |= (CNTRL_DIS_RA0 | CNTRL_DIS_RA1);
+       put_cmd640_reg(cmd640_key, CNTRL, b);
+
+       /*
+        * Initialize 2nd IDE port, if required
+        */
+       if (second_port) {
+               /* We reset timings, and setup read-ahead */
+               b = read_ahead ? 0 : (DIS_RA2 | DIS_RA3);
+               put_cmd640_reg(cmd640_key, ARTTIM23, b);
+               put_cmd640_reg(cmd640_key, DRWTIM23, 0);
+       }
 
        serialized = 1;
 
-       printk("ide: buggy CMD640 interface at ");
+       printk("ide: buggy CMD640%c interface at ", 
+              'A' - 1 + cmd640_chip_version);
        switch (bus_type) {
                case vlb :
                        printk("local bus, port 0x%x", cmd640_key);
@@ -247,17 +340,17 @@ int ide_probe_for_cmd640x(void)
        /*
         * Reset interface timings
         */
+       put_cmd640_reg(cmd640_key, CMDTIM, 0);
 
-       put_cmd640_reg(cmd640_key, 0x58, 0);
-       put_cmd640_reg(cmd640_key, 0x52, 0);
-
-       printk("\n ... serialized, disabled read-ahead, secondary interface enabled\n");
+       printk("\n ... serialized, %s read-ahead, secondary interface %s\n",
+              read_ahead ? "enabled" : "disabled",
+              second_port ? "enabled" : "disabled");
 
        return 1;
 }
 
 static int as_clocks(int a) {
-       switch (a & 0xf0) {
+       switch (a & 0xc0) {
                case 0 :        return 4;
                case 0x40 :     return 2;
                case 0x80 :     return 3;
@@ -274,42 +367,39 @@ static void cmd640_set_timing(int if_num, int dr_num, int r1, int r2) {
        int  b_reg;
        byte b;
        int  r52;
+       static int a = 0;
 
-       b_reg = if_num ? 0x57 : dr_num ? 0x55 : 0x53;
+       b_reg = if_num ? ARTTIM23 : dr_num ? ARTTIM1 : ARTTIM0;
 
        if (if_num == 0) {
                put_cmd640_reg(cmd640_key, b_reg, r1);
                put_cmd640_reg(cmd640_key, b_reg + 1, r2);
        } else {
                b = get_cmd640_reg(cmd640_key, b_reg);
-               if ((b&1) == 0) {
-                       put_cmd640_reg(cmd640_key, b_reg, r1);
-               } else {
-                       if (as_clocks(b) < as_clocks(r1))
-                               put_cmd640_reg(cmd640_key, b_reg, r1);
-               }
-               b = get_cmd640_reg(cmd640_key, b_reg + 1);
-               if (b == 0) {
+               if (a == 0 || as_clocks(b) < as_clocks(r1))
+                       put_cmd640_reg(cmd640_key, b_reg, (b & 0xc0) | r1);
+               
+               if (a == 0) {
                        put_cmd640_reg(cmd640_key, b_reg + 1, r2);
                } else {
-                       r52 = (b&0xf) < (r2&0xf) ? (r2&0xf) : (b&0xf);
+                       b = get_cmd640_reg(cmd640_key, b_reg + 1);
+                       r52 =  (b&0x0f) < (r2&0x0f) ? (r2&0x0f) : (b&0x0f);
                        r52 |= (b&0xf0) < (r2&0xf0) ? (r2&0xf0) : (b&0xf0);
                        put_cmd640_reg(cmd640_key, b_reg+1, r52);
                }
+               a = 1;
        }
 
-       b = get_cmd640_reg(cmd640_key, 0x52);
+       b = get_cmd640_reg(cmd640_key, CMDTIM);
        if (b == 0) {
-               put_cmd640_reg(cmd640_key, 0x52, r2);
+               put_cmd640_reg(cmd640_key, CMDTIM, r2);
        } else {
-               r52 = (b&0xf) < (r2&0xf) ? (r2&0xf) : (b&0xf);
+               r52  = (b&0x0f) < (r2&0x0f) ? (r2&0x0f) : (b&0x0f);
                r52 |= (b&0xf0) < (r2&0xf0) ? (r2&0xf0) : (b&0xf0);
-               put_cmd640_reg(cmd640_key, 0x52, r52);
+               put_cmd640_reg(cmd640_key, CMDTIM, r52);
        }
 }
 
-static int bus_speed = 33; /* MHz */
-
 struct pio_timing {
        int     mc_time;        /* Minimal cycle time (ns) */
        int     av_time;        /* Address valid to DIOR-/DIOW- setup (ns) */
@@ -385,10 +475,10 @@ static void set_pio_mode(int if_num, int drv_num, int mode_num) {
        int i;
 
        p_base = if_num ? 0x170 : 0x1f0;
-       outb(3, p_base + 1);
-       outb(mode_num | 8, p_base + 2);
-       outb((drv_num | 0xa) << 4, p_base + 6);
-       outb(0xef, p_base + 7);
+       outb_p(3, p_base + 1);
+       outb_p(mode_num | 8, p_base + 2);
+       outb_p((drv_num | 0xa) << 4, p_base + 6);
+       outb_p(0xef, p_base + 7);
        for (i = 0; (i < 100) && (inb (p_base + 7) & 0x80); i++)
                delay_10ms();
 }
@@ -442,3 +532,4 @@ void cmd640_tune_drive(ide_drive_t* drive) {
        cmd640_set_timing(interface_number, drive_number, r1, r2);
        printk ("Mode and Timing set to PIO%d (0x%x 0x%x)\n", max_pio, r1, r2);
 }
+
index defd3443f1da249a1d0d60b3de2d0315c2b3cb1b..1493453cc9a7b83183ff690c756bbe49648ed019 100644 (file)
@@ -72,6 +72,7 @@
  * 3.03  Oct 27, 1995 -- Some Creative drives have an id of just `CD'.
  *                       `DCI-2S10' drives are broken too.
  * 3.04  Nov 20, 1995 -- So are Vertros drives.
+ * 3.05  Dec  1, 1995 -- Changes to go with overhaul of ide.c and ide-tape.c
  *
  * NOTE: Direct audio reads will only work on some types of drive.
  * So far, i've received reports of success for Sony and Toshiba drives.
@@ -202,7 +203,7 @@ void cdrom_in_bytes (ide_drive_t *drive, void *buffer, uint bytecount)
   ide_input_data (drive, buffer, bytecount / 4);
   if ((bytecount & 0x03) >= 2)
     {
-      insw (IDE_DATA_REG, buffer + (bytecount & ~0x03), 1);
+      insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
     }
 }
 
@@ -214,7 +215,7 @@ void cdrom_out_bytes (ide_drive_t *drive, void *buffer, uint bytecount)
   ide_output_data (drive, buffer, bytecount / 4);
   if ((bytecount & 0x03) >= 2)
     {
-      outsw (IDE_DATA_REG, buffer + (bytecount & ~0x03), 1);
+      outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
     }
 }
 
@@ -501,27 +502,8 @@ static void cdrom_queue_request_sense (ide_drive_t *drive,
 {
   struct request *rq;
   struct packet_command *pc;
-  unsigned long flags;
   int len;
 
-  int major = HWIF(drive)->major;
-
-  save_flags (flags);
-  cli ();  /* safety */
-
-  rq = HWGROUP(drive)->rq;
-
-  /* If we're processing a request, put it back on the request queue. */
-  if (rq != NULL)
-    {
-      restore_request (rq);
-      rq->next = blk_dev[major].current_request;
-      blk_dev[major].current_request = rq;
-      HWGROUP(drive)->rq = NULL;
-    }
-
-  restore_flags (flags);
-
   /* If the request didn't explicitly specify where to put the sense data,
      use the statically allocated structure. */
   if (reqbuf == NULL)
@@ -544,29 +526,15 @@ static void cdrom_queue_request_sense (ide_drive_t *drive,
   pc->buffer = (char *)reqbuf;
   pc->buflen = len;
   pc->sense_data = (struct atapi_request_sense *)failed_command;
-  
+
+  /* stuff the sense request in front of our current request */
+
   rq = &HWIF(drive)->request_sense_request;
-  rq->rq_status = RQ_ACTIVE;
-  rq->rq_dev = MKDEV (major, (drive->select.b.unit) << PARTN_BITS);
+  ide_init_drive_cmd (rq);
   rq->cmd = REQUEST_SENSE_COMMAND;
-  rq->errors = 0;
-  rq->sector = 0;
-  rq->nr_sectors = 0;
-  rq->current_nr_sectors = 0;
   rq->buffer = (char *)pc;
   rq->sem = sem;
-  rq->bh = NULL;
-  rq->bhtail = NULL;
-  rq->next = NULL;
-
-  save_flags (flags);
-  cli ();  /* safety */
-
-  /* Stick it onto the front of the queue. */
-  rq->next = blk_dev[major].current_request;
-  blk_dev[major].current_request = rq;
-
-  restore_flags (flags);
+  (void) ide_do_drive_cmd (drive, rq, ide_preempt);
 }
 
 
@@ -638,8 +606,8 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat, int *stat_ret
          struct packet_command *pc = (struct packet_command *)rq->buffer;
          pc->stat = 1;
          cdrom_end_request (1, drive);
-         if (ide_error (drive, "request sense failure", stat))
-           return 1;
+         ide_error (drive, "request sense failure", stat);
+         return 1;
        }
 
       else if (cmd == PACKET_COMMAND)
@@ -734,8 +702,8 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat, int *stat_ret
          /* If there were other errors, go to the default handler. */
          else if ((err & ~ABRT_ERR) != 0)
            {
-             if (ide_error (drive, "cdrom_decode_status", stat))
-               return 1;
+             ide_error (drive, "cdrom_decode_status", stat);
+             return 1;
            }
 
          /* Else, abort if we've racked up too many retries. */
@@ -752,7 +720,6 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat, int *stat_ret
     }
 
   /* Retry, or handle the next request. */
-  IDE_DO_REQUEST;
   return 1;
 }
 
@@ -781,7 +748,7 @@ static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
 
   if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt)
     {
-      ide_set_handler (drive, handler);
+      ide_set_handler (drive, handler, WAIT_CMD);
       OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
     }
   else
@@ -819,7 +786,7 @@ static int cdrom_transfer_packet_command (ide_drive_t *drive,
     }
 
   /* Arm the interrupt handler. */
-  ide_set_handler (drive, handler);
+  ide_set_handler (drive, handler, WAIT_CMD);
 
   /* Send the command to the device. */
   cdrom_out_bytes (drive, cmd_buf, cmd_len);
@@ -925,7 +892,6 @@ int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason)
     }
 
   cdrom_end_request (0, drive);
-  IDE_DO_REQUEST;
   return -1;
 }
 
@@ -961,7 +927,6 @@ static void cdrom_read_intr (ide_drive_t *drive)
       else
         cdrom_end_request (1, drive);
 
-      IDE_DO_REQUEST;
       return;
     }
 
@@ -976,7 +941,6 @@ static void cdrom_read_intr (ide_drive_t *drive)
               drive->name, len);
       printk ("  This drive is not supported by this version of the driver\n");
       cdrom_end_request (0, drive);
-      IDE_DO_REQUEST;
       return;
     }
 
@@ -1040,7 +1004,7 @@ static void cdrom_read_intr (ide_drive_t *drive)
 
   /* Done moving data!
      Wait for another interrupt. */
-  ide_set_handler (drive, &cdrom_read_intr);
+  ide_set_handler (drive, &cdrom_read_intr, WAIT_CMD);
 }
 
 
@@ -1138,7 +1102,6 @@ static void cdrom_start_read_continuation (ide_drive_t *drive)
           printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n",
                   drive->name, rq->current_nr_sectors);
           cdrom_end_request (0, drive);
-          IDE_DO_REQUEST;
           return;
         }
 
@@ -1252,7 +1215,6 @@ static void cdrom_pc_intr (ide_drive_t *drive)
           pc->stat = 1;
           cdrom_end_request (1, drive);
         }
-      IDE_DO_REQUEST;
       return;
     }
 
@@ -1327,7 +1289,7 @@ static void cdrom_pc_intr (ide_drive_t *drive)
     }
 
   /* Now we wait for another interrupt. */
-  ide_set_handler (drive, &cdrom_pc_intr);
+  ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD);
 }
 
 
@@ -1367,40 +1329,6 @@ void cdrom_sleep (int time)
   schedule ();
 }
 
-
-static
-void cdrom_queue_request (ide_drive_t *drive, struct request *req)
-{
-  unsigned long flags;
-  struct request **p, **pfirst;
-  int major = HWIF(drive)->major;
-  struct semaphore sem = MUTEX_LOCKED;
-
-  req->rq_dev = MKDEV (major, (drive->select.b.unit) << PARTN_BITS);
-  req->rq_status = RQ_ACTIVE;
-  req->sem = &sem;
-  req->errors = 0;
-  req->next = NULL;
-
-  save_flags (flags);
-  cli ();
-
-  p = &blk_dev[major].current_request;
-  pfirst = p;
-  while ((*p) != NULL)
-    {
-      p = &((*p)->next);
-    }
-  *p = req;
-  if (p == pfirst)
-    blk_dev[major].request_fn ();
-
-  down (&sem);
-
-  restore_flags (flags);
-}
 static
 int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc)
 {
@@ -1416,15 +1344,10 @@ int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc)
 
   /* Start of retry loop. */
   do {
+    ide_init_drive_cmd (&req);
     req.cmd = PACKET_COMMAND;
-    req.sector = 0;
-    req.nr_sectors = 0;
-    req.current_nr_sectors = 0;
     req.buffer = (char *)pc;
-    req.bh = NULL;
-    req.bhtail = NULL;
-
-    cdrom_queue_request (drive, &req);
+    (void) ide_do_drive_cmd (drive, &req, ide_wait);
 
     if (pc->stat != 0)
       {
@@ -1491,8 +1414,8 @@ void ide_do_rw_cdrom (ide_drive_t *drive, unsigned long block)
   else if (rq -> cmd == RESET_DRIVE_COMMAND)
     {
       cdrom_end_request (1, drive);
-      if (ide_do_reset (drive))
-       return;
+      ide_do_reset (drive);
+      return;
     }
 
   else if (rq -> cmd != READ)
@@ -2434,10 +2357,9 @@ int ide_cdrom_ioctl (ide_drive_t *drive, struct inode *inode,
     case CDROMRESET:
       {
        struct request req;
-       memset (&req, 0, sizeof (req));
+       ide_init_drive_cmd (&req);
        req.cmd = RESET_DRIVE_COMMAND;
-       cdrom_queue_request (drive, &req);
-       return 0;
+       return ide_do_drive_cmd (drive, &req, ide_wait);
       }
 #endif
 
diff --git a/drivers/block/ide-tape.c b/drivers/block/ide-tape.c
new file mode 100644 (file)
index 0000000..08465d9
--- /dev/null
@@ -0,0 +1,3313 @@
+/*
+ * linux/drivers/block/ide-tape.c      Version 1.0 - ALPHA     Dec  3, 1995
+ *
+ * Copyright (C) 1995 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
+ *
+ * This driver was constructed as a student project in the software laboratory
+ * of the faculty of electrical engineering in the Technion - Israel's
+ * Institute Of Technology, with the guide of Avner Lottem and Dr. Ilana David.
+ *
+ * It is hereby placed under the terms of the GNU general public license.
+ * (See linux/COPYING).
+ */
+/*
+ * IDE ATAPI streaming tape driver.
+ *
+ * This driver is a part of the Linux ide driver and works in co-operation
+ * with linux/drivers/block/ide.c.
+ *
+ * This driver provides both a block device and a character device interface to
+ * the tape. The driver, in co-operation with ide.c, basically traverses the
+ * request-list for the block device interface. The character device interface,
+ * on the other hand, creates new requests, adds them to the request-list
+ * of the block device, and waits for their completion.
+ *
+ * The block device major and minor numbers are determined from the
+ * tape relative position in the ide interfaces, as explained in ide.c.
+ *
+ * The character device interface consists of two devices:
+ *
+ * ht0         major=37,minor=0        first IDE tape, rewind on close.
+ * nht0                major=37,minor=128      first IDE tape, no rewind on close.
+ *
+ * Run /usr/src/linux/drivers/block/MAKEDEV.ide to create the above entries.
+ * We currently support only one ide tape drive.
+ *
+ * Although we do support requests which originate from the buffer cache to
+ * some extent, it is recommended to use the character device interface when
+ * performing a long read or write operation (relative to the amount of free
+ * memory in your system). Otherwise, free memory will be used to cache tape
+ * blocks, those cached blocks won't be used, Linux's responsiveness will
+ * suffer as we start to swap.
+ *
+ * The general magnetic tape commands compatible interface, as defined by
+ * include/linux/mtio.h, is accessible through the character device.
+ * Our own ide-tape ioctl's can can be issued to either the block device or
+ * the character device.
+ *
+ * Opening the block device interface will be refused by default.
+ *
+ * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive.
+ *
+ * Ver 0.1   Nov  1 95   Pre-working code :-)
+ * Ver 0.2   Nov 23 95   A short backup (few megabytes) and restore procedure
+ *                        was successful ! (Using tar cvf ... on the block
+ *                        device interface).
+ *                       A longer backup resulted in major swapping, bad
+ *                        overall Linux performance and eventually failed as
+ *                        we received non serial read-ahead requests from the
+ *                        buffer cache.
+ * Ver 0.3   Nov 28 95   Long backups are now possible, thanks to the
+ *                        character device interface. Linux's responsiveness
+ *                        and performance doesn't seem to be much affected
+ *                        from the background backup procedure.
+ *                       Some general mtio.h magnetic tape operations are
+ *                        now supported by our character device. As a result,
+ *                        popular tape utilities are starting to work with
+ *                        ide tapes :-)
+ *                       The following configurations were tested:
+ *                             1. An IDE ATAPI TAPE shares the same interface
+ *                                and irq with an IDE ATAPI CDROM.
+ *                             2. An IDE ATAPI TAPE shares the same interface
+ *                                and irq with a normal IDE disk.
+ *                        Both configurations seemed to work just fine !
+ *                        However, to be on the safe side, it is meanwhile
+ *                        recommended to give the IDE TAPE its own interface
+ *                        and irq.
+ *                       The one thing which needs to be done here is to
+ *                        add a "request postpone" feature to ide.c,
+ *                        so that we won't have to wait for the tape to finish
+ *                        performing a long media access (DSC) request (such
+ *                        as a rewind) before we can access the other device
+ *                        on the same interface. This effect doesn't disturb
+ *                        normal operation most of the time because read/write
+ *                        requests are relatively fast, and once we are
+ *                        performing one tape r/w request, a lot of requests
+ *                        from the other device can be queued and ide.c will
+ *                       service all of them after this single tape request.
+ * Ver 1.0   ???         Integrated into Linux 1.3.??? development tree.
+ *                       On each read / write request, we now ask the drive
+ *                        if we can transfer a constant number of bytes
+ *                        (a parameter of the drive) only to its buffers,
+ *                        without causing actual media access. If we can't,
+ *                        we just wait until we can by polling the DSC bit.
+ *                        This ensures that while we are not transferring
+ *                        more bytes than the constant reffered to above, the
+ *                        interrupt latency will not become too high and
+ *                        we won't cause an interrupt timeout, as happened
+ *                        occasionally in the previous version.
+ *                       While polling for DSC, the current request is
+ *                        postponed and ide.c is free to handle requests from
+ *                        the other device. This is handled transparently to
+ *                        ide.c. The hwgroup locking method which was used
+ *                        in the previous version was removed.
+ *                       Use of new general features which are provided by
+ *                        ide.c for use with atapi devices.
+ *                        (Programming done by Mark Lord)
+ *                       Few potential bug fixes (Again, suggested by Mark)
+ *                       Single character device data transfers are now
+ *                        not limited in size, as they were before.
+ *                       We are asking the tape about its recommended
+ *                        transfer unit and send a larger data transfer
+ *                        as several transfers of the above size.
+ *                        For best results, use an integral number of this
+ *                        basic unit (which is shown during driver
+ *                        initialization). I will soon add an ioctl to get
+ *                        this important parameter.
+ *                       Our data transfer buffer is allocated on startup,
+ *                        rather than before each data transfer. This should
+ *                        ensure that we will indeed have a data buffer.
+ *
+ * We are currently in an *alpha* stage. The driver is not complete and not
+ * much tested. I would strongly suggest to:
+ *
+ *     1. Connect the tape to a separate interface and irq.
+ *     2. Be truly prepared for a kernel crash and the resulting data loss.
+ *     3. Don't rely too much on the resulting backups.
+ *
+ * Other than that, enjoy !
+ *
+ * Here are some words from the first releases of hd.c, which are quoted
+ * in ide.c and apply here as well:
+ *
+ * | Special care is recommended.  Have Fun!
+ *
+ */
+
+#include <linux/hdreg.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+
+#define _IDE_TAPE_C                    /* For ide_end_request in blk.h */
+
+/*
+ *     Main Linux ide driver include file
+ *
+ *     Automatically includes our first include file - ide-tape1.h.
+ */
+#include "ide.h"               
+
+/*
+ *     Supported ATAPI tape drives packet commands
+ */
+
+#define        IDETAPE_TEST_UNIT_READY_CMD     0x00
+#define        IDETAPE_REWIND_CMD              0x01
+#define        IDETAPE_REQUEST_SENSE_CMD       0x03
+#define        IDETAPE_READ_CMD                0x08
+#define        IDETAPE_WRITE_CMD               0x0a
+#define        IDETAPE_WRITE_FILEMARK_CMD      0x10
+#define        IDETAPE_SPACE_CMD               0x11
+#define        IDETAPE_INQUIRY_CMD             0x12
+#define        IDETAPE_ERASE_CMD               0x19
+#define        IDETAPE_MODE_SENSE_CMD          0x1a
+#define        IDETAPE_LOCATE_CMD              0x2b
+#define        IDETAPE_READ_POSITION_CMD       0x34
+
+/*
+ *     Some defines for the SPACE command
+ *
+ *     (The code field in the SPACE packet command).
+ */
+#define        IDETAPE_SPACE_OVER_FILEMARK     1
+#define        IDETAPE_SPACE_TO_EOD            3
+
+/*
+ *     Our ioctls - We will use 0x034n and 0x035n
+ *
+ *     Nothing special meanwhile.
+ *     mtio.h MTIOCTOP compatible commands are supported on the character
+ *     device interface.
+ */
+
+#define IDETAPE_INQUIRY_IOCTL          0x0341
+#define        IDETAPE_LOCATE_IOCTL            0x0342
+
+#define IDETAPE_RESET_IOCTL            0x0350
+
+/*
+ *     Special requests for our block device strategy routine.
+ *
+ *     In order to service a character device command, we add special
+ *     requests to the tail of our block device request queue and wait
+ *     for their completion.
+ *
+ */
+
+/*
+ *     IDETAPE_PACKET_COMMAND_REQUEST_TYPE1 is used to queue a packet command
+ *     in the request queue. We will wait for DSC before issuing the command
+ *     if it is still not set. In that case, we will temporary replace the
+ *     cmd field to type 2 and restore it back to type 1 when we receive DSC
+ *     and can start with sending the command.
+ */
+#define        IDETAPE_PACKET_COMMAND_REQUEST_TYPE1    90
+#define        IDETAPE_PACKET_COMMAND_REQUEST_TYPE2    91
+
+/*
+ *     IDETAPE_READ_REQUEST and IDETAPE_WRITE_REQUEST are used by our
+ *     character device interface to request read/write operations from
+ *     our block device interface.
+ *
+ *     In case a read or write request was requested by the buffer cache
+ *     and not by our character device interface, the cmd field in the
+ *     request will contain READ and WRITE instead.
+ *
+ *     We handle both cases in a similar way. The main difference is that
+ *     in our own requests, buffer head is NULL and idetape_end_request
+ *     will update the errors field if the request was not completed.
+ */
+
+#define        IDETAPE_READ_REQUEST                    92
+#define        IDETAPE_WRITE_REQUEST                   93
+
+/*
+ *     We are now able to postpone an idetape request in the stage
+ *     where it is polling for DSC and service requests from the other
+ *     ide device meanwhile.
+ */
+
+#define        IDETAPE_RQ_POSTPONED            0x1234
+
+/*
+ *     ATAPI Task File Registers (Re-definition of the ATA Task File
+ *     Registers for an ATAPI packet command).
+ *     From Table 3-2 of QIC-157C.
+ */
+
+/* Read Access */
+
+#define        IDETAPE_DATA_OFFSET             (0)
+#define IDETAPE_ERROR_OFFSET           (1)
+#define        IDETAPE_IREASON_OFFSET          (2)
+#define IDETAPE_RESERVED3_OFFSET       (3)
+#define IDETAPE_BCOUNTL_OFFSET         (4)
+#define        IDETAPE_BCOUNTH_OFFSET          (5)
+#define IDETAPE_DRIVESEL_OFFSET                (6)
+#define        IDETAPE_STATUS_OFFSET           (7)
+
+#define        IDETAPE_DATA_REG                (HWIF(drive)->io_base+IDETAPE_DATA_OFFSET)
+#define IDETAPE_ERROR_REG              (HWIF(drive)->io_base+IDETAPE_ERROR_OFFSET)
+#define        IDETAPE_IREASON_REG             (HWIF(drive)->io_base+IDETAPE_IREASON_OFFSET)
+#define IDETAPE_RESERVED3_REG          (HWIF(drive)->io_base+IDETAPE_RESERVED3_OFFSET)
+#define IDETAPE_BCOUNTL_REG            (HWIF(drive)->io_base+IDETAPE_BCOUNTL_OFFSET)
+#define        IDETAPE_BCOUNTH_REG             (HWIF(drive)->io_base+IDETAPE_BCOUNTH_OFFSET)
+#define IDETAPE_DRIVESEL_REG           (HWIF(drive)->io_base+IDETAPE_DRIVESEL_OFFSET)
+#define        IDETAPE_STATUS_REG              (HWIF(drive)->io_base+IDETAPE_STATUS_OFFSET)
+
+/* Write Access */
+
+#define        IDETAPE_FEATURES_OFFSET         (1)
+#define IDETAPE_ATACOMMAND_OFFSET      (7)
+
+#define IDETAPE_FEATURES_REG           (HWIF(drive)->io_base+IDETAPE_FEATURES_OFFSET)
+#define IDETAPE_ATACOMMAND_REG         (HWIF(drive)->io_base+IDETAPE_ATACOMMAND_OFFSET)
+#define IDETAPE_CONTROL_REG            (HWIF(drive)->ctl_port)
+
+
+/*
+ *     Structure of the various task file registers
+ */
+
+/*
+ *     The ATAPI Status Register.
+ */
+typedef union {
+       unsigned all                    :8;
+       struct {
+               unsigned check          :1;     /* Error occured */
+               unsigned idx            :1;     /* Reserved */
+               unsigned corr           :1;     /* Correctable error occured */
+               unsigned drq            :1;     /* Data is request by the device */
+               unsigned dsc            :1;     /* Set when a media access command is finished */
+                                               /* Reads / Writes are NOT media access commands */
+               unsigned reserved5      :1;     /* Reserved */
+               unsigned drdy           :1;     /* Ignored for ATAPI commands */
+                                               /* (The device is ready to accept ATA command) */
+               unsigned bsy            :1;     /* The device has access to the command block */
+       } b;
+} idetape_status_reg_t;
+
+/*
+ *     The ATAPI error register.
+ */
+typedef union {
+       unsigned all                    :8;
+       struct {
+               unsigned ili            :1;     /* Illegal Length Indication */
+               unsigned eom            :1;     /* End Of Media Detected */
+               unsigned abrt           :1;     /* Aborted command - As defined by ATA */
+               unsigned mcr            :1;     /* Media Change Requested - As defined by ATA */
+               unsigned sense_key      :4;     /* Sense key of the last failed packet command */
+       } b;
+} idetape_error_reg_t;
+
+/*
+ *     ATAPI Feature Register
+ */
+typedef union {
+       unsigned all                    :8;
+       struct {
+               unsigned dma            :1;     /* Using DMA of PIO */
+               unsigned reserved321    :3;     /* Reserved */
+               unsigned reserved654    :3;     /* Reserved (Tag Type) */
+               unsigned reserved7      :1;     /* Reserved */
+       } b;
+} idetape_feature_reg_t;
+
+/*
+ *     ATAPI Byte Count Register.
+ */
+typedef union {
+       unsigned all                    :16;
+       struct {
+               unsigned low            :8;     /* LSB */
+               unsigned high           :8;     /* MSB */
+       } b;
+} idetape_bcount_reg_t;
+
+/*
+ *     ATAPI Interrupt Reason Register.
+ */
+typedef union {
+       unsigned all                    :8;
+       struct {
+               unsigned cod            :1;     /* Information transferred is command (1) or data (0) */
+               unsigned io             :1;     /* The device requests us to read (1) or write (0) */
+               unsigned reserved       :6;     /* Reserved */
+       } b;
+} idetape_ireason_reg_t;
+
+/*
+ *     ATAPI Drive Select Register
+ */
+typedef union {        
+       unsigned all                    :8;
+       struct {
+               unsigned sam_lun        :4;     /* Should be zero with ATAPI (not used) */
+               unsigned drv            :1;     /* The responding drive will be drive 0 (0) or drive 1 (1) */
+               unsigned one5           :1;     /* Should be set to 1 */
+               unsigned reserved6      :1;     /* Reserved */
+               unsigned one7           :1;     /* Should be set to 1 */
+       } b;
+} idetape_drivesel_reg_t;
+
+/*
+ *     ATAPI Device Control Register
+ */
+typedef union {                        
+       unsigned all                    :8;
+       struct {
+               unsigned zero0          :1;     /* Should be set to zero */
+               unsigned nien           :1;     /* Device interrupt is disabled (1) or enabled (0) */
+               unsigned srst           :1;     /* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+               unsigned one3           :1;     /* Should be set to 1 */
+               unsigned reserved4567   :4;     /* Reserved */
+       } b;
+} idetape_control_reg_t;
+
+/*
+ *     idetape_chrdev_t provides the link between out character device
+ *     interface and our block device interface and the corresponding
+ *     ide_drive_t structure.
+ *
+ *     We currently support only one tape drive.
+ * 
+ */
+typedef struct {
+       ide_drive_t *drive;
+       int major,minor;
+       char name[4];
+} idetape_chrdev_t;
+
+/*
+ *     The following is used to format the general configuration word of
+ *     the ATAPI IDENTIFY DEVICE command.
+ */
+
+struct idetape_id_gcw {        
+
+       unsigned packet_size    :2;     /* Packet Size */
+       unsigned reserved2      :1;     /* Reserved */
+       unsigned reserved3      :1;     /* Reserved */
+       unsigned reserved4      :1;     /* Reserved */
+       unsigned drq_type       :2;     /* Command packet DRQ type */
+       unsigned removable      :1;     /* Removable media */
+       unsigned device_type    :5;     /* Device type */
+       unsigned reserved13     :1;     /* Reserved */
+       unsigned protocol       :2;     /* Protocol type */
+};
+
+/*
+ *     INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C)
+ */
+typedef struct {
+       unsigned device_type    :5;     /* Peripheral Device Type */
+       unsigned reserved0_765  :3;     /* Peripheral Qualifier - Reserved */
+       unsigned reserved1_6t0  :7;     /* Reserved */
+       unsigned rmb            :1;     /* Removable Medium Bit */
+       unsigned ansi_version   :3;     /* ANSI Version */
+       unsigned ecma_version   :3;     /* ECMA Version */
+       unsigned iso_version    :2;     /* ISO Version */
+       unsigned response_format :4;    /* Response Data Format */
+       unsigned reserved3_45   :2;     /* Reserved */
+       unsigned reserved3_6    :1;     /* TrmIOP - Reserved */
+       unsigned reserved3_7    :1;     /* AENC - Reserved */
+       byte additional_length;         /* Additional Length (total_length-4) */
+       byte reserved_5;                /* Reserved */
+       byte reserved_6;                /* Reserved */
+       unsigned reserved7_0    :1;     /* SftRe - Reserved */
+       unsigned reserved7_1    :1;     /* CmdQue - Reserved */
+       unsigned reserved7_2    :1;     /* Reserved */
+       unsigned reserved7_3    :1;     /* Linked - Reserved */
+       unsigned reserved7_4    :1;     /* Sync - Reserved */
+       unsigned reserved7_5    :1;     /* WBus16 - Reserved */
+       unsigned reserved7_6    :1;     /* WBus32 - Reserved */
+       unsigned reserved7_7    :1;     /* RelAdr - Reserved */
+       byte vendor_id [8];             /* Vendor Identification */
+       byte product_id [16];           /* Product Identification */
+       byte revision_level [4];        /* Revision Level */
+       byte vendor_specific [20];      /* Vendor Specific - Optional */
+       byte reserved56t95 [40];        /* Reserved - Optional */
+       
+                                       /* Additional information may be returned */
+} idetape_inquiry_result_t;
+
+/*
+ *     READ POSITION packet command - Data Format (From Table 6-57)
+ */
+typedef struct {
+       unsigned reserved0_10   :2;     /* Reserved */
+       unsigned bpu            :1;     /* Block Position Unknown */    
+       unsigned reserved0_543  :3;     /* Reserved */
+       unsigned eop            :1;     /* End Of Partition */
+       unsigned bop            :1;     /* Begining Of Partition */
+       byte partition_num;             /* Partition Number */
+       byte reserved_2;                /* Reserved */
+       byte reserved_3;                /* Reserved */
+       unsigned long first_block;      /* First Block Location */
+       unsigned long last_block;       /* Last Block Location (Optional) */
+       byte reserved_12;               /* Reserved */
+       byte blocks_in_buffer_2;        /* Blocks In Buffer - MSB (Optional) */
+       byte blocks_in_buffer_1;
+       byte blocks_in_buffer_0;        /* Blocks In Buffer - LSB (Optional) */
+       unsigned long bytes_in_buffer;  /* Bytes In Buffer (Optional) */
+} idetape_read_position_result_t;
+
+/*
+ *     REQUEST SENSE packet command result - Data Format.
+ */
+
+typedef struct {
+       unsigned error_code     :7;     /* Current of deferred errors */
+       unsigned valid          :1;     /* The information field conforms to QIC-157C */
+       byte reserved_1;                /* Segment Number - Reserved */
+       unsigned sense_key      :4;     /* Sense Key */
+       unsigned reserved2_4    :1;     /* Reserved */
+       unsigned ili            :1;     /* Incorrect Length Indicator */
+       unsigned eom            :1;     /* End Of Medium */
+       unsigned filemark       :1;     /* Filemark */
+       unsigned long information;      /* Information - Command specific */
+       byte asl;                       /* Additional sense length (n-7) */
+       unsigned long command_specific; /* Additional command specific information */
+       byte asc;                       /* Additional Sense Code */
+       byte ascq;                      /* Additional Sense Code Qualifier */
+       byte replaceable_unit_code;     /* Field Replaceable Unit Code */
+       unsigned sk_specific1   :7;     /* Sense Key Specific */
+       unsigned sksv           :1;     /* Sense Key Specific informatio is valid */
+       byte sk_specific2;              /* Sense Key Specific */
+       byte sk_specific3;              /* Sense Key Specific */
+} idetape_request_sense_result_t;
+
+/*
+ *     Follows structures which are realted to the SELECT SENSE / MODE SENSE
+ *     packet commands. Those packet commands are still not supported
+ *     by ide-tape.
+ */
+
+#define        IDETAPE_CAPABILITIES_PAGE       0x2a
+
+/*
+ *     Mode Parameter Header for the MODE SENSE packet command
+ */
+
+typedef struct {
+       byte mode_data_length;          /* The length of the following data that is */
+                                       /* available to be transferred */
+       byte medium_type;               /* Medium Type */
+       byte dsp;                       /* Device Specific Parameter */
+       byte bdl;                       /* Block Descriptor Length */
+} idetape_mode_parameter_header_t;
+
+/*
+ *     Mode Parameter Block Descriptor the MODE SENSE packet command
+ *
+ *     Support for block descriptors is optional.
+ */
+
+typedef struct {
+       byte density_code;              /* Medium density code */
+       byte blocks1;                   /* Number of blocks - MSB */
+       byte blocks2;                   /* Number of blocks - Middle byte */
+       byte blocks3;                   /* Number of blocks - LSB */
+       byte reserved4;                 /* Reserved */
+       byte length1;                   /* Block Length - MSB */
+       byte length2;                   /* Block Length - Middle byte */
+       byte length3;                   /* Block Length - LSB */
+} idetape_parameter_block_descriptor_t;
+
+/*
+ *     The Data Compression Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+       unsigned page_code      :6;     /* Page Code - Should be 0xf */
+       unsigned reserved       :1;     /* Reserved */
+       unsigned ps             :1;
+       byte page_length;               /* Page Length - Should be 14 */
+       unsigned reserved2      :6;     /* Reserved */
+       unsigned dcc            :1;     /* Data Compression Capable */
+       unsigned dce            :1;     /* Data Compression Enable */
+       unsigned reserved3      :5;     /* Reserved */
+       unsigned red            :2;     /* Report Exception on Decompression */
+       unsigned dde            :1;     /* Data Decompression Enable */
+       unsigned long ca;               /* Compression Algorithm */
+       unsigned long da;               /* Decompression Algorithm */
+       byte reserved_12;               /* Reserved */
+       byte reserved_13;               /* Reserved */
+       byte reserved_14;               /* Reserved */
+       byte reserved_15;               /* Reserved */
+} idetape_data_compression_page_t;
+
+/*
+ *     The Medium Partition Page, as returned by the MODE SENSE packet command.
+ */
+
+typedef struct {
+       unsigned page_code      :6;     /* Page Code - Should be 0x11 */
+       unsigned reserved1_6    :1;     /* Reserved */
+       unsigned ps             :1;
+       byte page_length;               /* Page Length - Should be 6 */
+       byte map;                       /* Maximum Additional Partitions - Should be 0 */
+       byte apd;                       /* Additional Partitions Defined - Should be 0 */
+       unsigned reserved4_012  :3;     /* Reserved */
+       unsigned psum           :2;     /* Should be 0 */
+       unsigned idp            :1;     /* Should be 0 */
+       unsigned sdp            :1;     /* Should be 0 */
+       unsigned fdp            :1;     /* Fixed Data Partitions */
+       byte mfr;                       /* Medium Format Recognition */
+       byte reserved6;                 /* Reserved */
+       byte reserved7;                 /* Reserved */
+} idetape_medium_partition_page_t;
+
+/*
+ *     Prototypes of various functions in ide-tape.c
+ *
+ *     The following functions are called from ide.c, and their prototypes
+ *     are available in ide.h:
+ *
+ *             idetape_identify_device
+ *             idetape_setup
+ *             idetape_blkdev_ioctl
+ *             idetape_do_request
+ *             idetape_blkdev_open
+ *             idetape_blkdev_release
+ *             idetape_register_chrdev (void);
+ */
+
+/*
+ *     The following functions are used to transfer data from / to the
+ *     tape's data register.
+ */
+void idetape_input_data (ide_drive_t *drive,void *buffer, unsigned long bcount);
+void idetape_output_data (ide_drive_t *drive,void *buffer, unsigned long bcount);
+void idetape_discard_data (ide_drive_t *drive, unsigned long bcount);
+
+
+/*
+ *     Packet command related functions.
+ */
+void idetape_issue_packet_command  (ide_drive_t *drive,idetape_packet_command_t *pc,ide_handler_t *handler);
+void idetape_pc_intr (ide_drive_t *drive);
+
+/*
+ *     DSC handling functions.
+ */
+void idetape_postpone_request (ide_drive_t *drive);
+void idetape_poll_for_dsc (unsigned long data);
+void idetape_put_back_postponed_request (ide_drive_t *drive);
+void idetape_media_access_finished (ide_drive_t *drive);
+
+/*
+ *     Some more packet command related functions.
+ */
+void idetape_pc_callback (ide_drive_t *drive);
+void idetape_retry_pc (ide_drive_t *drive);
+void idetape_zero_packet_command (idetape_packet_command_t *pc);
+void idetape_queue_pc_head (ide_drive_t *drive,idetape_packet_command_t *pc,struct request *rq);
+
+idetape_packet_command_t *idetape_next_pc_storage (ide_drive_t *drive);
+struct request *idetape_next_rq_storage (ide_drive_t *drive);
+
+void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
+
+/*
+ *     Various packet commands
+ */
+void idetape_create_inquiry_cmd (idetape_packet_command_t *pc);
+void idetape_inquiry_callback (ide_drive_t *drive);
+void idetape_create_locate_cmd (idetape_packet_command_t *pc,unsigned long block,byte partition);
+void idetape_create_rewind_cmd (idetape_packet_command_t *pc);
+void idetape_create_write_filemark_cmd (idetape_packet_command_t *pc,int write_filemark);
+void idetape_create_space_cmd (idetape_packet_command_t *pc,long count,byte cmd);
+void idetape_create_erase_cmd (idetape_packet_command_t *pc);
+void idetape_create_test_unit_ready_cmd (idetape_packet_command_t *pc);
+void idetape_create_read_position_cmd (idetape_packet_command_t *pc);
+void idetape_read_position_callback (ide_drive_t *drive);
+void idetape_create_read_cmd (idetape_packet_command_t *pc,unsigned long length);
+void idetape_read_callback (ide_drive_t *drive);
+void idetape_create_write_cmd (idetape_packet_command_t *pc,unsigned long length);
+void idetape_write_callback (ide_drive_t *drive);
+void idetape_create_request_sense_cmd (idetape_packet_command_t *pc);
+void idetape_create_mode_sense_cmd (idetape_packet_command_t *pc,byte page_code);
+void idetape_request_sense_callback (ide_drive_t *drive);
+
+void idetape_display_inquiry_result (byte *buffer);
+void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result);
+
+/*
+ *     Character device callback functions.
+ *
+ *     We currently support:
+ *
+ *             OPEN, RELEASE, READ, WRITE and IOCTL.
+ */
+
+int idetape_chrdev_read (struct inode *inode, struct file *file, char *buf, int count);
+int idetape_chrdev_read_remainder (struct inode *inode, struct file *file, char *buf, int count);
+int idetape_chrdev_write (struct inode *inode, struct file *file, const char *buf, int count);
+int idetape_chrdev_write_remainder (struct inode *inode, struct file *file, const char *buf, int count);
+int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+int idetape_chrdev_open (struct inode *inode, struct file *file);
+void idetape_chrdev_release (struct inode *inode,struct file *file);
+
+/*
+ *     idetape_mtioctop implements general magnetic tape io control
+ *     commands, as defined in include/linux/mtio.h. Those commands are
+ *     accessed through the character device interface, using the MTIOCTOP
+ *     ioctl.
+ */
+int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count);
+int idetape_queue_rw_tail (ide_drive_t *drive,int cmd,int blocks,char *buffer);
+int idetape_queue_pc_tail (ide_drive_t *drive,idetape_packet_command_t *pc);
+
+void idetape_fake_read (ide_drive_t *drive);
+int idetape_position_tape (ide_drive_t *drive,unsigned long block);
+int idetape_rewind_tape (ide_drive_t *drive);
+
+/*
+ *     Used to get device information
+ */
+
+void idetape_get_mode_sense_results (ide_drive_t *drive);
+
+/*
+ *     General utility functions
+ */
+void idetape_fixstring (byte *s, const int bytecount, const int byteswap);
+unsigned long idetape_swap_long (unsigned long temp);
+unsigned short idetape_swap_short (unsigned short temp);
+
+/*
+ *     For general magnetic tape device compatibility.
+ */
+#include <linux/mtio.h>
+
+/*
+ *     Global variables
+ *
+ *     The variables below are used for the character device interface.
+ *
+ *     Additional state variables are defined in our ide_drive_t structure.
+ */
+idetape_chrdev_t idetape_chrdev;               /* Character device interface information */
+byte idetape_drive_already_found=0;            /* 1 when the above data structure is initialized */
+
+/*
+ *     Our character device supporting functions, passed to register_chrdev.
+ */
+static struct file_operations idetape_fops = {
+       NULL,                   /* lseek - default */
+       idetape_chrdev_read,    /* read  */
+       idetape_chrdev_write,   /* write */
+       NULL,                   /* readdir - bad */
+       NULL,                   /* select */
+       idetape_chrdev_ioctl,   /* ioctl */
+       NULL,                   /* mmap */
+       idetape_chrdev_open,    /* open */
+       idetape_chrdev_release, /* release */
+       NULL,                   /* fsync */
+       NULL,                   /* fasync */
+       NULL,                   /* check_media_change */
+       NULL                    /* revalidate */
+};
+
+
+/*
+ *     idetape_identify_device is called by do_identify in ide.c during
+ *     the device probing stage to check the contents of the ATAPI IDENTIFY
+ *     command results, in case the device type is tape. We return:
+ *
+ *     1       If the tape can be supported by us, based on the information
+ *             we have so far.
+ *
+ *     0       If this tape driver is not currently supported by us.
+ *
+ *     In case we decide to support the tape, we store the current drive
+ *     pointer in our character device global variables, so that we can
+ *     pass between both interfaces.
+ */
+int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id)
+
+{
+       struct idetape_id_gcw gcw;
+       unsigned short *ptr;
+       int support=1;
+#if IDETAPE_DEBUG
+       unsigned short mask,i;
+#endif /* IDETAPE_DEBUG */
+               
+       ptr=(unsigned short *) &gcw;
+       *ptr=id->config;
+
+#if IDETAPE_DEBUG
+       printk ("Dumping ATAPI Identify Device tape parameters\n");
+       
+       printk ("Protocol Type: ");
+       switch (gcw.protocol) {
+               case 0: case 1: printk ("ATA\n");break;
+               case 2: printk ("ATAPI\n");break;
+               case 3: printk ("Reserved (Unknown to ide-tape)\n");break;
+       }
+       
+       printk ("Device Type: %x - ",gcw.device_type);  
+       switch (gcw.device_type) {
+               case 0: printk ("Direct-access Device\n");break;
+               case 1: printk ("Streaming Tape Device\n");break;
+               case 2: case 3: case 4: printk ("Reserved\n");break;
+               case 5: printk ("CD-ROM Device\n");break;
+               case 6: printk ("Reserved\n");
+               case 7: printk ("Optical memory Device\n");break;
+               case 0x1f: printk ("Unknown or no Device type\n");break;
+               default: printk ("Reserved\n");
+       }
+       printk ("Removable: %s",gcw.removable ? "Yes\n":"No\n");        
+               
+       printk ("Command Packet DRQ Type: ");
+       switch (gcw.drq_type) {
+               case 0: printk ("Microprocessor DRQ\n");break;
+               case 1: printk ("Interrupt DRQ\n");break;
+               case 2: printk ("Accelerated DRQ\n");break;
+               case 3: printk ("Reserved\n");break;
+       }
+       
+       printk ("Command Packet Size: ");
+       switch (gcw.packet_size) {
+               case 0: printk ("12 bytes\n");break;
+               case 1: printk ("16 bytes\n");break;
+               default: printk ("Reserved\n");break;
+       }
+       printk ("Model: %s\n",id->model);
+       printk ("Firmware Revision: %s\n",id->fw_rev);
+       printk ("Serial Number: %s\n",id->serial_no);
+       printk ("Write buffer size: %d bytes\n",id->buf_size*512);
+       printk ("DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
+       printk ("LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
+       printk ("IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
+       printk ("IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
+       printk ("PIO Cycle Timing Category: %d\n",id->tPIO);
+       printk ("DMA Cycle Timing Category: %d\n",id->tDMA);
+       printk ("Single Word DMA supported modes: ");
+       for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+               if (id->dma_1word & mask)
+                       printk ("%d ",i);
+               if (id->dma_1word & (mask << 8))
+                       printk ("(active) ");
+       }
+       printk ("\n");
+
+       printk ("Multi Word DMA supported modes: ");
+       for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+               if (id->dma_mword & mask)
+                       printk ("%d ",i);
+               if (id->dma_mword & (mask << 8))
+                       printk ("(active) ");
+       }
+       printk ("\n");
+
+       if (id->field_valid & 0x0002) {
+               printk ("Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
+               printk ("Minimum Multi-word DMA cycle per word: ");
+               if (id->eide_dma_min == 0)
+                       printk ("Not supported\n");
+               else
+                       printk ("%d ns\n",id->eide_dma_min);
+
+               printk ("Manafactuer\'s Recommended Multi-word cycle: ");
+               if (id->eide_dma_time == 0)
+                       printk ("Not supported\n");
+               else
+                       printk ("%d ns\n",id->eide_dma_time);
+
+               printk ("Minimum PIO cycle without IORDY: ");
+               if (id->eide_pio == 0)
+                       printk ("Not supported\n");
+               else
+                       printk ("%d ns\n",id->eide_pio);
+
+               printk ("Minimum PIO cycle with IORDY: ");
+               if (id->eide_pio_iordy == 0)
+                       printk ("Not supported\n");
+               else
+                       printk ("%d ns\n",id->eide_pio_iordy);
+               
+       }
+
+       else {
+               printk ("According to the device, fields 64-70 are not valid.\n");
+       }
+#endif /* IDETAPE_DEBUG */
+
+       /* Check that we can support this device */
+
+       if (gcw.protocol !=2 ) {
+               printk ("ide-tape: Protocol is not ATAPI\n");support=0;
+       }
+
+       if (gcw.device_type != 1) {
+               printk ("ide-tape: Device type is not set to tape\n");support=0;
+       }
+
+       if (!gcw.removable) {
+               printk ("ide-tape: The removable flag is not set\n");support=0;
+       }
+
+       if (gcw.drq_type != 2) {
+               printk ("ide-tape: Sorry, DRQ types other than Accelerated DRQ\n");
+               printk ("ide-tape: are still not supproted by the driver\n");support=0;
+       }
+
+       if (gcw.packet_size != 0) {
+               printk ("ide-tape: Packet size is not 12 bytes long\n");
+               if (gcw.packet_size == 1)
+                       printk ("ide-tape: Sorry, padding to 16 bytes is still not supported\n");
+               support=0;                      
+       }
+
+       if (idetape_drive_already_found) {
+               printk ("ide-tape: Sorry, only one ide tape drive is supported by the driver\n");
+               support=0;
+       }
+       else {
+               idetape_drive_already_found=1;
+               idetape_chrdev.drive=drive;
+               idetape_chrdev.major=IDETAPE_MAJOR;
+               idetape_chrdev.minor=0;
+               idetape_chrdev.name[0]='h';
+               idetape_chrdev.name[1]='t';
+               idetape_chrdev.name[2]='0';
+               idetape_chrdev.name[3]=0;
+       }
+
+       return (support);               /* In case support=0, we will not install the driver */
+}
+
+/*
+ *     idetape_register_chrdev calls register_chrdev to register our character
+ *     device interface. The connection to the ide_drive_t structure, which
+ *     is used by the entire ide driver is provided by our global variable
+ *     idetape_chrdev.drive, which was initialized earlier, during the device
+ *     probing stage.
+ */
+void idetape_register_chrdev (void)
+
+{
+       int major,minor;
+       ide_drive_t *drive;
+
+       if (!idetape_drive_already_found)
+               return;
+
+       drive=idetape_chrdev.drive;
+       major=idetape_chrdev.major;
+       minor=idetape_chrdev.minor;
+       
+       if (register_chrdev (major,idetape_chrdev.name,&idetape_fops)) {
+               printk ("Unable to register character device interface !\n");
+               /* ??? */
+       }
+       else {
+               printk ("ide-tape: %s <-> %s : Character device interface on major = %d\n",
+                       drive->name,idetape_chrdev.name,major);
+       }
+}
+
+/*
+ *     idetape_setup is called from the ide driver in the partition table
+ *     identification stage, to:
+ *
+ *             1.      Initialize our various state variables.
+ *             2.      Ask the tape for its capabilities.
+ *             3.      Allocate a buffer which will be used for data
+ *                     transfer. The buffer size is chosen based on
+ *                     the recommendation which we received in step (2).
+ *
+ *     Note that at this point ide.c already assigned us an irq, so that
+ *     we can queue requests here and wait for their completion.
+ */
+void idetape_setup (ide_drive_t *drive)
+
+{
+       int buffer_size;
+       idetape_tape_t *tape=&(drive->tape);
+
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Reached idetape_setup\n");
+#endif /* IDETAPE_DEBUG */     
+       
+       drive->ready_stat = 0;                  /* With an ATAPI device, we can issue packet commands */
+                                               /* regardless of the state of DRDY */
+       tape->block_address=0;                  
+       tape->block_address_valid=0;
+       tape->locate_to=0;
+       tape->locate_retries=0;
+       tape->pc_stack_index=0;
+       tape->failed_pc=NULL;
+       tape->postponed_rq=NULL;
+       tape->last_written_valid=0;
+       tape->busy=0;
+       
+       idetape_get_mode_sense_results (drive);
+
+       buffer_size=tape->capabilities.ctl*tape->tape_block_size;
+       tape->data_buffer=kmalloc (buffer_size,GFP_KERNEL);
+       if (tape->data_buffer == NULL) {
+               printk ("ide-tape: FATAL - Can not allocate %d bytes for data transfer buffer\n",buffer_size);
+               printk ("ide-tape: Aborting character device installation\n");
+               idetape_drive_already_found=0;
+               unregister_chrdev (idetape_chrdev.major,idetape_chrdev.name);
+               return;
+       }
+       printk ("ide-tape: Speed - %d KBps. Recommended transfer unit - %d bytes.\n",tape->capabilities.speed,buffer_size);
+       return;
+}
+
+/*
+ *     idetape_get_mode_sense_results asks the tape about its various
+ *     parameters. In particular, we will adjust our data transfer buffer
+ *     size to the recommended value as returned by the tape.
+ */
+
+void idetape_get_mode_sense_results (ide_drive_t *drive)
+
+{
+       int retval;
+       idetape_tape_t *tape=&(drive->tape);
+       idetape_mode_parameter_header_t *header;
+       idetape_capabilities_page_t *capabilities;
+       idetape_packet_command_t pc;
+       
+       idetape_create_mode_sense_cmd (&pc,IDETAPE_CAPABILITIES_PAGE);
+       pc.buffer=pc.temp_buffer;
+       pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+       pc.current_position=pc.temp_buffer;
+       retval=idetape_queue_pc_tail (drive,&pc);
+
+       header=(idetape_mode_parameter_header_t *) pc.buffer;   
+       capabilities=(idetape_capabilities_page_t *) (pc.buffer+sizeof (idetape_mode_parameter_header_t));
+
+       capabilities->max_speed=idetape_swap_short (capabilities->max_speed);
+       capabilities->ctl=idetape_swap_short (capabilities->ctl);
+       capabilities->speed=idetape_swap_short (capabilities->speed);
+       capabilities->buffer_size=idetape_swap_short (capabilities->buffer_size);
+
+       tape->capabilities=*capabilities;               /* Save us a copy */
+       tape->tape_block_size=capabilities->blk512 ? 512:1024;
+
+       if (retval) {
+               printk ("ide-tape: Can't get tape parameters\n");
+               printk ("ide-tape: Assuming some default parameters\n");
+               tape->tape_block_size=512;
+               tape->capabilities.ctl=26*1024;
+               return;
+       }
+
+#if IDETAPE_DEBUG
+       printk ("Dumping the results of the MODE SENSE packet command\n");
+       printk ("Mode Parameter Header:\n");
+       printk ("Mode Data Length - %d\n",header->mode_data_length);
+       printk ("Medium Type - %d\n",header->medium_type);
+       printk ("Device Specific Parameter - %d\n",header->dsp);
+       printk ("Block Descriptor Length - %d\n",header->bdl);
+       
+       printk ("Capabilities and Mechanical Status Page:\n");
+       printk ("Page code - %d\n",capabilities->page_code);
+       printk ("Page length - %d\n",capabilities->page_length);
+       printk ("Read only - %s\n",capabilities->ro ? "Yes":"No");
+       printk ("Supports reverse space - %s\n",capabilities->sprev ? "Yes":"No");
+       printk ("Supports erase initiated formatting - %s\n",capabilities->efmt ? "Yes":"No");
+       printk ("Supports QFA two Partition format - %s\n",capabilities->qfa ? "Yes":"No");
+       printk ("Supports locking the medium - %s\n",capabilities->lock ? "Yes":"No");
+       printk ("The volume is currently locked - %s\n",capabilities->locked ? "Yes":"No");
+       printk ("The device defaults in the prevent state - %s\n",capabilities->prevent ? "Yes":"No");
+       printk ("Supports ejecting the medium - %s\n",capabilities->eject ? "Yes":"No");
+       printk ("Supports error correction - %s\n",capabilities->ecc ? "Yes":"No");
+       printk ("Supports data compression - %s\n",capabilities->cmprs ? "Yes":"No");
+       printk ("Supports 512 bytes block size - %s\n",capabilities->blk512 ? "Yes":"No");
+       printk ("Supports 1024 bytes block size - %s\n",capabilities->blk1024 ? "Yes":"No");
+       printk ("Restricted byte count for PIO transfers - %s\n",capabilities->slowb ? "Yes":"No");
+       printk ("Maximum supported speed in KBps - %d\n",capabilities->max_speed);
+       printk ("Continuous transfer limits in blocks - %d\n",capabilities->ctl);
+       printk ("Current speed in KBps - %d\n",capabilities->speed);    
+       printk ("Buffer size - %d\n",capabilities->buffer_size*512);
+#endif /* IDETAPE_DEBUG */
+}
+
+/*
+ *     Packet Command Interface
+ *
+ *     The current Packet Command is available in tape->pc, and will not
+ *     change until we finish handling it. Each packet command is associated
+ *     with a callback function that will be called when the command is
+ *     finished.
+ *
+ *     The handling will be done in three stages:
+ *
+ *     1.      idetape_issue_packet_command will send the packet command to the
+ *             drive, and will set the interrupt handler to idetape_pc_intr.
+ *
+ *     2.      On each interrupt, idetape_pc_intr will be called. This step
+ *             will be repeated until the device signals us that no more
+ *             interrupts will be issued.
+ *
+ *     3.      ATAPI Tape media access commands have immediate status with a
+ *             delayed process. In case of a successfull initiation of a
+ *             media access packet command, the DSC bit will be set when the
+ *             actual execution of the command is finished. 
+ *             Since the tape drive will not issue an interrupt, we have to
+ *             poll for this event. In this case, we define the request as
+ *             "low priority request" by setting rq_status to
+ *             IDETAPE_RQ_POSTPONED,   set a timer to poll for DSC and exit
+ *             the driver.
+ *
+ *             ide.c will then give higher priority to requests which
+ *             originate from the other device, until will change rq_status
+ *             to RQ_ACTIVE.
+ *
+ *     4.      When the packet command is finished, it will be checked for errors.
+ *
+ *     5.      In case an error was found, we queue a request sense packet command
+ *             in front of the request queue and retry the operation up to
+ *             IDETAPE_MAX_PC_RETRIES times.
+ *
+ *     6.      In case no error was found, or we decided to give up and not
+ *             to retry again, the callback function will be called and then
+ *             we will handle the next request.
+ *
+ */
+
+void idetape_issue_packet_command  (ide_drive_t *drive,idetape_packet_command_t *pc,ide_handler_t *handler)
+
+{
+       idetape_tape_t *tape;
+       idetape_bcount_reg_t bcount;
+        idetape_ireason_reg_t ireason;
+
+       tape=&(drive->tape);
+               
+#ifdef IDETAPE_DEBUG
+       if (tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+               printk ("ide-tape: ide-tape.c bug - Two request sense in serial were issued\n");
+               /* ??? Need to rethink about that */            
+       }
+#endif /* IDETAPE_DEBUG */
+
+       if (tape->failed_pc == NULL && pc->c[0] != IDETAPE_REQUEST_SENSE_CMD)
+               tape->failed_pc=pc;
+       tape->pc=pc;                                                    /* Set the current packet command */
+
+       if (pc->retries > IDETAPE_MAX_PC_RETRIES) {
+               printk ("ide-tape: %s: I/O error, ",drive->name);
+               printk ("pc = %x, key = %x, asc = %x, ascq = %x\n",pc->c[0],tape->sense_key,tape->asc,tape->ascq);
+               printk ("ide-tape: Maximum retries reached - Giving up\n");
+               pc->error=1;                                    /* Giving up */
+               pc->active=0;
+               tape->failed_pc=NULL;
+#if IDETAPE_DEBUG
+               if (pc->callback==NULL)
+                       printk ("ide-tape: ide-tape bug - Callback function not set !\n");
+               else
+#endif /* IDETAPE_DEBUG */
+                       (*pc->callback)(drive);
+               return;
+       }
+
+#if IDETAPE_DEBUG
+       printk ("Retry number - %d\n",pc->retries);
+#endif /* IDETAPE_DEBUG */
+
+       pc->retries++;
+
+/*
+ *     We no longer call ide_wait_stat to wait for the drive to be ready,
+ *     as ide.c already does this for us in do_request.
+ */
+       pc->actually_transferred=0;                                     /* We haven't transferred any data yet */
+       pc->active=1;                                                   /* Packet command started */
+       bcount.all=pc->request_transfer;                                /* Request to transfer the entire buffer at once */
+
+                                                                       /* Initialize the task file registers */
+       OUT_BYTE (0,IDETAPE_FEATURES_REG);                              /* Use PIO data transger, No DMA */
+       OUT_BYTE (bcount.b.high,IDETAPE_BCOUNTH_REG);
+       OUT_BYTE (bcount.b.low,IDETAPE_BCOUNTL_REG);
+       OUT_BYTE (drive->select.all,IDETAPE_DRIVESEL_REG);
+       
+       ide_set_handler (drive,handler,WAIT_CMD);                       /* Set the interrupt routine */
+       OUT_BYTE (WIN_PACKETCMD,IDETAPE_ATACOMMAND_REG);                /* Issue the packet command */
+       if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {      /* Wait for DRQ to be ready - Assuming Accelerated DRQ */       
+               /*
+                *      We currently only support tape drives which report
+                *      accelerated DRQ assertion. For this case, specs
+                *      allow up to 50us. We really shouldn't get here.
+                *
+                *      ??? Still needs to think what to do if we reach
+                *      here anyway.
+                */
+                
+                printk ("ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
+                return;
+       }
+       
+       ireason.all=IN_BYTE (IDETAPE_IREASON_REG);
+       if (!ireason.b.cod || ireason.b.io) {
+               printk ("ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n");
+               /* ??? */
+       }
+               
+       ide_output_data (drive,pc->c,12/4);                     /* Send the actual packet */
+}
+
+/*
+ *     idetape_pc_intr is the usual interrupt handler which will be called
+ *     during a packet command. We will transfer some of the data (as
+ *     requested by the drive) and will re-point interrupt handler to us.
+ *     When data transfer is finished, we will act according to the
+ *     algorithm described before idetape_issue_packet_command.
+ *
+ */
+
+void idetape_pc_intr (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape;
+       idetape_status_reg_t status;
+       idetape_bcount_reg_t bcount;
+        idetape_ireason_reg_t ireason;
+       idetape_packet_command_t *pc;
+       
+       unsigned long temp;
+
+       tape=&(drive->tape);
+       
+       status.all=IN_BYTE (IDETAPE_STATUS_REG);                /* Clear the interrupt */
+
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Reached idetape_pc_intr interrupt handler\n");
+#endif /* IDETAPE_DEBUG */     
+
+       pc=tape->pc;                                            /* Current packet command */
+               
+       if (!status.b.drq) {                                    /* No more interrupts */
+#if IDETAPE_DEBUG
+               printk ("Packet command completed\n");
+               printk ("Total bytes transferred: %lu\n",pc->actually_transferred);
+#endif /* IDETAPE_DEBUG */
+               if (status.b.check) {                                   /* Error detected */
+#if IDETAPE_DEBUG
+       /*
+        *      Without debugging, we only log an error if we decided to
+        *      give up retrying.
+        */
+                       printk ("ide-tape: %s: I/O error, ",drive->name);
+#endif /* IDETAPE_DEBUG */
+                       idetape_retry_pc (drive);                       /* Retry operation */
+                       return;
+               }
+               pc->error=0;
+               if (pc->wait_for_dsc && !status.b.dsc) {                                /* Media access command */
+                       tape->dsc_polling_frequency=IDETAPE_DSC_FAST_MEDIA_ACCESS_FREQUENCY;
+                       idetape_postpone_request (drive);               /* Allow ide.c to handle other requests */
+                       return;
+               }
+               pc->active=0;
+               if (tape->failed_pc == pc)
+                       tape->failed_pc=NULL;
+#if IDETAPE_DEBUG
+               if (pc->callback==NULL)                 
+                       printk ("ide-tape: ide-tape bug - Callback function not set !\n");
+               else
+#endif IDETAPE_DEBUG
+                       (*pc->callback)(drive);                 /* Command finished - Call the callback function */
+               return;
+       }
+
+       bcount.b.high=IN_BYTE (IDETAPE_BCOUNTH_REG);                    /* Get the number of bytes to transfer */
+       bcount.b.low=IN_BYTE (IDETAPE_BCOUNTL_REG);                     /* on this interrupt */
+       ireason.all=IN_BYTE (IDETAPE_IREASON_REG);                      /* Read the interrupt reason register */
+
+       if (ireason.b.cod) {
+               printk ("ide-tape: CoD != 0 in idetape_pc_intr\n");
+               /* ??? */
+       }
+       if (ireason.b.io != !(pc->writing)) {                   /* Hopefully, we will never get here */
+               printk ("ide-tape: We wanted to %s, ",pc->writing ? "Write":"Read");
+               printk ("but the tape wants us to %s !\n",ireason.b.io ? "Read":"Write");
+               /* ??? */               
+       }
+       
+       if (!pc->writing) {                                     /* Reading - Check that we have enough space */
+               temp=(unsigned long) pc->actually_transferred + bcount.all;
+               if ( temp > pc->request_transfer) {
+                       printk ("ide-tape: The tape wants to send us more data than requested - ");
+                       if (temp > pc->buffer_size) {
+                               printk ("Discarding data\n");
+                               idetape_discard_data (drive,bcount.all);
+                               ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD);
+                               return;
+                       }
+                       else
+                               printk ("Allowing transfer\n");
+               }
+       }
+#if IDETAPE_DEBUG      
+       if (bcount.all && !pc->buffer) {        
+               printk ("ide-tape: ide-tape.c bug - Buffer not set in idetape_pc_intr. Discarding data.\n");
+               
+               if (!pc->writing) {
+                       printk ("ide-tape: Discarding data\n");
+                       idetape_discard_data (drive,bcount.all);
+                       ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD);
+                       return;
+               }
+               else {  /* ??? */
+               }
+       }
+#endif /* IDETAPE_DEBUG */
+       if (pc->writing)
+               idetape_output_data (drive,pc->current_position,bcount.all);    /* Write the current buffer */
+       else
+               idetape_input_data (drive,pc->current_position,bcount.all);     /* Read the current buffer */
+#if IDETAPE_DEBUG
+       printk ("ide-tape: %s %d bytes\n",pc->writing ? "Wrote":"Received",bcount.all);
+#endif /* IDETAPE_DEBUG */
+       pc->actually_transferred+=bcount.all;                                   /* Update the current position */
+       pc->current_position+=bcount.all;
+
+       ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD);              /* And set the interrupt handler again */
+}
+
+/*
+ *     idetape_postpone_request postpones the current request so that
+ *     ide.c will be able to service requests from another device on
+ *     the same hwgroup while we are polling for DSC.
+ */
+
+void idetape_postpone_request (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape;
+       unsigned long flags;
+       struct request *rq;
+               
+       tape=&(drive->tape);
+
+#if IDETAPE_DEBUG
+       printk ("Reached idetape_postpone_request\n");
+       if (tape->postponed_rq != NULL)
+               printk ("ide-tape.c bug - postponed_rq not NULL in idetape_postpone_request\n");
+#endif /* IDETAPE_DEBUG */
+
+       tape->dsc_count=0;
+       tape->dsc_timer.expires=jiffies + tape->dsc_polling_frequency;  /* Set timer to poll for */
+       tape->dsc_timeout=jiffies+IDETAPE_DSC_TIMEOUT;                  /* actual completion */
+       tape->dsc_timer.data=(unsigned long) drive;
+       tape->dsc_timer.function=&idetape_poll_for_dsc;
+       init_timer (&(tape->dsc_timer));
+
+       /*
+        * Remove current request from the request queue:
+        */
+       save_flags(flags);                                              /* Let ide.c handle another request */
+       cli();
+       tape->postponed_rq = rq = HWGROUP(drive)->rq;
+       rq->rq_status = IDETAPE_RQ_POSTPONED;   
+       blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
+       HWGROUP(drive)->rq = NULL;
+       restore_flags(flags);
+
+       tape->dsc_polling_start=jiffies;        
+       add_timer(&(tape->dsc_timer));          /* Activate the polling timer */
+}
+
+
+/*
+ *     idetape_poll_for_dsc gets invoked by a timer (which was set
+ *     by idetape_postpone_request) to poll for the DSC bit
+ *     in the status register. When the DSC bit is set, or a timeout is
+ *     reached, we put back the postponed request in front of the request
+ *     queue.
+ */
+void idetape_poll_for_dsc (unsigned long data)
+
+{
+       ide_drive_t *drive;
+       idetape_tape_t *tape;
+       
+       idetape_status_reg_t status;
+       idetape_packet_command_t *pc;
+
+       drive=(ide_drive_t *) data;
+       tape=&(drive->tape);
+       pc=tape->pc;
+       
+#if IDETAPE_DEBUG
+       printk ("%s: idetape_poll_for_dsc called\n",drive->name);
+#endif /* IDETAPE_DEBUG */     
+
+       status.all=IN_BYTE (IDETAPE_STATUS_REG);
+
+       if (status.b.dsc)
+               tape->dsc_count++;
+       else {
+               if (tape->dsc_count)
+                       printk ("ide-tape: DSC fluctuation detected - Restarting DSC count\n");
+               tape->dsc_count=0;
+       }
+
+       if (tape->dsc_count == IDETAPE_DSC_COUNT) {             /* DSC received */
+               tape->dsc_received=1;
+               del_timer (&(tape->dsc_timer));                 /* Stop polling and put back the postponed */
+               idetape_put_back_postponed_request (drive);     /* request in the request queue */
+               return;
+       }
+               
+       if (jiffies > tape->dsc_timeout)        { /* Timeout */
+               tape->dsc_received=0;
+               del_timer (&(tape->dsc_timer));
+               /* ??? */
+               idetape_put_back_postponed_request (drive);
+               return;
+       }
+       
+       /* Poll again */
+
+       if (jiffies - tape->dsc_polling_start > IDETAPE_FAST_SLOW_THRESHOLD)
+               tape->dsc_timer.expires = jiffies + IDETAPE_DSC_SLOW_MEDIA_ACCESS_FREQUENCY;
+       else
+               tape->dsc_timer.expires = jiffies + tape->dsc_polling_frequency;
+/*     init_timer (&(tape->dsc_timer)); */
+       add_timer(&(tape->dsc_timer));
+       return;
+}
+
+/*
+ *     idetape_put_back_postponed_request gets called by
+ *     idetape_poll_for_dsc when we decided to stop polling - Either
+ *     becase we received DSC or because we decided to give up and
+ *     stop waiting.
+ */
+
+void idetape_put_back_postponed_request (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape = &(drive->tape);
+
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Putting back postponed request\n");
+#endif /* IDETAPE_DEBUG */
+
+       (void) ide_do_drive_cmd (drive, tape->postponed_rq, ide_next);
+
+       /*
+        *      Note that the procedure done here is differnet than the method
+        *      we are using in idetape_queue_pc_head - There we are putting
+        *      request(s) before our currently called request.
+        *
+        *      Here, on the other hand, HWGROUP(drive)->rq is not our
+        *      request but rather a request to another device. Therefore,
+        *      we will let it finish and only then service our postponed
+        *      request --> We don't touch HWGROUP(drive)->rq.
+        */
+}
+
+void idetape_media_access_finished (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape=&(drive->tape);
+       idetape_status_reg_t status;
+       idetape_packet_command_t *pc;
+
+       pc=tape->pc;
+       
+       status.all=IN_BYTE (IDETAPE_STATUS_REG);
+
+       if (tape->dsc_received) {
+#if IDETAPE_DEBUG
+               printk ("DSC received\n");
+#endif /* IDETAPE_DEBUG */
+               pc->active=0;
+               if (status.b.check) {                                   /* Error detected */
+                       printk ("ide-tape: %s: I/O error, ",drive->name);
+                       idetape_retry_pc (drive);                       /* Retry operation */
+                       return;
+               }
+               pc->error=0;
+               if (tape->failed_pc == pc)
+                       tape->failed_pc=NULL;
+#if IDETAPE_DEBUG
+               if (pc->callback==NULL)
+                       printk ("ide-tape: ide-tape bug - Callback function not set !\n");
+               else
+#endif /* IDETAPE_DEBUG */
+                       (*pc->callback)(drive);
+
+               return;
+       }
+       else {
+               pc->active=0;
+               printk ("ide-tape: %s: DSC timeout.\n",drive->name);
+               /* ??? */
+               pc->error=1;
+               tape->failed_pc=NULL;
+#if IDETAPE_DEBUG
+               if (pc->callback==NULL)
+                       printk ("ide-tape: ide-tape bug - Callback function not set !\n");
+               else
+#endif /* IDETAPE_DEBUG */
+                       (*pc->callback)(drive);
+               return;
+       }
+}
+
+
+/*
+ *     idetape_retry_pc is called when an error was detected during the
+ *     last packet command. We queue a request sense packet command in
+ *     the head of the request list.
+ */
+void idetape_retry_pc (ide_drive_t *drive)
+
+{
+       idetape_packet_command_t *pc;
+       struct request *new_rq;
+
+       idetape_error_reg_t error;
+       error.all=IN_BYTE (IDETAPE_ERROR_REG);
+       pc=idetape_next_pc_storage (drive);
+       new_rq=idetape_next_rq_storage (drive);
+       idetape_create_request_sense_cmd (pc); 
+       pc->buffer=pc->temp_buffer;
+       pc->buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+       pc->current_position=pc->temp_buffer;
+       idetape_queue_pc_head (drive,pc,new_rq);
+}
+
+/*
+ *     General packet command callback function.
+ */
+void idetape_pc_callback (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape;
+       struct request *rq;
+       
+       tape=&(drive->tape);
+       rq=HWGROUP(drive)->rq;
+       
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Reached idetape_pc_callback\n");
+#endif /* IDETAPE_DEBUG */
+       if (!tape->pc->error) {
+#if IDETAPE_DEBUG
+               printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+               idetape_end_request (1,HWGROUP (drive));
+       }
+       else {
+               printk ("Aborting request\n");
+               idetape_end_request (0,HWGROUP (drive));
+       }
+       return;
+}
+
+
+void idetape_read_callback (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape;
+       struct request *rq;
+
+       tape=&(drive->tape);    
+       rq=HWGROUP(drive)->rq;
+#if IDETAPE_DEBUG      
+       printk ("ide-tape: Reached idetape_read_callback\n");
+#endif /* IDETAPE_DEBUG */
+       tape->block_address+=tape->pc->actually_transferred/512;
+       if (!tape->pc->error) {
+#if IDETAPE_DEBUG
+               printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+               rq->sector+=rq->current_nr_sectors;
+               rq->nr_sectors-=rq->current_nr_sectors;
+               rq->current_nr_sectors=0;
+               idetape_end_request (1,HWGROUP (drive));
+       }
+       else {
+               printk ("Aborting request\n");
+               idetape_end_request (0,HWGROUP (drive));
+       }
+       return;
+}
+
+void idetape_fake_read (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape;
+       struct request *rq;
+       unsigned long i;
+
+       tape=&(drive->tape);    
+       rq=HWGROUP(drive)->rq;
+#if IDETAPE_DEBUG      
+       printk ("ide-tape: Reached idetape_fake_read\n");
+#endif /* IDETAPE_DEBUG */
+
+#if IDETAPE_DEBUG
+       printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+       
+       for (i=0;i<rq->current_nr_sectors*512;i++)
+               rq->buffer [i]=0;
+
+       tape->block_address+=rq->current_nr_sectors;
+       rq->sector+=rq->current_nr_sectors;
+       rq->nr_sectors-=rq->current_nr_sectors;
+       rq->current_nr_sectors=0;
+       idetape_end_request (1,HWGROUP (drive));
+}
+
+void idetape_write_callback (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape;
+       struct request *rq;
+       
+       tape=&(drive->tape);
+       rq=HWGROUP(drive)->rq;
+#if IDETAPE_DEBUG      
+       printk ("ide-tape: Reached idetape_write_callback\n");
+#endif /* IDETAPE_DEBUG */
+       tape->block_address+=tape->pc->actually_transferred/512;
+       if (!tape->pc->error) {
+#if IDETAPE_DEBUG
+               printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+               rq->sector+=rq->current_nr_sectors;
+               rq->nr_sectors-=rq->current_nr_sectors;
+               rq->current_nr_sectors=0;
+               idetape_end_request (1,HWGROUP (drive));
+       }
+       else {
+               printk ("Aborting request\n");
+               idetape_end_request (0,HWGROUP (drive));
+       }
+       return;
+}
+
+void idetape_inquiry_callback (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape;
+       
+       tape=&(drive->tape);
+       
+       idetape_display_inquiry_result (tape->pc->buffer);
+       idetape_pc_callback (drive);
+       return;
+}
+
+/*
+ *     idetape_input_data is called to read data from the tape's data
+ *     register. We basically let ide_input_data do the job, but we also
+ *     take care about the remaining bytes which can not be transferred
+ *     in 32-bit data transfers.
+ */
+void idetape_input_data (ide_drive_t *drive,void *buffer, unsigned long bcount)
+
+{
+       unsigned long wcount;
+       
+       wcount=bcount >> 2;
+       bcount -= 4*wcount;
+       
+       if (wcount)
+               ide_input_data (drive,buffer,wcount);
+       
+       if (bcount) {
+               ((byte *)buffer) += 4*wcount;
+               insb (IDETAPE_DATA_REG,buffer,bcount);
+       }
+}
+
+/*
+ *     idetape_output_data is used to write data to the tape.
+ */
+void idetape_output_data (ide_drive_t *drive,void *buffer, unsigned long bcount)
+
+{
+       unsigned long wcount;
+       
+       wcount=bcount >> 2;
+       bcount -= 4*wcount;
+       
+       if (wcount)
+               ide_output_data (drive,buffer,wcount);
+       
+       if (bcount) {
+               ((byte *)buffer) += 4*wcount;
+               outsb (IDETAPE_DATA_REG,buffer,bcount);
+       }
+}
+
+/*
+ *     Too bad. The drive wants to send us data which we are not ready to accept.
+ *     Just throw it away.
+ */
+void idetape_discard_data (ide_drive_t *drive, unsigned long bcount)
+
+{
+       unsigned long i;
+       
+       for (i=0;i<bcount;i++)
+               IN_BYTE (IDETAPE_DATA_REG);
+}
+
+/*
+ *     Issue an INQUIRY packet command.
+ */
+void idetape_create_inquiry_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Creating INQUIRY packet command\n");
+#endif /* IDETAPE_DEBUG */     
+       pc->request_transfer=36;
+       pc->callback=&idetape_inquiry_callback;
+       pc->writing=0;
+       
+       idetape_zero_packet_command (pc);               
+       pc->c[0]=IDETAPE_INQUIRY_CMD;
+       pc->c[4]=255;
+}
+
+/*
+ *     Format the INQUIRY command results.
+ */
+void idetape_display_inquiry_result (byte *buffer)
+
+{
+       idetape_inquiry_result_t *result;
+
+       result=(idetape_inquiry_result_t *) buffer;
+       idetape_fixstring (result->vendor_id,8,0);
+       idetape_fixstring (result->product_id,16,0);
+       idetape_fixstring (result->revision_level,4,0);
+
+       if (result->response_format != 2) {
+               printk ("The INQUIRY Data Format is unknown to us !\n");
+               printk ("Assuming QIC-157C format.\n");
+       }
+
+#if IDETAPE_DEBUG
+       printk ("Dumping INQUIRY command results:\n");
+       printk ("Response Data Format: %d - ",result->response_format);
+       switch (result->response_format) {
+               case 2:
+                       printk ("As specified in QIC-157 Revision C\n");
+                       break;
+               default:
+                       printk ("Unknown\n");
+                       break;
+       }
+       
+       printk ("Device Type: %x - ",result->device_type);      
+       switch (result->device_type) {
+               case 0: printk ("Direct-access Device\n");break;
+               case 1: printk ("Streaming Tape Device\n");break;
+               case 2: case 3: case 4: printk ("Reserved\n");break;
+               case 5: printk ("CD-ROM Device\n");break;
+               case 6: printk ("Reserved\n");
+               case 7: printk ("Optical memory Device\n");break;
+               case 0x1f: printk ("Unknown or no Device type\n");break;
+               default: printk ("Reserved\n");
+       }
+       
+       printk ("Removable Medium: %s",result->rmb ? "Yes\n":"No\n");
+
+       printk ("ANSI Version: %d - ",result->ansi_version);
+       switch (result->ansi_version) {
+               case 2:
+                       printk ("QIC-157 Revision C\n");
+                       break;
+               default:
+                       printk ("Unknown\n");
+                       break;
+       }
+
+       printk ("ECMA Version: ");
+       if (result->ecma_version)
+               printk ("%d\n",result->ecma_version);
+       else
+               printk ("Not supported\n");
+
+       printk ("ISO Version: ");
+       if (result->iso_version)
+               printk ("%d\n",result->iso_version);
+       else
+               printk ("Not supported\n");
+
+       printk ("Additional Length: %d\n",result->additional_length);
+       printk ("Vendor Identification: %s\n",result->vendor_id);
+       printk ("Product Identification: %s\n",result->product_id);
+       printk ("Product Revision Level: %s\n",result->revision_level);
+#endif /* IDETAPE_DEBUG */
+
+       if (result->device_type != 1)
+               printk ("Device type is not set to tape\n");
+
+       if (!result->rmb)
+               printk ("The removable flag is not set\n");
+
+       if (result->ansi_version != 2) {
+               printk ("The Ansi Version is unknown to us !\n");
+               printk ("Assuming compliance with QIC-157C specification.\n");
+       }
+}
+
+void idetape_create_request_sense_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Creating REQUEST SENSE packet command\n");
+#endif /* IDETAPE_DEBUG */     
+       pc->request_transfer=18;
+       pc->callback=&idetape_request_sense_callback;
+       pc->writing=0;
+       
+       idetape_zero_packet_command (pc);       
+       pc->c[0]=IDETAPE_REQUEST_SENSE_CMD;
+       pc->c[4]=255;
+}
+
+void idetape_request_sense_callback (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape;
+       struct request *rq;
+
+       tape=&(drive->tape);    
+       rq=HWGROUP(drive)->rq;
+       
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Reached idetape_request_sense_callback\n");
+#endif /* IDETAPE_DEBUG */
+       if (!tape->pc->error) {
+#if IDETAPE_DEBUG
+               printk ("Request completed\n");
+#endif /* IDETAPE_DEBUG */
+               idetape_analyze_error (drive,(idetape_request_sense_result_t *) tape->pc->buffer);
+               idetape_end_request (1,HWGROUP (drive));
+       }
+       else {
+               printk ("Error in REQUEST SENSE itself - Aborting request!\n");
+               idetape_end_request (0,HWGROUP (drive));
+       }
+       return;
+}
+
+/*
+ *     idetape_analyze_error is called on each failed packet command retry
+ *     to analyze the request sense. We currently do not utilize this
+ *     information.
+ */
+void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result)
+
+{
+       idetape_tape_t *tape;
+       
+       tape=&(drive->tape);
+       tape->sense_key=result->sense_key;
+       tape->asc=result->asc;
+       tape->ascq=result->ascq;
+#if IDETAPE_DEBUG      
+       /*
+        *      Without debugging, we only log an error if we decided to
+        *      give up retrying.
+        */
+       printk ("ide-tape: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq);
+#endif /* IDETAPE_DEBUG */     
+       return; 
+}
+
+void idetape_create_test_unit_ready_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Creating TEST UNIT READY packet command\n");
+#endif /* IDETAPE_DEBUG */     
+       pc->request_transfer=0;
+       pc->buffer=NULL;
+       pc->current_position=NULL;
+       pc->callback=&idetape_pc_callback;
+       pc->writing=0;
+       
+       idetape_zero_packet_command (pc);       
+       pc->c[0]=IDETAPE_TEST_UNIT_READY_CMD;
+}
+
+void idetape_create_locate_cmd (idetape_packet_command_t *pc,unsigned long block,byte partition)
+
+{
+       unsigned long *ptr;
+
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Creating LOCATE packet command\n");
+#endif /* IDETAPE_DEBUG */
+       pc->request_transfer=0;
+       pc->buffer=NULL;
+       pc->current_position=NULL;
+       pc->buffer_size=0;
+       pc->wait_for_dsc=1;
+       pc->callback=&idetape_pc_callback;
+       pc->writing=0;
+               
+       idetape_zero_packet_command (pc);
+       pc->c [0]=IDETAPE_LOCATE_CMD;
+       pc->c [1]=2;
+       ptr=(unsigned long *) &(pc->c[3]);
+       *ptr=idetape_swap_long (block);
+       pc->c[8]=partition;
+}
+
+void idetape_create_rewind_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Creating REWIND packet command\n");
+#endif /* IDETAPE_DEBUG */
+       pc->request_transfer=0;
+       pc->buffer=NULL;
+       pc->current_position=NULL;
+       pc->buffer_size=0;
+       pc->wait_for_dsc=1;
+       pc->callback=&idetape_pc_callback;
+       pc->writing=0;
+               
+       idetape_zero_packet_command (pc);
+       pc->c [0]=IDETAPE_REWIND_CMD;
+}
+
+/*
+ *     A mode sense command is used to "sense" tape parameters.
+ */
+
+void idetape_create_mode_sense_cmd (idetape_packet_command_t *pc,byte page_code)
+
+{
+#if IDETAPE_DEBUG
+               printk ("ide-tape: Creating MODE SENSE packet command - Page %d\n",page_code);
+#endif /* IDETAPE_DEBUG */
+
+       pc->wait_for_dsc=0;
+       pc->callback=&idetape_pc_callback;
+       pc->writing=0;
+
+       switch (page_code) {
+               case IDETAPE_CAPABILITIES_PAGE:
+                       pc->request_transfer=24;
+       }
+               
+       idetape_zero_packet_command (pc);
+       pc->c [0]=IDETAPE_MODE_SENSE_CMD;
+       pc->c [1]=8;                            /* DBD = 1 - Don't return block descriptors for now */
+       pc->c [2]=page_code;
+       pc->c [3]=255;                          /* Don't limit the returned information */
+       pc->c [4]=255;                          /* (We will just discard data in that case) */
+}
+
+/*
+ *     idetape_create_write_filemark_cmd will:
+ *
+ *             1.      Write a filemark if write_filemark=1.
+ *             2.      Flush the device buffers without writing a filemark
+ *                     if write_filemark=0.
+ *
+ */
+void idetape_create_write_filemark_cmd (idetape_packet_command_t *pc,int write_filemark)
+
+{
+#if IDETAPE_DEBUG
+       printk ("Creating WRITE FILEMARK packet command\n");
+       if (!write_filemark)
+               printk ("which will only flush buffered data\n");
+#endif /* IDETAPE_DEBUG */
+       pc->request_transfer=0;
+       pc->buffer=NULL;
+       pc->current_position=NULL;
+       pc->buffer_size=0;
+       pc->wait_for_dsc=1;
+       pc->callback=&idetape_pc_callback;
+       pc->writing=0;
+               
+       idetape_zero_packet_command (pc);
+       pc->c [0]=IDETAPE_WRITE_FILEMARK_CMD;
+       if (write_filemark)
+               pc->c [4]=1;
+}
+
+void idetape_create_erase_cmd (idetape_packet_command_t *pc)
+
+{
+
+#if IDETAPE_DEBUG
+       printk ("Creating ERASE command\n");
+#endif /* IDETAPE_DEBUG */
+
+       pc->request_transfer=0;
+       pc->buffer=NULL;
+       pc->current_position=NULL;
+       pc->buffer_size=0;
+       pc->wait_for_dsc=1;
+       pc->callback=&idetape_pc_callback;
+       pc->writing=0;
+               
+       idetape_zero_packet_command (pc);
+       pc->c [0]=IDETAPE_ERASE_CMD;
+       pc->c [1]=1;
+}
+
+void idetape_create_read_cmd (idetape_packet_command_t *pc,unsigned long length)
+
+{
+       union convert {
+               unsigned all    :32;
+               struct {
+                       unsigned b1     :8;
+                       unsigned b2     :8;
+                       unsigned b3     :8;
+                       unsigned b4     :8;
+               } b;
+       } original;
+       
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Creating READ packet command\n");
+#endif /* IDETAPE_DEBUG */
+
+       original.all=length;
+
+       pc->wait_for_dsc=0;
+       pc->callback=&idetape_read_callback;
+       pc->writing=0;
+
+       idetape_zero_packet_command (pc);
+       pc->c [0]=IDETAPE_READ_CMD;
+       pc->c [1]=1;
+       pc->c [4]=original.b.b1;
+       pc->c [3]=original.b.b2;
+       pc->c [2]=original.b.b3;
+
+       return;
+}
+
+void idetape_create_space_cmd (idetape_packet_command_t *pc,long count,byte cmd)
+
+{
+       union convert {
+               unsigned all    :32;
+               struct {
+                       unsigned b1     :8;
+                       unsigned b2     :8;
+                       unsigned b3     :8;
+                       unsigned b4     :8;
+               } b;
+       } original;
+       
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Creating SPACE packet command\n");
+#endif /* IDETAPE_DEBUG */
+
+       original.all=count;
+
+       pc->request_transfer=0;
+       pc->buffer=NULL;
+       pc->current_position=NULL;
+       pc->buffer_size=0;
+       pc->wait_for_dsc=1;
+       pc->callback=&idetape_pc_callback;
+       pc->writing=0;
+
+       idetape_zero_packet_command (pc);
+       pc->c [0]=IDETAPE_SPACE_CMD;
+       pc->c [1]=cmd;
+       pc->c [4]=original.b.b1;
+       pc->c [3]=original.b.b2;
+       pc->c [2]=original.b.b3;
+
+       return;
+}
+
+void idetape_create_write_cmd (idetape_packet_command_t *pc,unsigned long length)
+
+{
+       union convert {
+               unsigned all    :32;
+               struct {
+                       unsigned b1     :8;
+                       unsigned b2     :8;
+                       unsigned b3     :8;
+                       unsigned b4     :8;
+               } b;
+       } original;
+       
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Creating WRITE packet command\n");
+#endif /* IDETAPE_DEBUG */
+
+       original.all=length;
+
+       pc->wait_for_dsc=0;
+       pc->callback=&idetape_write_callback;
+       pc->writing=1;
+
+       idetape_zero_packet_command (pc);
+       pc->c [0]=IDETAPE_WRITE_CMD;
+       pc->c [1]=1;
+       pc->c [4]=original.b.b1;
+       pc->c [3]=original.b.b2;
+       pc->c [2]=original.b.b3;
+
+       return;
+}
+
+void idetape_create_read_position_cmd (idetape_packet_command_t *pc)
+
+{
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Creating READ POSITION packet command\n");
+#endif /* IDETAPE_DEBUG */
+
+       pc->request_transfer=20;
+       pc->wait_for_dsc=0;
+       pc->callback=&idetape_read_position_callback;
+       pc->writing=0;
+
+       idetape_zero_packet_command (pc);
+       pc->c [0]=IDETAPE_READ_POSITION_CMD;
+       pc->c [1]=0;
+}
+
+void idetape_read_position_callback (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape;
+       struct request *rq;
+       idetape_read_position_result_t *result;
+       
+       tape=&(drive->tape);
+       
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Reached idetape_read_position_callback\n");
+#endif /* IDETAPE_DEBUG */
+
+       rq=HWGROUP(drive)->rq;
+       
+       if (!tape->pc->error) {
+               result=(idetape_read_position_result_t *) tape->pc->buffer;
+#if IDETAPE_DEBUG
+               printk ("Request completed\n");
+               printk ("Dumping the results of the READ POSITION command\n");
+               printk ("BOP - %s\n",result->bop ? "Yes":"No");
+               printk ("EOP - %s\n",result->eop ? "Yes":"No");
+#endif /* IDETAPE_DEBUG */
+               if (result->bpu) {
+                       printk ("ide-tape: Block location is unknown to the tape\n");
+                       printk ("Aborting request\n");
+                       tape->block_address_valid=0;
+                       idetape_end_request (0,HWGROUP (drive));
+               }
+               else {
+#if IDETAPE_DEBUG
+                       printk ("Block Location - %lu\n",idetape_swap_long (result->first_block));
+#endif /* IDETAPE_DEBUG */
+                       tape->block_address=idetape_swap_long (result->first_block);
+                       tape->block_address_valid=1;
+                       idetape_end_request (1,HWGROUP (drive));
+               }
+       }
+       else {
+               printk ("Aborting request\n");
+               idetape_end_request (0,HWGROUP (drive));
+       }
+       return;
+}
+
+/*
+ *     Our special ide-tape ioctl's.
+ *
+ *     Currently there aren't any significant ioctl's.
+ *     mtio.h compatible commands should be issued to the character device
+ *     interface.
+ */
+int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+                       unsigned int cmd, unsigned long arg)
+{
+       idetape_packet_command_t pc;
+       int retval;
+       
+       pc.buffer=pc.temp_buffer;
+       pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+       pc.current_position=pc.temp_buffer;
+
+#if IDETAPE_DEBUG      
+       printk ("ide-tape: Reached idetape_blkdev_ioctl\n");
+#endif /* IDETAPE_DEBUG */
+       switch (cmd) {
+               case IDETAPE_INQUIRY_IOCTL:
+#if IDETAPE_DEBUG
+                       printk ("Adding INQUIRY packet command to the tail of the request queue\n");
+#endif /* IDETAPE_DEBUG */
+                       idetape_create_inquiry_cmd (&pc);
+                       pc.buffer=pc.temp_buffer;
+                       pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+                       pc.current_position=pc.temp_buffer;
+                       return (idetape_queue_pc_tail (drive,&pc));
+               case IDETAPE_LOCATE_IOCTL:
+#if IDETAPE_DEBUG
+                       printk ("Adding LOCATE packet command to the tail of the request queue\n");
+#endif /* IDETAPE_DEBUG */
+                       idetape_create_locate_cmd (&pc,arg,0);
+                       retval=idetape_queue_pc_tail (drive,&pc);
+                       if (retval!=0) return (retval);
+                       
+                       idetape_create_read_position_cmd (&pc);
+                       pc.buffer=pc.temp_buffer;
+                       pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+                       pc.current_position=pc.temp_buffer;
+                       return (idetape_queue_pc_tail (drive,&pc));
+/*
+               case IDETAPE_RESET_IOCTL:
+                       printk ("Resetting drive\n");
+                       return (!ide_do_reset (drive));
+*/
+               default:
+                       return -EIO;
+       }
+}
+
+/*
+ *     Functions which handle requests.
+ */
+
+/*
+ *     idetape_end_request is used to end a request.
+ *
+ *     It is very similiar to ide_end_request, with a major difference - If
+ *     we are handling our own requests rather than requests which originate
+ *     in the buffer cache, we set rq->errors to 1 if the request failed.
+ */
+
+void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+
+{
+       ide_drive_t *drive = hwgroup->drive;
+       struct request *rq = hwgroup->rq;
+
+       if (rq->cmd == READ || rq->cmd == WRITE) {      /* Buffer cache originated request */
+               ide_end_request (uptodate,hwgroup);     /* Let the common code handle it */
+               return;
+       }
+                                                       /* Our own originated request */
+       rq->errors=!uptodate;                           /* rq->errors will tell us if the request was successfull */
+       ide_end_drive_cmd (drive, 0, 0);
+
+       /* The "up(rq->sem);" does the necessary "wake_up()" for us,
+        * providing we started sleeping with a "down()" call.
+        * This may not be the case if the driver converts a READ or WRITE
+        * request into a special internal rq->cmd type.   -ml
+        */
+        
+        /*
+         * As Mark explained, we do not need a "wake_up()" call here,
+         * since we are always sleeping with a "down()" call.
+         */
+
+}
+
+/*
+ *     idetape_do_request is our request handling function.    
+ */
+
+void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+
+{
+       idetape_tape_t *tape;
+       idetape_packet_command_t *pc;
+       struct request *new_rq;
+       idetape_status_reg_t status;
+
+       tape=&(drive->tape);
+               
+#if IDETAPE_DEBUG
+       printk ("Current request:\n");
+       printk ("rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+       printk ("sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDETAPE_DEBUG */
+
+       /* Retry a failed packet command */
+
+       if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+               idetape_issue_packet_command (drive,tape->failed_pc,&idetape_pc_intr);
+               return;
+       }
+
+       /* Check if we have a postponed request */
+       
+       if (tape->postponed_rq != NULL) {
+/* #if IDETAPE_DEBUG */
+               if (tape->postponed_rq->rq_status != RQ_ACTIVE || rq != tape->postponed_rq) {
+                       printk ("ide-tape: ide-tape.c bug - Two DSC requests were queued\n");
+                       idetape_end_request (0,HWGROUP (drive));
+                       return;
+               }
+/* #endif */ /* IDETAPE_DEBUG */
+               if (rq->cmd == IDETAPE_PACKET_COMMAND_REQUEST_TYPE1) {
+       
+                       /* Media access command */
+                       
+                       tape->postponed_rq = NULL;
+                       idetape_media_access_finished (drive);
+                       return;
+               }
+               
+               /*
+                * Read / Write command - DSC polling was done before the
+                * actual command - Continue normally so that the command
+                * will be performed below.
+                */
+                
+                tape->postponed_rq = NULL;
+       }       
+       
+
+       if (rq->cmd == READ || rq->cmd == IDETAPE_READ_REQUEST || rq->cmd == WRITE || rq->cmd == IDETAPE_WRITE_REQUEST) {
+
+               if (!tape->block_address_valid || tape->block_address!=rq->sector) {            /* Re-position the tape */
+               
+                       if (tape->locate_to == rq->sector && tape->locate_retries > IDETAPE_LOCATE_RETRIES) {
+                               printk ("ide-tape: Can not reach block %lu - Aborting request\n",rq->sector);
+                               tape->locate_retries=0;
+                               idetape_end_request (0,HWGROUP (drive));
+                               return;
+                       }
+                                               
+                       if (tape->locate_to == rq->sector)
+                               tape->locate_retries++;
+                       else {
+                               tape->locate_to=rq->sector;
+                               tape->locate_retries=1;
+                       }
+#if IDETAPE_DEBUG
+                       printk ("ide-tape: We are not at the requested block\n");
+                       printk ("ide-tape: Re-positioning tape\n");
+                       printk ("ide-tape: Adding READ POSITION command to the head of the queue\n");
+#endif /* IDETAPE_DEBUG */
+                       pc=idetape_next_pc_storage (drive);
+                       new_rq=idetape_next_rq_storage (drive);
+                       idetape_create_read_position_cmd (pc); 
+                       pc->buffer=pc->temp_buffer;
+                       pc->buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+                       pc->current_position=pc->temp_buffer;
+                       idetape_queue_pc_head (drive,pc,new_rq);
+#if IDETAPE_DEBUG                      
+                       printk ("ide-tape: Adding LOCATE %lu command to the head of the queue\n",rq->sector);
+#endif /* IDETAPE_DEBUG */
+                       pc=idetape_next_pc_storage (drive);
+                       new_rq=idetape_next_rq_storage (drive);
+                       idetape_create_locate_cmd (pc,rq->sector,0);
+                       idetape_queue_pc_head (drive,pc,new_rq);
+
+                       if (!tape->block_address_valid) {               /* The tape doesn't know the position - help it */
+                                                                       /* by rewinding the tape */
+#if IDETAPE_DEBUG                      
+                               printk ("ide-tape: Adding LOCATE 0 command to the head of the queue\n");
+#endif /* IDETAPE_DEBUG */
+                               pc=idetape_next_pc_storage (drive);
+                               new_rq=idetape_next_rq_storage (drive);
+                               idetape_create_locate_cmd (pc,0,0);
+                               idetape_queue_pc_head (drive,pc,new_rq);
+                       }
+                       
+                       return;
+               }
+               else
+                       tape->locate_retries=0;
+       }
+       
+       switch (rq->cmd) {
+               case READ:
+               case IDETAPE_READ_REQUEST:
+#if IDETAPE_DEBUG
+                       if (rq->cmd == READ)
+                               printk ("ide-tape: Handling buffer cache READ request\n");
+                       else
+                               printk ("ide-tape: Handling our own (not buffer cache originated) READ request\n");
+#endif /* IDETAPE_DEBUG */                     
+                       status.all=IN_BYTE (IDETAPE_STATUS_REG);
+                       if (!status.b.dsc) {                            /* Tape buffer not ready to accept r/w command */
+#if IDETAPE_DEBUG
+                               printk ("ide-tape: DSC != 1 - Postponing read request\n");
+#endif /* IDETAPE_DEBUG */     
+                               tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+                               idetape_postpone_request (drive);       /* Allow ide.c to process requests from */
+                               return;
+                       }                       
+
+                       tape->last_written_valid=0;
+                       
+                       pc=idetape_next_pc_storage (drive);
+
+                       idetape_create_read_cmd (pc,rq->current_nr_sectors);
+                       
+                       pc->buffer=rq->buffer;
+                       pc->buffer_size=rq->current_nr_sectors*512;
+                       pc->current_position=rq->buffer;
+                       pc->request_transfer=rq->current_nr_sectors*512;
+
+                       idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
+                       return;
+               
+               case WRITE:
+               case IDETAPE_WRITE_REQUEST:
+#if IDETAPE_DEBUG
+                       if (rq->cmd == WRITE)
+                               printk ("ide-tape: Handling buffer cache WRITE request\n");
+                       else
+                               printk ("ide-tape: Handling our own (not buffer cache originated) WRITE request\n");
+#endif /* IDETAPE_DEBUG */                     
+
+                       status.all=IN_BYTE (IDETAPE_STATUS_REG);
+                       if (!status.b.dsc) {                            /* Tape buffer not ready to accept r/w command */
+#if IDETAPE_DEBUG
+                               printk ("ide-tape: DSC != 1 - Postponing write request\n");
+#endif /* IDETAPE_DEBUG */     
+                               tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+                               idetape_postpone_request (drive);       /* Allow ide.c to process requests from */
+                               return;
+                       }                       
+
+                       tape->last_written_valid=1;
+                       tape->last_written_block=rq->sector;
+                       
+                       pc=idetape_next_pc_storage (drive);
+
+                       idetape_create_write_cmd (pc,rq->current_nr_sectors);
+                       
+                       pc->buffer=rq->buffer;
+                       pc->buffer_size=rq->current_nr_sectors*512;
+                       pc->current_position=rq->buffer;
+                       pc->request_transfer=rq->current_nr_sectors*512;
+
+                       idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
+                       return;
+                                       
+               case IDETAPE_PACKET_COMMAND_REQUEST_TYPE1:
+               case IDETAPE_PACKET_COMMAND_REQUEST_TYPE2:
+/*
+ *     This should be unnecessary (postponing of a general packet command),
+ *     but I have occasionally missed DSC on a media access command otherwise.
+ *     ??? Still have to figure it out ...
+ */
+                       status.all=IN_BYTE (IDETAPE_STATUS_REG);
+                       if (!status.b.dsc) {                            /* Tape buffers are still not ready */
+#if IDETAPE_DEBUG
+                               printk ("ide-tape: DSC != 1 - Postponing packet command request\n");
+#endif IDETAPE_DEBUG
+                               rq->cmd=IDETAPE_PACKET_COMMAND_REQUEST_TYPE2;   /* Note that we are waiting for DSC *before* we */
+                                                                               /* even issued the command */
+                               tape->dsc_polling_frequency=IDETAPE_DSC_READ_WRITE_FREQUENCY;
+                               idetape_postpone_request (drive);       /* Allow ide.c to process requests from */
+                               return;
+                       }
+                       rq->cmd=IDETAPE_PACKET_COMMAND_REQUEST_TYPE1;
+                       pc=(idetape_packet_command_t *) rq->buffer;
+                       idetape_issue_packet_command (drive,pc,&idetape_pc_intr);
+                       return;
+
+               default:
+                       printk ("ide-tape: Unknown command in request - Aborting request\n");
+                       idetape_end_request (0,HWGROUP (drive));
+       }       
+}
+
+/*
+ *     idetape_queue_pc_tail is based on the following functions:
+ *
+ *     ide_do_drive_cmd from ide.c
+ *     cdrom_queue_request and cdrom_queue_packet_command from ide-cd.c
+ *
+ *     We add a special packet command request to the tail of the request queue,
+ *     and wait for it to be serviced.
+ *
+ *     This is not to be called from within the request handling part
+ *     of the driver ! We allocate here data in the stack, and it is valid
+ *     until the request is finished. This is not the case for the bottom
+ *     part of the driver, where we are always leaving the functions to wait
+ *     for an interrupt or a timer event.
+ *
+ *     From the bottom part of the driver, we should allocate safe memory
+ *     using idetape_next_pc_storage and idetape_next_rq_storage, and add
+ *     the request to the request list without waiting for it to be serviced !
+ *     In that case, we usually use idetape_queue_pc_head.
+ */
+
+int idetape_queue_pc_tail (ide_drive_t *drive,idetape_packet_command_t *pc)
+{
+       struct request rq;
+
+       ide_init_drive_cmd (&rq);
+       rq.buffer = (char *) pc;
+       rq.cmd = IDETAPE_PACKET_COMMAND_REQUEST_TYPE1;
+       return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ *     idetape_queue_pc_head generates a new packet command request in front
+ *     of the request queue, before the current request, so that it will be
+ *     processed immediately, on the next pass through the driver.
+ *
+ *     idetape_queue_pc_head is called from the request handling part of
+ *     the driver (the "bottom" part). Safe storage for the request should
+ *     be allocated with idetape_next_pc_storage and idetape_next_rq_storage
+ *     before calling idetape_queue_pc_head.
+ *
+ *     Memory for those requests is pre-allocated at initialization time, and
+ *     is limited to IDETAPE_PC_STACK requests. We assume that we have enough
+ *     space for the maximum possible number of inter-dependent packet commands.
+ *
+ *     The higher level of the driver - The ioctl handler and the character
+ *     device handling functions should queue request to the lower level part
+ *     and wait for their completion using idetape_queue_pc_tail or
+ *     idetape_queue_rw_tail.
+ */
+void idetape_queue_pc_head (ide_drive_t *drive,idetape_packet_command_t *pc,struct request *rq)
+
+{
+       ide_init_drive_cmd (rq);
+       rq->buffer = (char *) pc;
+       rq->cmd = IDETAPE_PACKET_COMMAND_REQUEST_TYPE1;
+       (void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+/*
+ *     idetape_queue_rw_tail is typically called from the character device
+ *     interface to generate a read/write request for the block device interface
+ *     and wait for it to be serviced. Note that cmd will be different than
+ *     a buffer cache originated read/write request. This will be used
+ *     in idetape_end_request.
+ *
+ *     Returns 0 on success or -EIO if an error occured.
+ */
+
+int idetape_queue_rw_tail (ide_drive_t *drive,int cmd,int blocks,char *buffer)
+
+{
+       idetape_tape_t *tape = &(drive->tape);
+       struct request rq;
+
+#if IDETAPE_DEBUG
+       printk ("idetape_queue_rw_tail: cmd=%d\n",cmd);
+#endif /* IDETAPE_DEBUG */
+       /* build up a special read request, and add it to the queue */
+       
+       ide_init_drive_cmd (&rq);
+       rq.buffer = buffer;
+       rq.cmd = cmd;
+       rq.sector = tape->block_address;
+       rq.nr_sectors = blocks;
+       rq.current_nr_sectors = blocks;
+       return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ *     Copied from ide.c (declared static there)
+ */
+
+void idetape_fixstring (byte *s, const int bytecount, const int byteswap)
+{
+       byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
+
+       if (byteswap) {
+               /* convert from big-endian to host byte order */
+               for (p = end ; p != s;) {
+                       unsigned short *pp = (unsigned short *) (p -= 2);
+                       *pp = ntohs(*pp);
+               }
+       }
+
+       /* strip leading blanks */
+       while (s != end && *s == ' ')
+               ++s;
+
+       /* compress internal blanks and strip trailing blanks */
+       while (s != end && *s) {
+               if (*s++ != ' ' || (s != end && *s && *s != ' '))
+                       *p++ = *(s-1);
+       }
+
+       /* wipe out trailing garbage */
+       while (p != end)
+               *p++ = '\0';
+}
+
+/*
+ *     idetape_zero_packet_command just zeros a packet command and
+ *     sets the number of retries to 0, as we haven't retried it yet.
+ */
+void idetape_zero_packet_command (idetape_packet_command_t *pc)
+
+{
+       int i;
+       
+       for (i=0;i<12;i++)
+               pc->c[i]=0;
+       pc->retries=0;
+}
+
+/*
+ *     idetape_swap_shorts converts a 16 bit number from little endian
+ *     to big endian format.
+ */
+unsigned short idetape_swap_short (unsigned short temp)
+
+{
+       union convert {
+               unsigned all    :16;
+               struct {
+                       unsigned b1     :8;
+                       unsigned b2     :8;
+               } b;
+       } original,converted;
+       
+       original.all=temp;
+       converted.b.b1=original.b.b2;
+       converted.b.b2=original.b.b1;
+       return (converted.all);
+}
+
+/*
+ *     idetape_swap_long converts from little endian to big endian format.
+ */
+unsigned long idetape_swap_long (unsigned long temp)
+
+{
+       union convert {
+               unsigned all    :32;
+               struct {
+                       unsigned b1     :8;
+                       unsigned b2     :8;
+                       unsigned b3     :8;
+                       unsigned b4     :8;
+               } b;
+       } original,converted;
+       
+       original.all=temp;
+       converted.b.b1=original.b.b4;
+       converted.b.b2=original.b.b3;
+       converted.b.b3=original.b.b2;
+       converted.b.b4=original.b.b1;
+       return (converted.all);
+}
+
+
+/*
+ *     idetape_next_pc_storage returns a pointer to a place in which we can
+ *     safely store a packet command, even though we intend to leave the
+ *     driver. A storage space for a maximum of IDETAPE_PC_STACK packet
+ *     commands is allocated at initialization time.
+ */
+idetape_packet_command_t *idetape_next_pc_storage (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape;
+       
+       tape=&(drive->tape);
+#if IDETAPE_DEBUG
+       printk ("ide-tape: pc_stack_index=%d\n",tape->pc_stack_index);
+#endif /* IDETAPE_DEBUG */
+       if (tape->pc_stack_index==IDETAPE_PC_STACK)
+               tape->pc_stack_index=0;
+       return (&(tape->pc_stack [tape->pc_stack_index++]));
+}
+
+/*
+ *     idetape_next_rq_storage is used along with idetape_next_pc_storage.
+ *     Since we queue packet commands in the request queue, we need to
+ *     allocate a request, along with the allocation of a packet command.
+ */
+/**************************************************************
+ *                                                            *
+ *  This should get fixed to use kmalloc(GFP_ATOMIC, ..)      *
+ *  followed later on by kfree().   -ml                       *
+ *                                                            *
+ **************************************************************/
+struct request *idetape_next_rq_storage (ide_drive_t *drive)
+
+{
+       idetape_tape_t *tape;
+       
+       tape=&(drive->tape);
+
+#if IDETAPE_DEBUG
+       printk ("ide-tape: rq_stack_index=%d\n",tape->rq_stack_index);
+#endif /* IDETAPE_DEBUG */
+       if (tape->rq_stack_index==IDETAPE_PC_STACK)
+               tape->rq_stack_index=0;
+       return (&(tape->rq_stack [tape->rq_stack_index++]));
+}
+
+/*
+ *     Block device interface functions
+ *
+ *     The default action is not to allow direct access to the block device
+ *     interface (-EBUSY will be returned on open).
+ */
+
+int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+
+{
+#if IDETAPE_ALLOW_OPENING_BLOCK_DEVICE
+       return (0);
+#else
+       printk ("ide-tape: The block device interface should not be used.\n");
+       printk ("ide-tape: Use the character device interfaces\n");
+       printk ("ide-tape: /dev/ht0 and /dev/nht0 instead.\n");
+       printk ("ide-tape: (Run linux/drivers/block/MAKEDEV.ide to create them)\n");
+       printk ("ide-tape: Refusing open request.\n");
+       return (-EBUSY);
+#endif /* IDETAPE_ALLOW_OPENING_BLOCK_DEVICE */
+}
+
+void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+
+{
+       return;
+}
+
+/*
+ *     Character device interface functions
+ */
+
+/*
+ *     lseek is currently not installed.
+ */
+int idetape_chrdev_lseek (struct inode *inode, struct file *file, off_t offset, int origin)
+
+{
+       ide_drive_t *drive;
+       
+#if IDETAPE_DEBUG
+       printk ("Reached idetape_chrdev_lseek\n");
+#endif /* IDETAPE_DEBUG */
+
+       drive=idetape_chrdev.drive;
+       if (idetape_position_tape (drive,offset) != 0) {
+               printk ("ide-tape: Rewinding tape failed\n");
+               return (-1);
+       }
+       
+       return (0);
+}
+
+/*
+ *     Our character device read / write functions.
+ *
+ *     The tape is optimized to maximize throughpot when it is transfering
+ *     an integral number of the "continous transfer limit", which is
+ *     a parameter of the specific tape (26 KB on my particular tape). The
+ *     resulting increase in performance should be dramatical. In the
+ *     character device read/write functions, we split the current
+ *     request to units of the above size, and handle the remaining bytes
+ *     in some other sub-functions.
+ *
+ *     In case the count number is not even an integral number of the tape
+ *     block size (usually 512 or 1024 bytes), we will pad the transfer with
+ *     zeroes (write) or read the entire block and return only the requested
+ *     bytes (but the tape will be in the "wrong" position). Do not supply
+ *     such a count value unless you are going to close the device right
+ *     after this request.
+ *
+ *     Again, for best results use an integral number of the tape's parameter
+ *     (which is displayed in the driver installation stage). I will soon
+ *     add an ioctl to get this important parameter.
+ */
+
+/*
+ *     Our character device read function.
+ */
+
+int idetape_chrdev_read (struct inode *inode, struct file *file, char *buf, int count)
+
+{
+       ide_drive_t *drive;
+       idetape_tape_t *tape;
+       int blocks,remainder,retval,ctl_bytes;
+       char *buf_ptr;
+       unsigned long previous_block_address,actually_read;
+
+#if IDETAPE_DEBUG
+       printk ("Reached idetape_chrdev_read\n");
+#endif /* IDETAPE_DEBUG */
+
+       drive=idetape_chrdev.drive;
+       tape=&(drive->tape);
+       tape->last_dt_was_write=0;
+
+       if (count==0)
+               return (0);
+
+       actually_read=0;
+       buf_ptr=buf;
+       ctl_bytes=tape->capabilities.ctl*tape->tape_block_size;
+       blocks=count/ctl_bytes;
+       remainder=count%ctl_bytes;
+       
+       while (blocks) {
+#if IDETAPE_DEBUG
+               printk ("Adding a READ request to the block device request queue\n");
+#endif /* IDETAPE_DEBUG */
+               previous_block_address=tape->block_address;
+               retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,tape->capabilities.ctl,tape->data_buffer);
+               actually_read+=tape->tape_block_size*(tape->block_address-previous_block_address);
+
+               if (retval) {
+                       printk ("ide-tape: Error occured while reading\n");
+                       return (actually_read);
+               }
+#if IDETAPE_DEBUG
+       printk ("Copying %d bytes to the user space memory\n",ctl_bytes);
+#endif /* IDETAPE_DEBUG */
+
+               memcpy_tofs (buf_ptr,tape->data_buffer,ctl_bytes);
+               buf_ptr+=ctl_bytes;
+               blocks--;
+       }
+       if (remainder)
+               return (actually_read+idetape_chrdev_read_remainder (inode,file,buf_ptr,remainder));
+       else
+               return (actually_read);         
+}
+int idetape_chrdev_read_remainder (struct inode *inode, struct file *file, char *buf, int count)
+
+{
+       ide_drive_t *drive;
+       idetape_tape_t *tape;
+       int blocks,remainder,retval;
+       unsigned long previous_block_address,actually_read;
+
+#if IDETAPE_DEBUG
+       printk ("Reached idetape_chrdev_read_remainder\n");
+#endif /* IDETAPE_DEBUG */
+
+       drive=idetape_chrdev.drive;
+       tape=&(drive->tape);
+
+       tape->last_dt_was_write=0;
+
+       if (count==0)
+               return (0);
+
+
+       blocks=count/512;
+       remainder=count%512;
+       if (remainder) {
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Padding read to block boundary\n");
+#endif /* IDETAPE_DEBUG */
+               blocks++;
+       }
+#if IDETAPE_DEBUG
+       printk ("Adding a READ request to the block device request queue\n");
+#endif /* IDETAPE_DEBUG */
+       previous_block_address=tape->block_address;
+       retval=idetape_queue_rw_tail (drive,IDETAPE_READ_REQUEST,blocks,tape->data_buffer);
+       if (retval) {
+               printk ("ide-tape: Error occured while reading\n");
+               actually_read=512*(tape->block_address-previous_block_address);
+               if (actually_read > count)
+                       actually_read=count;
+               if (actually_read != 0)
+                       memcpy_tofs (buf,tape->data_buffer,actually_read);
+               return (actually_read);
+       }
+#if IDETAPE_DEBUG
+       printk ("Copying %d bytes to the user space memory\n",count);
+#endif /* IDETAPE_DEBUG */
+       memcpy_tofs (buf,tape->data_buffer,count);
+       return (count);
+}
+
+int idetape_chrdev_write (struct inode *inode, struct file *file, const char *buf, int count)
+
+{
+       ide_drive_t *drive;
+       idetape_tape_t *tape;
+       int blocks,remainder,retval,ctl_bytes;
+       const char *buf_ptr;
+       unsigned long previous_block_address,actually_written;
+
+#if IDETAPE_DEBUG
+       printk ("Reached idetape_chrdev_write\n");
+#endif /* IDETAPE_DEBUG */
+
+       drive=idetape_chrdev.drive;
+       tape=&(drive->tape);
+       tape->last_dt_was_write=1;
+
+       if (count==0)
+               return (0);
+
+       actually_written=0;
+       buf_ptr=buf;
+       ctl_bytes=tape->capabilities.ctl*tape->tape_block_size;
+       blocks=count/ctl_bytes;
+       remainder=count%ctl_bytes;
+
+       while (blocks) {
+#if IDETAPE_DEBUG
+               printk ("Copying %d bytes from the user space memory\n",ctl_bytes);
+#endif /* IDETAPE_DEBUG */
+               memcpy_fromfs (tape->data_buffer,buf_ptr,ctl_bytes);
+               buf_ptr+=ctl_bytes;
+#if IDETAPE_DEBUG
+               printk ("Adding a WRITE request to the block device request queue\n");
+#endif /* IDETAPE_DEBUG */
+               previous_block_address=tape->block_address;
+               retval=idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,tape->capabilities.ctl,tape->data_buffer);
+               actually_written+=tape->tape_block_size*(tape->block_address-previous_block_address);
+
+               if (retval) {
+                       printk ("ide-tape: Error occured while writing\n");
+                       return (actually_written);
+               }
+               blocks--;
+       }
+       if (remainder)
+               return (actually_written+idetape_chrdev_write_remainder (inode,file,buf_ptr,remainder));
+       else
+               return (actually_written);              
+}
+
+int idetape_chrdev_write_remainder (struct inode *inode, struct file *file, const char *buf, int count)
+
+{
+       ide_drive_t *drive;
+       idetape_tape_t *tape;
+       int blocks,remainder,retval;
+       char *ptr;
+       unsigned long previous_block_address,actually_written;
+
+#if IDETAPE_DEBUG
+       printk ("Reached idetape_chrdev_write_remainder\n");
+#endif /* IDETAPE_DEBUG */
+               
+       drive=idetape_chrdev.drive;
+       tape=&(drive->tape);
+
+       blocks=count/512;
+       remainder=count%512;
+       if (remainder)
+               blocks++;
+#if IDETAPE_DEBUG
+       printk ("Copying %d bytes from the user space memory\n",count);
+#endif /* IDETAPE_DEBUG */
+
+       memcpy_fromfs (tape->data_buffer,buf,count);
+       if (remainder) {
+#if IDETAPE_DEBUG
+       printk ("ide-tape: Padding written data to block boundary\n");
+#endif /* IDETAPE_DEBUG */
+               ptr=tape->data_buffer+(blocks-1)*512;
+               memset (ptr,0,remainder);
+       }
+#if IDETAPE_DEBUG
+       printk ("Adding a WRITE request to the block device request queue\n");
+#endif /* IDETAPE_DEBUG */
+
+       previous_block_address=tape->block_address;
+       retval=idetape_queue_rw_tail (drive,IDETAPE_WRITE_REQUEST,blocks,tape->data_buffer);
+       if (retval) {
+               printk ("ide-tape: Error occured while writing\n");
+               actually_written=512*(tape->block_address-previous_block_address);
+               if (actually_written > count)
+                       actually_written=count;
+               return (actually_written);
+       }
+       return (count);
+}
+
+/*
+ *     Our character device ioctls.
+ *
+ *     General mtio.h magnetic io commands are supported here, and not in
+ *     the correspoding block interface.
+ *
+ *     Our own ide-tape ioctls are supported on both interfaces.
+ */
+
+int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+
+{
+       struct mtop mtop;
+       ide_drive_t *drive;
+       int retval;
+
+#if IDETAPE_DEBUG
+       printk ("Reached idetape_chrdev_ioctl, cmd=%u\n",cmd);
+#endif
+
+       drive=idetape_chrdev.drive;
+
+       switch (cmd) {
+               case MTIOCTOP:
+                       retval=verify_area (VERIFY_READ,(char *) arg,sizeof (struct mtop));
+                       if (retval) return (retval);
+                       memcpy_fromfs ((char *) &mtop, (char *) arg, sizeof (struct mtop));
+                       return (idetape_mtioctop (drive,mtop.mt_op,mtop.mt_count));
+               default:
+                       return (idetape_blkdev_ioctl (drive,inode,file,cmd,arg));
+       }
+}
+
+/*
+ *     idetape_mtioctop is called from idetape_chrdev_ioctl when
+ *     the general mtio MTIOCTOP ioctl is requested.
+ *
+ *     We currently support the following mtio.h operations:
+ *
+ *     MTFSF   -       Space over mt_count filemarks in the positive direction.
+ *                     The tape is positioned after the last spaced filemark.
+ *
+ *     MTFSFM  -       Same as MTFSF, but the tape is positioned before the
+ *                     last filemark.
+ *
+ *     MTBSF   -       Steps background over mt_count filemarks, tape is
+ *                     positioned before the last filemark.
+ *
+ *     MTBSFM  -       Like MTBSF, only tape is positioned after the last filemark.
+ *
+ *     MTWEOF  -       Writes mt_count filemarks. Tape is positioned after
+ *                     the last written filemark.
+ *
+ *     MTREW   -       Rewindes tape.
+ *
+ *     MTNOP   -       Flushes tape buffers.
+ *
+ *     MTEOM   -       Moves to the end of recorded data.
+ *
+ *     MTERASE -       Erases tape.
+ *
+ *     The following commands are currently not supported:
+ *
+ *     MTFSR, MTBSR, MTFSS, MTBSS, MTWSM, MTOFFL, MTRETEN, MTSEEK, MTSETBLK,
+ *     MTSETDENSITY, MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD.
+ */
+int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count)
+
+{
+       int i,retval;
+       
+       idetape_packet_command_t pc;
+
+       pc.buffer=pc.temp_buffer;
+       pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+       pc.current_position=pc.temp_buffer;
+
+       idetape_create_write_filemark_cmd (&pc,0);      /* Flush buffers */
+       retval=idetape_queue_pc_tail (drive,&pc);
+       if (retval) return (retval);
+
+       switch (mt_op) {
+               case MTFSF:
+#if IDETAPE_DEBUG
+                       printk ("Handling MTFSF command\n");
+#endif /* IDETAPE_DEBUG */
+                       idetape_create_space_cmd (&pc,mt_count,IDETAPE_SPACE_OVER_FILEMARK);
+                       return (idetape_queue_pc_tail (drive,&pc));
+               case MTFSFM:
+#if IDETAPE_DEBUG
+                       printk ("Handling MTFSFM command\n");
+#endif /* IDETAPE_DEBUG */
+                       retval=idetape_mtioctop (drive,MTFSF,mt_count);
+                       if (retval) return (retval);
+                       return (idetape_mtioctop (drive,MTBSF,1));
+               case MTBSF:
+#if IDETAPE_DEBUG
+                       printk ("Handling MTBSF command\n");
+#endif /* IDETAPE_DEBUG */
+                       idetape_create_space_cmd (&pc,-mt_count,IDETAPE_SPACE_OVER_FILEMARK);
+                       return (idetape_queue_pc_tail (drive,&pc));
+               case MTBSFM:
+#if IDETAPE_DEBUG
+                       printk ("Handling MTBSFM command\n");
+#endif /* IDETAPE_DEBUG */
+                       retval=idetape_mtioctop (drive,MTBSF,mt_count);
+                       if (retval) return (retval);
+                       return (idetape_mtioctop (drive,MTFSF,1));
+               case MTWEOF:
+#if IDETAPE_DEBUG
+                       printk ("Handling MTWEOF command\n");
+#endif /* IDETAPE_DEBUG */
+               
+                       for (i=0;i<mt_count;i++) {
+                               idetape_create_write_filemark_cmd (&pc,1);
+                               retval=idetape_queue_pc_tail (drive,&pc);
+                               if (retval) return (retval);
+                       }
+                       return (0);
+               case MTREW:
+#if IDETAPE_DEBUG
+                       printk ("Handling MTREW command\n");
+#endif /* IDETAPE_DEBUG */
+                       return (idetape_rewind_tape (drive));
+               case MTNOP:
+#if IDETAPE_DEBUG
+                       printk ("Handling MTNOP command\n");
+#endif /* IDETAPE_DEBUG */
+                       idetape_create_write_filemark_cmd (&pc,0);
+                       return (idetape_queue_pc_tail (drive,&pc));
+               case MTEOM:
+#if IDETAPE_DEBUG
+                       printk ("Handling MTEOM command\n");
+#endif /* IDETAPE_DEBUG */
+               
+                       idetape_create_space_cmd (&pc,0,IDETAPE_SPACE_TO_EOD);
+                       return (idetape_queue_pc_tail (drive,&pc));
+               case MTERASE:
+#if IDETAPE_DEBUG
+                       printk ("Handling MTERASE command\n");
+#endif /* IDETAPE_DEBUG */
+                       retval=idetape_position_tape (drive,0);
+                       if (retval) return (retval);
+                       idetape_create_erase_cmd (&pc);
+                       return (idetape_queue_pc_tail (drive,&pc));
+               default:
+                       printk ("ide-tape: MTIO operation %d not supported\n",mt_op);
+                       return (-EIO);
+       }
+}
+
+/*
+ *     Our character device open function.
+ */
+
+int idetape_chrdev_open (struct inode *inode, struct file *filp)
+
+{
+       ide_drive_t *drive;
+       idetape_tape_t *tape;
+       unsigned long flags;
+       unsigned int minor;
+               
+       save_flags (flags);
+       cli();
+
+#if IDETAPE_DEBUG
+       printk ("Reached idetape_chrdev_open\n");
+#endif /* IDETAPE_DEBUG */
+
+
+       drive=idetape_chrdev.drive;
+       tape=&(drive->tape);
+       minor=MINOR (inode->i_rdev);
+
+       if (minor!=0 && minor!=128) {           /* Currently supporting only one */
+               restore_flags (flags);          /* tape drive */
+               return (-ENXIO);
+       }
+
+       if (tape->busy) {
+               restore_flags (flags);          /* Allowing access only through one */
+               return (-EBUSY);                /* one file descriptor */
+       }
+
+       tape->busy=1;
+       restore_flags (flags);
+
+       if (!tape->block_address_valid) {
+               if (idetape_rewind_tape (drive)) {
+                       printk ("ide-tape: Rewinding tape failed\n");
+                       tape->busy=0;
+                       return (-EIO);
+               }
+       }
+
+       tape->last_dt_was_write=0;
+       
+       return (0);
+}
+
+/*
+ *     Our character device release function.
+ */
+
+void idetape_chrdev_release (struct inode *inode, struct file *filp)
+
+{
+       ide_drive_t *drive;
+       idetape_tape_t *tape;
+       
+       unsigned int minor;
+       idetape_packet_command_t pc;
+       unsigned long flags;
+                       
+#if IDETAPE_DEBUG
+       printk ("Reached idetape_chrdev_release\n");
+#endif /* IDETAPE_DEBUG */
+
+       drive=idetape_chrdev.drive;
+       tape=&(drive->tape);
+       minor=MINOR (inode->i_rdev);
+
+       if (tape->last_dt_was_write) {
+               idetape_create_write_filemark_cmd (&pc,1);      /* Write a filemark */
+               if (idetape_queue_pc_tail (drive,&pc)) {
+                       printk ("ide-tape: Couldn't write a filemark\n");
+                       /* ??? */
+               }
+       }
+       else {
+               idetape_create_write_filemark_cmd (&pc,0);      /* Flush buffers */
+               if (idetape_queue_pc_tail (drive,&pc)) {
+                       printk ("ide-tape: Couldn't flush buffers\n");
+                       /* ??? */
+               }
+       }
+
+       if (minor < 128) {
+               if (idetape_rewind_tape (drive)) {
+                       printk ("ide-tape: Rewinding tape failed\n");
+                       /* ??? */
+               }
+       }
+
+       save_flags (flags);
+       cli();
+       tape->busy=0;
+       restore_flags (flags);
+               
+       return;
+}
+
+/*
+ *     idetape_position_tape positions the tape to the requested block
+ *     using the LOCATE packet command. A READ POSITION command is then
+ *     issued to check where we are positioned.
+ *
+ *     Like all higher level operations, we queue the commands at the tail
+ *     of the request queue and wait for their completion.
+ *     
+ */
+int idetape_position_tape (ide_drive_t *drive,unsigned long block)
+
+{
+       int retval;
+       idetape_packet_command_t pc;
+
+       idetape_create_locate_cmd (&pc,block,0);
+       retval=idetape_queue_pc_tail (drive,&pc);
+       if (retval!=0) return (retval);
+                       
+       idetape_create_read_position_cmd (&pc);
+       pc.buffer=pc.temp_buffer;
+       pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+       pc.current_position=pc.temp_buffer;
+       return (idetape_queue_pc_tail (drive,&pc));
+}
+
+/*
+ *     Rewinds the tape to the Begining Of the current Partition (BOP).
+ *
+ *     We currently support only one partition.
+ */ 
+
+int idetape_rewind_tape (ide_drive_t *drive)
+
+{
+       int retval;
+       idetape_packet_command_t pc;
+#if IDETAPE_DEBUG
+       printk ("Reached idetape_rewind_tape\n");
+#endif /* IDETAPE_DEBUG */     
+
+       idetape_create_write_filemark_cmd (&pc,0);      /* Flush buffers */
+       retval=idetape_queue_pc_tail (drive,&pc);
+       if (retval) return (retval);
+       
+       idetape_create_rewind_cmd (&pc);
+       retval=idetape_queue_pc_tail (drive,&pc);
+       if (retval) return (retval);
+                       
+       idetape_create_read_position_cmd (&pc);
+       pc.buffer=pc.temp_buffer;
+       pc.buffer_size=IDETAPE_TEMP_BUFFER_SIZE;
+       pc.current_position=pc.temp_buffer;
+       return (idetape_queue_pc_tail (drive,&pc));
+}
diff --git a/drivers/block/ide-tape.h b/drivers/block/ide-tape.h
new file mode 100644 (file)
index 0000000..7dabd4e
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * linux/drivers/block/ide-tape.h      Version 1.0 - ALPHA     Dec  3, 1995
+ *
+ * Copyright (C) 1995 Gadi Oxman <tgud@tochnapc2.technion.ac.il>
+ */
+
+/*
+ * Include file for the IDE ATAPI streaming tape driver.
+ *
+ * This file contains various ide-tape related structures and function
+ * prototypes which are already used in ide.h.
+ *
+ * The various compile time options are described below.
+ */
+
+#ifndef IDETAPE_H
+#define IDETAPE_H 
+
+/**************************** Tunable parameters *****************************/
+
+/*
+ *     Setting IDETAPE_DEBUG to 1 will:
+ *
+ *             1.      Generally log all driver actions.
+ *             2.      Enable self-sanity checks in some places.
+ *
+ *     Use IDETAPE_DEBUG when encountering a problem with the driver.
+ *
+ *     Setting IDETAPE_DEBUG to 0 will restore normal operation mode:
+ *
+ *             1.      Disable logging normal successful operations.
+ *             2.      Disable self-sanity checks.
+ *             3.      Errors will still be logged, of course.
+ */
+#define        IDETAPE_DEBUG           0
+
+/*
+ *     After each failed packet command we issue a request sense command
+ *     and retry the packet command IDETAPE_MAX_PC_RETRIES times.
+ *
+ *     Setting IDETAPE_MAX_PC_RETRIES to 0 will disable retries.
+ */
+
+#define        IDETAPE_MAX_PC_RETRIES  2
+
+/*
+ *     In case the tape is not at the requested block, we re-position the
+ *     tape. Repeat the procedure for IDETAPE_LOCATE_RETRIES times before
+ *     we give up and abort the request. Note that this should not usually
+ *     happen when using only the character device interface.
+ */
+
+#define        IDETAPE_LOCATE_RETRIES  1
+
+/*
+ *     With each packet command, we allocate a buffer of
+ *     IDETAPE_TEMP_BUFFER_SIZE bytes. This is used for several packet
+ *     commands (Not for READ/WRITE commands).
+ *
+ *     The default below is too high - We should be using around 100 bytes
+ *     typically, but I didn't check all the cases, so I rather be on the
+ *     safe size.
+ */
+#define        IDETAPE_TEMP_BUFFER_SIZE 256
+
+/*
+ *     In various places in the driver, we need to allocate storage
+ *     for packet commands and requests, which will remain valid while
+ *     we leave the driver to wait for an interrupt or a timeout event.
+ *
+ *     In the corresponding ide_drive_t structure, we pre-allocate storage
+ *     for IDETAPE_PC_STACK packet commands and requests. This storage is
+ *     used as a circular array - Each time we reach the last entry, we
+ *     warp around to the first.
+ *
+ *     It is crucial that we have enough entries for the maximum number
+ *     of packet commands / sub-requests which we need to allocate during
+ *     the handling of a specific request.
+ *
+ *     Follows a worse case calculation of the required storage, with a
+ *     large safety margin. Hopefully. :-)
+ */
+
+#define        IDETAPE_PC_STACK        10+\
+                               IDETAPE_MAX_PC_RETRIES+\
+                               3*IDETAPE_LOCATE_RETRIES*IDETAPE_MAX_PC_RETRIES
+/*
+ *     Media access packet command (like the LOCATE command) have immediate
+ *     status with a delayed (and usually long) execution. The tape doesn't
+ *     issue an interrupt when the command is actually complete (so that the
+ *     bus is freed to use the other IDE device on the same interface), so we
+ *     must for poll for this event.
+ *
+ *     We set a timer with polling frequency of 1/IDETAPE_DSC_MEDIA_ACCESS_FREQUENCY
+ *     in this case. We also poll for DSC *before* read/write commands. At
+ *     this time the DSC role is changed and instead of signalling command
+ *     completion, it will signal buffer availability. Since read/write
+ *     commands are fast in comparision to media access commands, the polling
+ *     frequency here should be much higher.
+ *
+ *     We will insist of reading DSC=1 for IDETAPE_DSC_COUNT times in a row,
+ *     to accommodate for random fluctuations in the sampling of DSC.
+ *     We will also set IDETAPE_DSC_POLLING_FREQUENCY to a rather low
+ *     frequency which besides freeing the CPU and the bus will let
+ *     random fluctuations a time to settle down.
+ *
+ *     We also set a timeout for the timer, in case something goes wrong.
+ *     The timeout should be longer then the maximum execution time of a
+ *     tape operation. I still have to measure exactly how much time does
+ *     it take to space over a far filemark, etc. It seemed that 15 minutes
+ *     was way too low, so I am meanwhile setting it to a rather large
+ *     timeout - 2 Hours.
+ *
+ *     Once we pass a threshold, the polling frequency will change to
+ *     a slow frequency: On relatively fast operations, there is a point
+ *     in polling fast, but if we sense that the operation is taking too
+ *     much time, we will poll at a lower frequency.
+ */
+
+#define        IDETAPE_DSC_FAST_MEDIA_ACCESS_FREQUENCY 1*HZ            /* 1 second */
+#define        IDETAPE_FAST_SLOW_THRESHOLD             5*60*HZ         /* 5 minutes */
+#define IDETAPE_DSC_SLOW_MEDIA_ACCESS_FREQUENCY        60*HZ           /* 1 minute */
+#define        IDETAPE_DSC_READ_WRITE_FREQUENCY        HZ/20           /* 50 msec */
+#define        IDETAPE_DSC_TIMEOUT                     2*60*60*HZ      /* 2 hours */
+#define IDETAPE_DSC_COUNT                      1               /* Assume no DSC fluctuations */
+
+/*
+ *     As explained in many places through the code, we provide both a block
+ *     device and a character device interface to the tape. The block device
+ *     interface is needed for compatibility with ide.c. The character device
+ *     interface is the higher level of the driver, and passes requests
+ *     to the lower part of the driver which interfaces with ide.c.
+ *     Using the block device interface, we can bypass this high level
+ *     of the driver, talking directly with the lower level part.
+ *
+ *     It is intended that the character device interface will be used by
+ *     the user. To prevent mistakes in this regard, opening of the block
+ *     device interface will be refused if ALLOW_OPENING_BLOCK_DEVICE is 0.
+ *
+ *     Do not change the following parameter unless you are developing
+ *     the driver itself.
+ */
+#define IDETAPE_ALLOW_OPENING_BLOCK_DEVICE     0
+
+/*************************** End of tunable parameters ***********************/
+
+/*
+ *     Definitions which are already needed in ide.h
+ */
+/*
+ *     The following is currently not used.
+ */
+
+typedef enum {no_excess_data,excess_data_read,excess_data_write} excess_data_status_t;
+
+struct ide_drive_s;                            /* Forward declaration - Will be defined later in ide.h */
+typedef void (idetape_pc_completed_t)(struct ide_drive_s *);
+
+/*
+ *     Our view of a packet command.
+ */
+
+typedef struct idetape_packet_command_s {
+       byte c [12];                            /* Actual packet bytes */
+       
+       byte retries;                           /* On each retry, we increment retries */
+       byte error;                             /* Set when an error occured */
+       byte active;                            /* Set when a packet command is in progress */
+       byte wait_for_dsc;                      /* 1 When polling for DSC */
+       byte dsc_count;         
+       unsigned long request_transfer;         /* Bytes to transfer */
+       unsigned long actually_transferred;     /* Bytes actually transferred */
+       unsigned long buffer_size;              /* Size of our data buffer */
+       byte *buffer;                           /* Data buffer */
+       byte *current_position;                 /* Pointer into the above buffer */
+       byte writing;                           /* Data direction */            
+       idetape_pc_completed_t *callback;       /* Called when this packet command is completed */
+       byte temp_buffer [IDETAPE_TEMP_BUFFER_SIZE];    /* Temporary buffer */
+} idetape_packet_command_t;
+
+/*
+ *     Capabilities and Mechanical Status Page
+ */
+
+typedef struct {
+       unsigned page_code      :6;     /* Page code - Should be 0x2a */
+       unsigned reserved1_67   :2;
+       byte page_length;               /* Page Length - Should be 0x12 */
+       byte reserved2; 
+       byte reserved3; 
+       unsigned ro             :1;     /* Read Only Mode */
+       unsigned reserved4_1234 :4;
+       unsigned sprev          :1;     /* Supports SPACE in the reverse direction */
+       unsigned reserved4_67   :2;
+       unsigned reserved5_012  :3;
+       unsigned efmt           :1;     /* Supports ERASE command initiated formatting */
+       unsigned reserved5_4    :1;
+       unsigned qfa            :1;     /* Supports the QFA two partition formats */
+       unsigned reserved5_67   :2;
+       unsigned lock           :1;     /* Supports locking the volume */
+       unsigned locked         :1;     /* The volume is locked */
+       unsigned prevent        :1;     /* The device defaults in the prevent state after power up */   
+       unsigned eject          :1;     /* The device can eject the volume */
+       unsigned reserved6_45   :2;     /* Reserved */  
+       unsigned ecc            :1;     /* Supports error correction */
+       unsigned cmprs          :1;     /* Supports data compression */
+       unsigned reserved7_0    :1;
+       unsigned blk512         :1;     /* Supports 512 bytes block size */
+       unsigned blk1024        :1;     /* Supports 1024 bytes block size */
+       unsigned reserved7_3_6  :4;
+       unsigned slowb          :1;     /* The device restricts the byte count for PIO */
+                                       /* transfers for slow buffer memory ??? */
+       unsigned short max_speed;       /* Maximum speed supported in KBps */
+       byte reserved10;
+       byte reserved11;
+       unsigned short ctl;             /* Continuous Transfer Limit in blocks */
+       unsigned short speed;           /* Current Speed, in KBps */
+       unsigned short buffer_size;     /* Buffer Size, in 512 bytes */
+       byte reserved18;
+       byte reserved19;
+} idetape_capabilities_page_t;
+
+/*
+ *     Most of our global data which we need to save even as we leave the
+ *     driver due to an interrupt or a timer event is stored in a variable
+ *     of type tape_info, defined below.
+ *
+ *     Additional global variables which provide the link between the
+ *     character device interface to this structure are defined in
+ *     ide-tape.c
+ */
+typedef struct {       
+
+       /*
+        *      Since a typical character device operation requires more
+        *      than one packet command, we provide here enough memory
+        *      for the maximum of interconnected packet commands.
+        *      The packet commands are stored in the circular array pc_stack.
+        *      pc_stack_index points to the last used entry, and warps around
+        *      to the start when we get to the last array entry.
+        *
+        *      pc points to the current processed packet command.
+        *
+        *      failed_pc points to the last failed packet command, or contains
+        *      NULL if we do not need to retry any packet command. This is
+        *      required since an additional packet command is needed before the
+        *      retry, to get detailed information on what went wrong.
+        */
+
+       idetape_packet_command_t *pc;           /* Current packet command */
+       idetape_packet_command_t *failed_pc;    /* Last failed packet command */
+       idetape_packet_command_t pc_stack [IDETAPE_PC_STACK]; /* Packet command stack */
+       byte pc_stack_index;                    /* Next free packet command storage space */
+
+       /* 
+        *      The Linux ide driver basically traverses the request lists
+        *      of the ide block devices, finds the next request, completes
+        *      it, and passes to the next one. This is done in ide_do_request.
+        *
+        *      In this regard, ide-tape.c is fully compatible with the rest of
+        *      the ide driver - From the point of view of ide.c, we are just
+        *      another ide block device which receives requests and completes
+        *      them.
+        *
+        *      However, our requests usually don't originate in the buffer
+        *      cache but rather in ide-tape.c itself. Here we provide safe
+        *      storage for such requests.
+        */
+
+       struct request rq_stack [IDETAPE_PC_STACK];
+       byte rq_stack_index;                    /* We implement a circular array */
+
+       /*
+        *      While polling for DSC we use postponed_rq to postpone the
+        *      current request so that ide.c will be able to service
+        *      pending requests on the other device.
+        */
+
+       struct request *postponed_rq;
+       byte dsc_count;         
+       unsigned long dsc_polling_start;
+       struct timer_list dsc_timer;            /* Timer used to poll for dsc */
+       unsigned long dsc_polling_frequency;
+       unsigned long dsc_timeout;              /* Maximum waiting time */
+       byte dsc_received;
+
+       /* Position information */
+       
+       byte partition_num;                     /* Currently not used */
+       unsigned long block_address;            /* Current block */
+       byte block_address_valid;               /* 0 When the tape position is unknown */
+                                               /* (To the tape or to us) */
+       unsigned long locate_to;                /* We want to reach this block, as a part */
+                                               /* of handling the current request */
+       byte locate_retries;                    /* Each time, increase locate_retries */
+
+       unsigned long last_written_block;       /* Once writing started, we don't allow read */
+       byte last_written_valid;                /* access beyond the last written block */
+                                               /* ??? Should I remove this ? */
+
+       /* Last error information */
+       
+       byte sense_key,asc,ascq;
+
+       /* Character device operation */
+
+       unsigned char last_dt_was_write;        /* Last character device data transfer was a write */
+       byte busy;                              /* Device already opened */     
+
+       /* Device information */
+       
+       unsigned short tape_block_size;
+       idetape_capabilities_page_t capabilities;       /* Capabilities and Mechanical Page */
+
+       /* Data buffer */
+       
+       char *data_buffer;
+
+} idetape_tape_t;
+
+#endif /* IDETAPE_H */
index 811d4bf2dbac8f4486a09dfc5e496fd3568277f7..3e89315adafe99bece863b50fbf8351cfae5ed57 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c  Version 5.18  Nov 16, 1995
+ *  linux/drivers/block/ide.c  Version 5.22  Dec 10, 1995
  *
  *  Copyright (C) 1994, 1995  Linus Torvalds & authors (see below)
  */
  *  Version 5.18       new CMD640 code, moved to cmd640.c, #include'd for now
  *                     new UMC8672 code, moved to umc8672.c, #include'd for now
  *                     disallow turning on DMA when h/w not capable of DMA
+ *  Version 5.19       fix potential infinite timeout on resets
+ *                     extend reset poll into a general purpose polling scheme
+ *                     add atapi tape drive support from Gadi Oxman
+ *                     simplify exit from _intr routines -- no IDE_DO_REQUEST
+ *  Version 5.20       leave current rq on blkdev request list during I/O
+ *                     generalized ide_do_drive_cmd() for tape/cdrom driver use
+ *  Version 5.21       fix nasty cdrom/tape bug (ide_preempt was messed up)
+ *  Version 5.22       fix ide_xlate_1024() to work with/without drive->id
  *
  *  Driver compile-time options are in ide.h
  *
  *     - add ioctls to get/set interface timings on various interfaces
  *     - add Promise Caching controller support from peterd@pnd-pc.demon.co.uk
  *     - modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
- *     - find someone to work on IDE *tape drive* support
  */
 
 #undef REALLY_SLOW_IO          /* most systems can safely undef this */
@@ -246,10 +253,16 @@ static unsigned long read_timer(void)
        return (t - i);
 }
 
-void ide_set_recovery_timer (ide_hwif_t *hwif)
+static void set_recovery_timer (ide_hwif_t *hwif)
 {
        hwif->last_time = read_timer();
 }
+#define SET_RECOVERY_TIMER(drive) set_recovery_timer (drive)
+
+#else
+
+#define SET_RECOVERY_TIMER(drive)
+
 #endif /* DISK_RECOVERY_TIME */
 
 /*
@@ -391,6 +404,10 @@ void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
  * Apparently, systems with multiple CMD640 chips may need something similar..
  *
  * This algorithm courtesy of malafoss@snakemail.hut.fi
+ *
+ * At least one user has reported that this code can confuse the floppy
+ * controller and/or driver -- perhaps this should be changed to use
+ * a read-modify-write sequence, so as not to disturb other bits in the reg?
  */
 
 void ide_hwif_select (ide_hwif_t *hwif)
@@ -419,15 +436,17 @@ void ide_hwif_select (ide_hwif_t *hwif)
  * timer is started to prevent us from waiting forever in case
  * something goes wrong (see the timer_expiry() handler later on).
  */
-void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler)
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout)
 {
        ide_hwgroup_t *hwgroup = HWGROUP(drive);
 #ifdef DEBUG
-       if (hwgroup->handler != NULL)
-               printk("%s: ide_set_handler: old handler not null\n", drive->name);
+       if (hwgroup->handler != NULL) {
+               printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
+                       drive->name, hwgroup->handler, handler);
+       }
 #endif
        hwgroup->handler       = handler;
-       hwgroup->timer.expires = jiffies + WAIT_CMD;
+       hwgroup->timer.expires = jiffies + timeout;
        add_timer(&(hwgroup->timer));
 }
 
@@ -468,8 +487,8 @@ static unsigned long current_capacity (ide_drive_t  *drive)
 
        if (!drive->present)
                return 0;
-       if (drive->media != disk)
-               return 0x1fffff;        /* cdrom */
+       if (drive->media != ide_disk)
+               return 0x7fffffff;      /* cdrom or tape */
        /* Determine capacity, and use LBA if the drive properly supports it */
        if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
                drive->select.b.lba = 1;
@@ -493,11 +512,15 @@ static void ide_geninit (struct gendisk *gd)
        for (unit = 0; unit < gd->nr_real; ++unit) {
                ide_drive_t *drive = &hwif->drives[unit];
 #ifdef CONFIG_BLK_DEV_IDECD
-               if (drive->present && drive->media == cdrom)
+               if (drive->present && drive->media == ide_cdrom)
                        ide_cdrom_setup(drive);
 #endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+               if (drive->present && drive->media == ide_tape)
+                       idetape_setup(drive);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
                drive->part[0].nr_sects = current_capacity(drive);
-               if (!drive->present || drive->media != disk) {
+               if (!drive->present || drive->media != ide_disk) {
                        drive->part[0].start_sect = -1; /* skip partition check */
                }
        }
@@ -554,95 +577,54 @@ static void init_gendisk (ide_hwif_t *hwif)
        hwif->gd = gendisk_head = gd;
 }
 
-static void unexpected_intr (int, ide_hwgroup_t *);
-/*
- * reset_ihandler() is a dummy interrupt handler which we install during
- * an ide interface reset operation.  This prevents other devices in the
- * same hwgroup from being serviced while we play around with shared resources.
- * If it ever gets invoked, we call unexpected_intr(), which treats the event
- * the same as a timer_expiry().
- */
-static void reset_ihandler (ide_drive_t *drive)
-{
-       unsigned long flags;
-
-       save_flags(flags);
-       cli();
-       unexpected_intr (HWIF(drive)->irq, HWGROUP(drive));
-       restore_flags(flags);
-}
-
-/*
- * start_reset_timer() sets up a timer event for 50ms in the future,
- * to poll for completion of an ide reset operation (no interrupt to help us).
- */
-static void start_reset_timer (ide_hwif_t *hwif)
-{
-       ide_hwgroup_t *hwgroup = hwif->hwgroup;
+static void do_reset1 (ide_drive_t *, int);            /* needed below */
 
-       hwgroup->reset_timeout = jiffies + WAIT_WORSTCASE; /* max waiting time */
-       hwgroup->handler = &reset_ihandler;             /* dummy irq handler */
-       hwgroup->timer.expires = jiffies + (HZ/20);     /* polling interval */
-       add_timer(&(hwgroup->timer));
-}
-
-#ifdef CONFIG_BLK_DEV_IDECD
+#ifdef CONFIG_BLK_DEV_IDEATAPI
 /*
- * atapi_reset_handler() gets invoked to poll the interface for completion every 50ms
+ * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
  * during an atapi drive reset operation. If the drive has not yet responded,
  * and we have not yet hit our maximum waiting time, then the timer is restarted
  * for another 50ms.
- *
- * Returns 1 if waiting for another 50ms, returns 0 otherwise.
  */
-static int atapi_reset_handler (ide_hwgroup_t *hwgroup)
+static void atapi_reset_pollfunc (ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = hwgroup->hwif;
-       ide_drive_t *drive = hwgroup->drive;
+       ide_hwgroup_t *hwgroup = HWGROUP(drive);
        byte stat;
 
        OUT_BYTE (drive->select.all, IDE_SELECT_REG);
        udelay (10);
 
-       if (!OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
-               if (jiffies < hwgroup->reset_timeout) {
-                       start_reset_timer (hwif);
-                       return 1;
+       if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
+               printk("%s: ATAPI reset complete\n", drive->name);
+       } else {
+               if (jiffies < hwgroup->poll_timeout) {
+                       ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
+                       return; /* continue polling */
                }
+               hwgroup->poll_timeout = 0;      /* end of polling */
                printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat);
-               return ide_do_reset (drive);    /* do it the old fashioned way */
+               do_reset1 (drive, 1);   /* do it the old fashioned way */
        }
-       hwgroup->doing_atapi_reset = 0;
-       hwgroup->handler = NULL;        /* allow new requests to be processed */
-       hwgroup->reset_timeout = 0;     /* signal end of ide reset operation */
-       printk("%s: ATAPI reset complete\n", drive->name);
-       return 0;
+       hwgroup->poll_timeout = 0;      /* done polling */
 }
-#endif /* CONFIG_BLK_DEV_IDECD */
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
 
 /*
- * reset_handler() gets invoked to poll the interface for completion every 50ms
+ * reset_pollfunc() gets invoked to poll the interface for completion every 50ms
  * during an ide reset operation. If the drives have not yet responded,
  * and we have not yet hit our maximum waiting time, then the timer is restarted
  * for another 50ms.
- *
- * Returns 1 if waiting for another 50ms, returns 0 otherwise.
  */
-static int reset_handler (ide_hwgroup_t *hwgroup)
+static void reset_pollfunc (ide_drive_t *drive)
 {
-       ide_hwif_t *hwif = hwgroup->hwif;
-       ide_drive_t *drive = hwgroup->drive;
+       ide_hwgroup_t *hwgroup = HWGROUP(drive);
+       ide_hwif_t *hwif = HWIF(drive);
        byte tmp;
 
-#ifdef CONFIG_BLK_DEV_IDECD
-       if (hwgroup->doing_atapi_reset)
-               return atapi_reset_handler(hwgroup);
-#endif /* CONFIG_BLK_DEV_IDECD */
-
        if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
-               if (jiffies < hwgroup->reset_timeout) {
-                       start_reset_timer (hwif);
-                       return 1;
+               if (jiffies < hwgroup->poll_timeout) {
+                       ide_set_handler (drive, &reset_pollfunc, HZ/20);
+                       return; /* continue polling */
                }
                printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
        } else  {
@@ -669,13 +651,11 @@ static int reset_handler (ide_hwgroup_t *hwgroup)
                        printk("\n");
                }
        }
-       hwgroup->handler = NULL;        /* allow new requests to be processed */
-       hwgroup->reset_timeout = 0;     /* signal end of ide reset operation */
-       return 0;
+       hwgroup->poll_timeout = 0;      /* done polling */
 }
 
 /*
- * ide_do_reset() attempts to recover a confused drive by resetting it.
+ * do_reset1() attempts to recover a confused drive by resetting it.
  * Unfortunately, resetting a disk drive actually resets all devices on
  * the same interface, so it can really be thought of as resetting the
  * interface rather than resetting the drive.
@@ -689,7 +669,7 @@ static int reset_handler (ide_hwgroup_t *hwgroup)
  * (up to 30 seconds worstcase).  So, instead of busy-waiting here for it,
  * we set a timer to poll at 50ms intervals.
  */
-int ide_do_reset (ide_drive_t *drive)
+static void do_reset1 (ide_drive_t *drive, int  do_not_try_atapi)
 {
        unsigned int unit;
        unsigned long flags;
@@ -699,24 +679,22 @@ int ide_do_reset (ide_drive_t *drive)
        save_flags(flags);
        cli();          /* Why ? */
 
-#ifdef CONFIG_BLK_DEV_IDECD
+#ifdef CONFIG_BLK_DEV_IDEATAPI
        /* For an ATAPI device, first try an ATAPI SRST. */
-       if (drive->media == cdrom) {
-               if (!hwgroup->doing_atapi_reset) {
-                       hwgroup->doing_atapi_reset = 1;
+       if (drive->media != ide_disk) {
+               if (!do_not_try_atapi) {
                        if (!drive->keep_settings)
                                drive->unmask = 0;
                        OUT_BYTE (drive->select.all, IDE_SELECT_REG);
                        udelay (20);
                        OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
-                       hwgroup->reset_timeout = jiffies + WAIT_WORSTCASE;
-                       start_reset_timer (hwif); /* begin periodic polling */
+                       hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+                       ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
                        restore_flags (flags);
-                       return 1;
+                       return;
                }
        }
-       hwgroup->doing_atapi_reset = 0;
-#endif /* CONFIG_BLK_DEV_IDECD */
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
 
        /*
         * First, reset any device state data we were maintaining
@@ -749,33 +727,45 @@ int ide_do_reset (ide_drive_t *drive)
        OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG); /* set SRST and nIEN */
        udelay(5);                      /* more than enough time */
        OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */
-       hwgroup->reset_timeout = jiffies + WAIT_WORSTCASE;
-       start_reset_timer (hwif);       /* begin periodic polling */
+       hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+       ide_set_handler (drive, &reset_pollfunc, HZ/20);
 #endif /* OK_TO_RESET_CONTROLLER */
 
        restore_flags (flags);
-       return OK_TO_RESET_CONTROLLER;  /* 1 = we are waiting, 0 = done */
 }
 
 /*
- * Clean up after success/failure of an explicit (ioctl) drive cmd
+ * ide_do_reset() is the entry point to the drive/interface reset code.
+ */
+void ide_do_reset (ide_drive_t *drive)
+{
+       do_reset1 (drive, 0);
+}
+
+/*
+ * Clean up after success/failure of an explicit drive cmd
  */
-static void end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
 {
        unsigned long flags;
        struct request *rq = HWGROUP(drive)->rq;
-       byte *args = (byte *) rq->buffer;
 
-       rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
-       if (args) {
-               args[0] = stat;
-               args[1] = err;
-               args[2] = IN_BYTE(IDE_NSECTOR_REG);
+       if (rq->cmd == IDE_DRIVE_CMD) {
+               byte *args = (byte *) rq->buffer;
+               rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
+               if (args) {
+                       args[0] = stat;
+                       args[1] = err;
+                       args[2] = IN_BYTE(IDE_NSECTOR_REG);
+               }
        }
        save_flags(flags);
        cli();
-       up(rq->sem);
+       blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
        HWGROUP(drive)->rq = NULL;
+       rq->rq_status = RQ_INACTIVE;
+       if (rq->sem != NULL)
+               up(rq->sem);
        restore_flags(flags);
 }
 
@@ -791,7 +781,7 @@ byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
        sti();
        printk("%s: %s: status=0x%02x", drive->name, msg, stat);
 #if FANCY_STATUS_DUMPS
-       if (drive->media == disk) {
+       if (drive->media == ide_disk) {
                printk(" { ");
                if (stat & BUSY_STAT)
                        printk("Busy ");
@@ -812,7 +802,7 @@ byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
                err = GET_ERR();
                printk("%s: %s: error=0x%02x", drive->name, msg, err);
 #if FANCY_STATUS_DUMPS
-               if (drive->media == disk) {
+               if (drive->media == ide_disk) {
                        printk(" { ");
                        if (err & BBD_ERR)      printk("BadSector ");
                        if (err & ECC_ERR)      printk("UncorrectableError ");
@@ -868,28 +858,25 @@ static void try_to_flush_leftover_data (ide_drive_t *drive)
 
 /*
  * ide_error() takes action based on the error returned by the controller.
- *
- * Returns 1 if an ide reset operation has been initiated, in which case
- * the caller MUST simply return from the driver (through however many levels).
- * Returns 0 otherwise.
  */
-int ide_error (ide_drive_t *drive, const char *msg, byte stat)
+void ide_error (ide_drive_t *drive, const char *msg, byte stat)
 {
        struct request *rq;
        byte err;
 
        err = ide_dump_status(drive, msg, stat);
        if ((rq = HWGROUP(drive)->rq) == NULL || drive == NULL)
-               return 0;
-       if (rq->cmd == IDE_DRIVE_CMD) { /* never retry an explicit DRIVE_CMD */
-               end_drive_cmd(drive, stat, err);
-               return 0;
+               return;
+       if (rq->cmd != READ && rq->cmd != WRITE) { /* retry only "normal" i/o */
+               rq->errors = 1;
+               ide_end_drive_cmd(drive, stat, err);
+               return;
        }
        if (stat & BUSY_STAT) {         /* other bits are useless when BUSY */
                rq->errors |= ERROR_RESET;
        } else {
-               if (drive->media == disk && (stat & ERR_STAT)) {
-                       /* err has different meaning on cdrom */
+               if (drive->media == ide_disk && (stat & ERR_STAT)) {
+                       /* err has different meaning on cdrom and tape */
                        if (err & BBD_ERR)              /* retries won't help this! */
                                rq->errors = ERROR_MAX;
                        else if (err & TRK0_ERR)        /* help it find track zero */
@@ -906,7 +893,7 @@ int ide_error (ide_drive_t *drive, const char *msg, byte stat)
                drive->using_dma = 0;
                printk("%s: DMA disabled\n", drive->name);
                --rq->errors;
-               return 0;
+               return;
        }
 #endif /* CONFIG_BLK_DEV_TRITON */
        if (rq->errors >= ERROR_MAX)
@@ -914,12 +901,12 @@ int ide_error (ide_drive_t *drive, const char *msg, byte stat)
        else {
                if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
                        ++rq->errors;
-                       return ide_do_reset(drive);
+                       ide_do_reset(drive);
+                       return;
                } else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
                        drive->special.b.recalibrate = 1;
                ++rq->errors;
        }
-       return 0;
 }
 
 /*
@@ -933,9 +920,7 @@ static void read_intr (ide_drive_t *drive)
        struct request *rq;
 
        if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
-               sti();
-               if (!ide_error(drive, "read_intr", stat))
-                       IDE_DO_REQUEST;
+               ide_error(drive, "read_intr", stat);
                return;
        }
        msect = drive->mult_count;
@@ -962,10 +947,8 @@ read_next:
        if (i > 0) {
                if (msect)
                        goto read_next;
-               ide_set_handler (drive, &read_intr);
-               return;
+               ide_set_handler (drive, &read_intr, WAIT_CMD);
        }
-       IDE_DO_REQUEST;
 }
 
 /*
@@ -975,7 +958,8 @@ static void write_intr (ide_drive_t *drive)
 {
        byte stat;
        int i;
-       struct request *rq = HWGROUP(drive)->rq;
+       ide_hwgroup_t *hwgroup = HWGROUP(drive);
+       struct request *rq = hwgroup->rq;
 
        if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
 #ifdef DEBUG
@@ -990,19 +974,15 @@ static void write_intr (ide_drive_t *drive)
                        i = --rq->nr_sectors;
                        --rq->current_nr_sectors;
                        if (rq->current_nr_sectors <= 0)
-                               ide_end_request(1, HWGROUP(drive));
+                               ide_end_request(1, hwgroup);
                        if (i > 0) {
                                ide_output_data (drive, rq->buffer, SECTOR_WORDS);
-                               ide_set_handler (drive, &write_intr);
-                               return;
+                               ide_set_handler (drive, &write_intr, WAIT_CMD);
                        }
-                       IDE_DO_REQUEST;
                        return;
                }
        }
-       sti();
-       if (!ide_error(drive, "write_intr", stat))
-               IDE_DO_REQUEST;
+       ide_error(drive, "write_intr", stat);
 }
 
 /*
@@ -1049,30 +1029,28 @@ static void multwrite_intr (ide_drive_t *drive)
 {
        byte stat;
        int i;
-       struct request *rq = &HWGROUP(drive)->wrq;
+       ide_hwgroup_t *hwgroup = HWGROUP(drive);
+       struct request *rq = &hwgroup->wrq;
 
        if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
                if (stat & DRQ_STAT) {
                        if (rq->nr_sectors) {
                                multwrite(drive);
-                               ide_set_handler (drive, &multwrite_intr);
+                               ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
                                return;
                        }
                } else {
                        if (!rq->nr_sectors) {  /* all done? */
-                               rq = HWGROUP(drive)->rq;
+                               rq = hwgroup->rq;
                                for (i = rq->nr_sectors; i > 0;){
                                        i -= rq->current_nr_sectors;
-                                       ide_end_request(1, HWGROUP(drive));
+                                       ide_end_request(1, hwgroup);
                                }
-                               IDE_DO_REQUEST;
                                return;
                        }
                }
        }
-       sti();
-       if (!ide_error(drive, "multwrite_intr", stat))
-               IDE_DO_REQUEST;
+       ide_error(drive, "multwrite_intr", stat);
 }
 
 /*
@@ -1081,7 +1059,7 @@ static void multwrite_intr (ide_drive_t *drive)
  */
 static void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
 {
-       ide_set_handler (drive, handler);
+       ide_set_handler (drive, handler, WAIT_CMD);
        OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
        OUT_BYTE(nsect,IDE_NSECTOR_REG);
        OUT_BYTE(cmd,IDE_COMMAND_REG);
@@ -1102,7 +1080,6 @@ static void set_multmode_intr (ide_drive_t *drive)
                drive->special.b.recalibrate = 1;
                (void) ide_dump_status(drive, "set_multmode", stat);
        }
-       IDE_DO_REQUEST;
 }
 
 /*
@@ -1114,9 +1091,7 @@ static void set_geometry_intr (ide_drive_t *drive)
 
        sti();
        if (!OK_STAT(stat,READY_STAT,BAD_STAT))
-               if (ide_error(drive, "set_geometry_intr", stat))
-                       return;
-       IDE_DO_REQUEST;
+               ide_error(drive, "set_geometry_intr", stat);
 }
 
 /*
@@ -1128,9 +1103,7 @@ static void recal_intr (ide_drive_t *drive)
 
        sti();
        if (!OK_STAT(stat,READY_STAT,BAD_STAT))
-               if (ide_error(drive, "recal_intr", stat))
-                       return;
-       IDE_DO_REQUEST;
+               ide_error(drive, "recal_intr", stat);
 }
 
 /*
@@ -1142,10 +1115,9 @@ static void drive_cmd_intr (ide_drive_t *drive)
 
        sti();
        if (OK_STAT(stat,READY_STAT,BAD_STAT))
-               end_drive_cmd (drive, stat, GET_ERR());
-       else if (ide_error(drive, "drive_cmd", stat)) /* calls end_drive_cmd */
-               return;
-       IDE_DO_REQUEST;
+               ide_end_drive_cmd (drive, stat, GET_ERR());
+       else
+               ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
 }
 
 /*
@@ -1161,7 +1133,7 @@ static inline void do_special (ide_drive_t *drive)
 #endif
        if (s->b.set_geometry) {
                s->b.set_geometry = 0;
-               if (drive->media == disk) {
+               if (drive->media == ide_disk) {
                        OUT_BYTE(drive->sect,IDE_SECTOR_REG);
                        OUT_BYTE(drive->cyl,IDE_LCYL_REG);
                        OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
@@ -1170,12 +1142,12 @@ static inline void do_special (ide_drive_t *drive)
                }
        } else if (s->b.recalibrate) {
                s->b.recalibrate = 0;
-               if (drive->media == disk) {
+               if (drive->media == ide_disk) {
                        ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
                }
        } else if (s->b.set_multmode) {
                s->b.set_multmode = 0;
-               if (drive->media == disk) {
+               if (drive->media == ide_disk) {
                        if (drive->id && drive->mult_req > drive->id->max_multsect)
                                drive->mult_req = drive->id->max_multsect;
                        ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
@@ -1191,23 +1163,24 @@ static inline void do_special (ide_drive_t *drive)
  * This routine busy-waits for the drive status to be not "busy".
  * It then checks the status for all of the "good" bits and none
  * of the "bad" bits, and if all is okay it returns 0.  All other
- * cases return 1 after invoking ide_error()
+ * cases return 1 after invoking ide_error() -- caller should just return.
  *
  * This routine should get fixed to not hog the cpu during extra long waits..
  * That could be done by busy-waiting for the first jiffy or two, and then
  * setting a timer to wake up at half second intervals thereafter,
- * until WAIT_WORSTCASE is achieved, before timing out.
+ * until timeout is achieved, before timing out.
  */
 int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout)
 {
        byte stat;
        unsigned long flags;
+
 test:
        udelay(1);      /* spec allows drive 400ns to change "BUSY" */
        if (OK_STAT((stat = GET_STAT()), good, bad))
                return 0;       /* fast exit for most frequent case */
        if (!(stat & BUSY_STAT)) {
-               (void) ide_error(drive, "status error", stat);
+               ide_error(drive, "status error", stat);
                return 1;
        }
 
@@ -1222,7 +1195,7 @@ test:
        } while (jiffies <= timeout);
 
        restore_flags(flags);
-       (void) ide_error(drive, "status timeout", GET_STAT());
+       ide_error(drive, "status timeout", GET_STAT());
        return 1;
 }
 
@@ -1268,7 +1241,7 @@ static inline void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned
                if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive)))
                        return;
 #endif /* CONFIG_BLK_DEV_TRITON */
-               ide_set_handler(drive, &read_intr);
+               ide_set_handler(drive, &read_intr, WAIT_CMD);
                OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, io_base+IDE_COMMAND_OFFSET);
                return;
        }
@@ -1287,10 +1260,10 @@ static inline void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned
                        cli();
                if (drive->mult_count) {
                        HWGROUP(drive)->wrq = *rq; /* scratchpad */
-                       ide_set_handler (drive, &multwrite_intr);
+                       ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
                        multwrite(drive);
                } else {
-                       ide_set_handler (drive, &write_intr);
+                       ide_set_handler (drive, &write_intr, WAIT_CMD);
                        ide_output_data(drive, rq->buffer, SECTOR_WORDS);
                }
                return;
@@ -1311,7 +1284,7 @@ static inline void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned
 #ifdef DEBUG
                        printk("%s: DRIVE_CMD (null)\n", drive->name);
 #endif
-                       end_drive_cmd(drive, GET_STAT(), GET_ERR());
+                       ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
                        return;
                }
        }
@@ -1330,7 +1303,7 @@ static inline void do_request (ide_hwif_t *hwif, struct request *rq)
 
        sti();
 #ifdef DEBUG
-       printk("%s: ide_do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
+       printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
 #endif
        minor = MINOR(rq->rq_dev);
        unit = minor >> PARTN_BITS;
@@ -1372,17 +1345,25 @@ static inline void do_request (ide_hwif_t *hwif, struct request *rq)
        if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
                printk("%s: drive not ready for command\n", drive->name);
                return;
-
        }
        if (!drive->special.all) {
-#ifdef CONFIG_BLK_DEV_IDECD
+#ifdef CONFIG_BLK_DEV_IDEATAPI
                switch (drive->media) {
-                       case disk:
+                       case ide_disk:
                                do_rw_disk (drive, rq, block);
                                return;
-                       case cdrom:
+#ifdef CONFIG_BLK_DEV_IDECD
+                       case ide_cdrom:
                                ide_do_rw_cdrom (drive, block);
                                return;
+#endif /* CONFIG_BLK_DEV_IDECD */
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+                       case ide_tape:
+                               idetape_do_request (drive, rq, block);
+                               return;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
                        default:
                                printk("%s: media type %d not supported\n",
                                        drive->name, drive->media);
@@ -1391,7 +1372,7 @@ static inline void do_request (ide_hwif_t *hwif, struct request *rq)
 #else
                do_rw_disk (drive, rq, block); /* simpler and faster */
                return;
-#endif;
+#endif /* CONFIG_BLK_DEV_IDEATAPI */;
        }
        do_special(drive);
        return;
@@ -1425,16 +1406,14 @@ void ide_do_request (ide_hwgroup_t *hwgroup)
                ide_hwif_t *hwif = hwgroup->hwif;
                struct request *rq;
                if ((rq = hwgroup->rq) == NULL) {
-                       hwgroup->drive = NULL;  /* paranoia */
                        do {
                                rq = blk_dev[hwif->major].current_request;
                                if (rq != NULL && rq->rq_status != RQ_INACTIVE)
                                        goto got_rq;
                        } while ((hwif = hwif->next) != hwgroup->hwif);
                        return;         /* no work left for this hwgroup */
-               got_rq:
-                       blk_dev[hwif->major].current_request = rq->next;
                }
+       got_rq: 
                do_request(hwgroup->hwif = hwif, hwgroup->rq = rq);
                cli();
        } while (hwgroup->handler == NULL);
@@ -1495,19 +1474,21 @@ static void timer_expiry (unsigned long data)
        save_flags(flags);
        cli();
 
-       if (hwgroup->reset_timeout != 0) { /* ide reset in progress? */
-               if (!reset_handler(hwgroup))
-                       do_hwgroup_request (hwgroup);
+       if (hwgroup->poll_timeout != 0) { /* polling in progress? */
+               ide_handler_t *handler = hwgroup->handler;
+               hwgroup->handler = NULL;
+               handler(drive);
        } else if (hwgroup->handler == NULL) {   /* not waiting for anything? */
                sti(); /* drive must have responded just as the timer expired */
                printk("%s: marginal timeout\n", drive->name);
-       } else {                                 /* drive not responding */
-               hwgroup->handler = NULL;
+       } else {
+               hwgroup->handler = NULL;        /* abort the operation */
                if (hwgroup->hwif->dmaproc)
                        (void) hwgroup->hwif->dmaproc (ide_dma_abort, drive);
-               if (!ide_error(drive, "irq timeout", GET_STAT()))
-                       do_hwgroup_request (hwgroup);
+               ide_error(drive, "irq timeout", GET_STAT());
        }
+       if (hwgroup->handler == NULL)
+               do_hwgroup_request (hwgroup);
        restore_flags(flags);
 }
 
@@ -1540,15 +1521,6 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
        unsigned int unit;
        ide_hwif_t *hwif = hwgroup->hwif;
 
-       /*
-        * check for ide reset in progress
-        */
-       if (hwgroup->reset_timeout != 0) {
-               if (!reset_handler(hwgroup))
-                       do_hwgroup_request (hwgroup);
-               return;
-       }
-
        /*
         * handle the unexpected interrupt
         */
@@ -1586,6 +1558,11 @@ static void ide_intr (int irq, struct pt_regs *regs)
                if (drive->unmask)
                        sti();
                handler(drive);
+               cli();  /* this is necessary, as next rq may be different irq */
+               if (hwgroup->handler == NULL) {
+                       SET_RECOVERY_TIMER(HWIF(drive));
+                       ide_do_request(hwgroup);
+               }
        } else {
                unexpected_intr(irq, hwgroup);
        }
@@ -1621,49 +1598,83 @@ static ide_drive_t *get_info_ptr (kdev_t i_rdev)
 }
 
 /*
- * This function issues a specific IDE drive command onto the
- * tail of the request queue, and waits for it to be completed.
- * If arg is NULL, it goes through all the motions,
- * but without actually sending a command to the drive.
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
  */
-int ide_do_drive_cmd(kdev_t rdev, char *args)
+void ide_init_drive_cmd (struct request *rq)
+{
+       rq->buffer = NULL;
+       rq->cmd = IDE_DRIVE_CMD;
+       rq->sector = 0;
+       rq->nr_sectors = 0;
+       rq->current_nr_sectors = 0;
+       rq->sem = NULL;
+       rq->bh = NULL;
+       rq->bhtail = NULL;
+       rq->next = NULL;
+
+#if 0  /* these are done each time through ide_do_drive_cmd() */
+       rq->errors = 0;
+       rq->rq_status = RQ_ACTIVE;
+       rq->rq_dev = ????;
+#endif
+}
+
+/*
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then then rq is queued at the end of
+ * the request queue, and the function sleeps until it has been
+ * processed.  This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed.  This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed.  As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the
+ * ATAPI tape/cdrom driver code.
+ */
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action)
 {
        unsigned long flags;
-       unsigned int major = MAJOR(rdev);
-       struct request rq, *cur_rq;
-       struct blk_dev_struct *bdev;
+       unsigned int major = HWIF(drive)->major;
+       struct request *cur_rq;
+       struct blk_dev_struct *bdev = &blk_dev[major];
        struct semaphore sem = MUTEX_LOCKED;
 
-       /* build up a special request, and add it to the queue */
-       rq.buffer = args;
-       rq.cmd = IDE_DRIVE_CMD;
-       rq.errors = 0;
-       rq.sector = 0;
-       rq.nr_sectors = 0;
-       rq.current_nr_sectors = 0;
-       rq.sem = &sem;
-       rq.bh = NULL;
-       rq.bhtail = NULL;
-       rq.next = NULL;
-       rq.rq_status = RQ_ACTIVE;
-       rq.rq_dev = rdev;
-       bdev = &blk_dev[major];
+       rq->errors = 0;
+       rq->rq_status = RQ_ACTIVE;
+       rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
+       if (action == ide_wait)
+               rq->sem = &sem;
 
        save_flags(flags);
        cli();
        cur_rq = bdev->current_request;
-       if (cur_rq == NULL) {                   /* empty request list? */
-               bdev->current_request = &rq;    /* service ours immediately */
-               bdev->request_fn();
+
+       if (cur_rq == NULL || action == ide_preempt) {
+               rq->next = cur_rq;
+               bdev->current_request = rq;
+               HWGROUP(drive)->rq = NULL;
+               if (action != ide_preempt)
+                       bdev->request_fn();
        } else {
-               while (cur_rq->next != NULL)    /* find end of request list */
-                       cur_rq = cur_rq->next;
-               cur_rq->next = &rq;             /* add rq to the end */
+               if (action == ide_wait) {
+                       while (cur_rq->next != NULL)    /* find end of list */
+                               cur_rq = cur_rq->next;
+               }
+               rq->next = cur_rq->next;
+               cur_rq->next = rq;
        }
-
-       down(&sem);                             /* wait for it to be serviced */
+       if (action == ide_wait)
+               down(&sem);     /* wait for it to be serviced */
        restore_flags(flags);
-       return rq.errors ? -EIO : 0;            /* return -EIO if errors */
+       return rq->errors ? -EIO : 0;   /* return -EIO if errors */
 }
 
 static int ide_open(struct inode * inode, struct file * filp)
@@ -1680,13 +1691,20 @@ static int ide_open(struct inode * inode, struct file * filp)
        drive->usage++;
        restore_flags(flags);
 #ifdef CONFIG_BLK_DEV_IDECD
-       if (drive->media == cdrom)
+       if (drive->media == ide_cdrom)
                return ide_cdrom_open (inode, filp, drive);
 #endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+       if (drive->media == ide_tape)
+               return idetape_blkdev_open (inode, filp, drive);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
        if (drive->removeable) {
                byte door_lock[] = {WIN_DOORLOCK,0,0,0};
+               struct request rq;
                check_disk_change(inode->i_rdev);
-               ide_do_drive_cmd(inode->i_rdev, door_lock);
+               ide_init_drive_cmd (&rq);
+               rq.buffer = door_lock;
+               return ide_do_drive_cmd(drive, &rq, ide_wait);
        }
        return 0;
 }
@@ -1703,14 +1721,24 @@ static void ide_release(struct inode * inode, struct file * file)
                sync_dev(inode->i_rdev);
                drive->usage--;
 #ifdef CONFIG_BLK_DEV_IDECD
-               if (drive->media == cdrom)
+               if (drive->media == ide_cdrom) {
                        ide_cdrom_release (inode, file, drive);
-               else
+                       return;
+               }
 #endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+               if (drive->media == ide_tape) {
+                       idetape_blkdev_release (inode, file, drive);
+                       return;
+               }
+#endif /* CONFIG_BLK_DEV_IDETAPE */
                if (drive->removeable) {
                        byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
+                       struct request rq;
                        invalidate_buffers(inode->i_rdev);
-                       ide_do_drive_cmd(inode->i_rdev, door_unlock);
+                       ide_init_drive_cmd (&rq);
+                       rq.buffer = door_unlock;
+                       (void) ide_do_drive_cmd(drive, &rq, ide_wait);
                }
        }
 }
@@ -1755,7 +1783,7 @@ static int revalidate_disk(kdev_t i_rdev)
        };
 
        drive->part[0].nr_sects = current_capacity(drive);
-       if (drive->media == disk)
+       if (drive->media == ide_disk)
                resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit);
 
        drive->busy = 0;
@@ -1782,14 +1810,16 @@ static int ide_ioctl (struct inode *inode, struct file *file,
        int err;
        ide_drive_t *drive;
        unsigned long flags;
+       struct request rq;
 
+       ide_init_drive_cmd (&rq);
        if (!inode || !(inode->i_rdev))
                return -EINVAL;
        if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
                return -ENODEV;
        switch (cmd) {
                case HDIO_GETGEO:
-                       if (!loc || drive->media != disk) return -EINVAL;
+                       if (!loc || drive->media != ide_disk) return -EINVAL;
                        err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
                        if (err) return err;
                        put_user(drive->bios_head, (byte *) &loc->heads);
@@ -1848,7 +1878,7 @@ static int ide_ioctl (struct inode *inode, struct file *file,
                        return write_fs_long(arg, drive->bad_wstat == BAD_R_STAT);
 
                case HDIO_SET_DMA:
-                       if (drive->media != disk)
+                       if (drive->media != ide_disk)
                                return -EPERM;
                        if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
                                return -EPERM;
@@ -1914,7 +1944,7 @@ static int ide_ioctl (struct inode *inode, struct file *file,
                        drive->mult_req = arg;
                        drive->special.b.set_multmode = 1;
                        restore_flags(flags);
-                       ide_do_drive_cmd (inode->i_rdev, NULL);
+                       (void) ide_do_drive_cmd (drive, &rq, ide_wait);
                        return (drive->mult_count == arg) ? 0 : -EIO;
 
                case HDIO_DRIVE_CMD:
@@ -1922,13 +1952,14 @@ static int ide_ioctl (struct inode *inode, struct file *file,
                        unsigned long args;
 
                        if (NULL == (long *) arg)
-                               err = ide_do_drive_cmd(inode->i_rdev,NULL);
+                               err = ide_do_drive_cmd(drive, &rq, ide_wait);
                        else {
                                if (!(err = verify_area(VERIFY_READ,(long *)arg,sizeof(long))))
                                {
                                        args = get_user((long *)arg);
                                        if (!(err = verify_area(VERIFY_WRITE,(long *)arg,sizeof(long)))) {
-                                               err = ide_do_drive_cmd(inode->i_rdev,(char *)&args);
+                                               rq.buffer = (char *) &args;
+                                               err = ide_do_drive_cmd(drive, &rq, ide_wait);
                                                put_user(args,(long *)arg);
                                        }
                                }
@@ -1940,9 +1971,13 @@ static int ide_ioctl (struct inode *inode, struct file *file,
 
                default:
 #ifdef CONFIG_BLK_DEV_IDECD
-                       if (drive->media == cdrom)
+                       if (drive->media == ide_cdrom)
                                return ide_cdrom_ioctl(drive, inode, file, cmd, arg);
 #endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+                       if (drive->media == ide_tape)
+                               return idetape_blkdev_ioctl(drive, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
                        return -EPERM;
        }
 }
@@ -1954,7 +1989,7 @@ static int ide_check_media_change (kdev_t i_rdev)
        if ((drive = get_info_ptr(i_rdev)) == NULL)
                return -ENODEV;
 #ifdef CONFIG_BLK_DEV_IDECD
-       if (drive->media == cdrom)
+       if (drive->media == ide_cdrom)
                return ide_cdrom_check_media_change (drive);
 #endif /* CONFIG_BLK_DEV_IDECD */
        if (drive->removeable) /* for disks */
@@ -2027,22 +2062,45 @@ static inline void do_identify (ide_drive_t *drive, byte cmd)
        /*
         * Check for an ATAPI device
         */
+
        if (cmd == WIN_PIDENTIFY) {
+               byte type = (id->config >> 8) & 0x1f;
+               printk("%s: %s, ATAPI ", drive->name, id->model);
+               switch (type) {
+                       case 0:                         /* Early cdrom models used zero */
+                       case 5:
 #ifdef CONFIG_BLK_DEV_IDECD
-               byte type = (id->config >> 8) & 0x0f;
-#endif /* CONFIG_BLK_DEV_IDECD */
-               printk("%s: %s, ATAPI, ", drive->name, id->model);
-               drive->media = cdrom;
-#ifdef CONFIG_BLK_DEV_IDECD
-               if (type == 0 || type == 5)
-                       printk("CDROM drive\n");
-               else
-                       printk("UNKNOWN device\n");
-               drive->present = 1;
-               drive->removeable = 1;
+                               printk ("CDROM drive\n");
+                               drive->media = ide_cdrom;
+                               drive->present = 1;
+                               drive->removeable = 1;
+                               return;
 #else
-               printk("not supported by this kernel\n");
-#endif /* CONFIG_BLK_DEV_IDECD */
+                               printk ("CDROM ");
+                               break;
+#endif /* CONFIG_BLK_DEV_IDECD */
+                       case 1:
+#ifdef CONFIG_BLK_DEV_IDETAPE
+                               printk ("TAPE drive\n");
+                               if (idetape_identify_device (drive,id)) {
+                                       drive->media = ide_tape;
+                                       drive->present = 1;
+                                       drive->removeable = 1;
+                               }
+                               else {
+                                       drive->present = 0;
+                                       printk ("ide-tape: The tape is not supported by this version of the driver\n");
+                               }
+                               return;
+#else
+                               printk ("TAPE ");
+                               break;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+                       default:
+                               printk("Type %d - Unknown device\n", type);
+                               return;
+               }
+               printk("- not supported by this kernel\n");
                return;
        }
 
@@ -2052,7 +2110,7 @@ static inline void do_identify (ide_drive_t *drive, byte cmd)
                        drive->removeable = 1;
        }
 
-       drive->media = disk;
+       drive->media = ide_disk;
        /* Extract geometry if we did not already have one for the drive */
        if (!drive->present) {
                drive->present = 1;
@@ -2211,15 +2269,15 @@ static int try_to_identify (ide_drive_t *drive, byte cmd)
 static int do_probe (ide_drive_t *drive, byte cmd)
 {
        int rc;
-#ifdef CONFIG_BLK_DEV_IDECD
+#ifdef CONFIG_BLK_DEV_IDEATAPI
        if (drive->present) {   /* avoid waiting for inappropriate probes */
-               if ((drive->media == cdrom) && (cmd == WIN_IDENTIFY))
+               if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
                        return 4;
        }
-#endif /* CONFIG_BLK_DEV_IDECD */
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
 #ifdef DEBUG
-       printk("probing for %s: present=%d, type=%s, probetype=%s\n",
-               drive->name, drive->present, drive->media ? "cdrom":"disk",
+       printk("probing for %s: present=%d, media=%d, probetype=%s\n",
+               drive->name, drive->present, drive->media,
                (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
 #endif
 #if SUPPORT_HT6560B
@@ -2263,19 +2321,19 @@ static inline byte probe_for_drive (ide_drive_t *drive)
        if (drive->noprobe)                     /* skip probing? */
                return drive->present;
        if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
-#ifdef CONFIG_BLK_DEV_IDECD
+#ifdef CONFIG_BLK_DEV_IDEATAPI
                (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
-#endif /* CONFIG_BLK_DEV_IDECD */
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
        }
        if (!drive->present)
                return 0;                       /* drive not found */
        if (drive->id == NULL) {                /* identification failed? */
-               if (drive->media == disk) {
+               if (drive->media == ide_disk) {
                        printk ("%s: non-IDE drive, CHS=%d/%d/%d\n",
                         drive->name, drive->cyl, drive->head, drive->sect);
                }
 #ifdef CONFIG_BLK_DEV_IDECD
-               else if (drive->media == cdrom) {
+               else if (drive->media == ide_cdrom) {
                        printk("%s: ATAPI cdrom (?)\n", drive->name);
                }
 #endif /* CONFIG_BLK_DEV_IDECD */
@@ -2284,7 +2342,7 @@ static inline byte probe_for_drive (ide_drive_t *drive)
                        return 1;               /* drive was found */
                }
        }
-       if (drive->media == disk && !drive->select.b.lba) {
+       if (drive->media == ide_disk && !drive->select.b.lba) {
                if (!drive->head || drive->head > 16) {
                        printk("%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n",
                         drive->name, drive->head);
@@ -2568,14 +2626,14 @@ void ide_setup (char *s)
                                goto done;
                        case -3: /* "cdrom" */
                                drive->present = 1;
-                               drive->media = cdrom;
+                               drive->media = ide_cdrom;
                                hwif->noprobe = 0;
                                goto done;
                        case -4: /* "serialize" */
                                printk(" -- USE \"ide%c=serialize\" INSTEAD", '0'+hw);
                                goto do_serialize;
                        case 3: /* cyl,head,sect */
-                               drive->media    = disk;
+                               drive->media    = ide_disk;
                                drive->cyl      = drive->bios_cyl  = vals[0];
                                drive->head     = drive->bios_head = vals[1];
                                drive->sect     = drive->bios_sect = vals[2];
@@ -2679,12 +2737,17 @@ int ide_xlate_1024 (kdev_t i_rdev, int offset, const char *msg)
        const byte *heads = head_vals;
        unsigned long tracks;
 
-       if ((drive = get_info_ptr(i_rdev)) == NULL || drive->id == NULL)
+       if ((drive = get_info_ptr(i_rdev)) == NULL)
                return 0;
 
-       drive->cyl  = drive->bios_cyl  = drive->id->cyls;
-       drive->head = drive->bios_head = drive->id->heads;
-       drive->sect = drive->bios_sect = drive->id->sectors;
+       if (drive->id) {
+               drive->bios_cyl  = drive->id->cyls;
+               drive->bios_head = drive->id->heads;
+               drive->bios_sect = drive->id->sectors;
+       }
+       drive->cyl  = drive->bios_cyl;
+       drive->head = drive->bios_head;
+       drive->sect = drive->bios_sect;
        drive->special.b.set_geometry = 1;
 
        tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63;
@@ -2801,11 +2864,8 @@ static int init_irq (ide_hwif_t *hwif)
                hwgroup->hwif    = hwif->next = hwif;
                hwgroup->rq      = NULL;
                hwgroup->handler = NULL;
-               hwgroup->drive   = NULL;
-               hwgroup->reset_timeout = 0;
-#ifdef CONFIG_BLK_DEV_IDECD
-               hwgroup->doing_atapi_reset = 0;
-#endif /* CONFIG_BLK_DEV_IDECD */
+               hwgroup->drive   = &hwif->drives[0];
+               hwgroup->poll_timeout = 0;
                init_timer(&hwgroup->timer);
                hwgroup->timer.function = &timer_expiry;
                hwgroup->timer.data = (unsigned long) hwgroup;
@@ -3002,5 +3062,10 @@ int ide_init (void)
                        hwif->present = 1;      /* success */
                }
        }
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+       idetape_register_chrdev();      /* Register character device interface to the ide tape */
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+       
        return 0;
 }
index 0c0f200815d6c222e01a6f94dd8f3a5477ee290f..00a0303ff164805726c27a25531b418b7f5c1eaa 100644 (file)
@@ -152,6 +152,10 @@ typedef unsigned char      byte;   /* used everywhere */
 #define WAIT_WORSTCASE (30*HZ) /* 30sec  - worst case when spinning up */
 #define WAIT_CMD       (10*HZ) /* 10sec  - maximum wait for an IRQ to happen */
 
+#ifdef CONFIG_BLK_DEV_IDETAPE
+#include "ide-tape.h"
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
 #ifdef CONFIG_BLK_DEV_IDECD
 
 struct atapi_request_sense {
@@ -234,7 +238,8 @@ struct cdrom_info {
 /*
  * Now for the data we need to maintain per-drive:  ide_drive_t
  */
-typedef enum {disk, cdrom} media_t;
+
+typedef enum {ide_disk, ide_cdrom, ide_tape} ide_media_t;
 
 typedef union {
        unsigned all                    : 8;    /* all of the bits together */
@@ -271,7 +276,7 @@ typedef struct ide_drive_s {
        unsigned removeable     : 1;    /* 1 if need to do check_media_change */
        unsigned using_dma      : 1;    /* disk is using dma for read/write */
        unsigned unmask         : 1;    /* flag: okay to unmask other irqs */
-       media_t         media;          /* disk, cdrom, tape */
+       ide_media_t     media;          /* disk, cdrom, tape */
        select_t        select;         /* basic drive/head select reg value */
        void            *hwif;          /* actually (ide_hwif_t *) */
        byte            ctl;            /* "normal" value for IDE_CONTROL_REG */
@@ -295,6 +300,22 @@ typedef struct ide_drive_s {
 #ifdef CONFIG_BLK_DEV_IDECD
        struct cdrom_info cdrom_info;   /* from ide-cd.c */
 #endif /* CONFIG_BLK_DEV_IDECD */
+
+#ifdef CONFIG_BLK_DEV_IDETAPE          /* ide-tape specific data */
+
+/*
+ *     Most of our global data which we need to save even as we leave the
+ *     driver due to an interrupt or a timer event is stored here.
+ *
+ *     Additional global variables which provide the link between the
+ *     character device interface to this structure are defined in
+ *     ide-tape.c
+ */
+
+       idetape_tape_t tape;
+
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
        } ide_drive_t;
 
 /*
@@ -348,10 +369,7 @@ typedef struct hwgroup_s {
        struct request          *rq;    /* current request */
        struct timer_list       timer;  /* failsafe timer */
        struct request          wrq;    /* local copy of current write rq */
-       unsigned long   reset_timeout;  /* timeout value during ide resets */
-#ifdef CONFIG_BLK_DEV_IDECD
-       int                     doing_atapi_reset;
-#endif /* CONFIG_BLK_DEV_IDECD */
+       unsigned long           poll_timeout;   /* timeout value during long polls */
        } ide_hwgroup_t;
 
 /*
@@ -367,16 +385,6 @@ void ide_set_recovery_timer (ide_hwif_t *);
 #define SET_RECOVERY_TIMER(drive)
 #endif
 
-/*
- * The main (re-)entry point for handling a new request is IDE_DO_REQUEST.
- * Note that IDE_DO_REQUEST should *only* ever be invoked from an interrupt
- * handler.  All others, such as a timer expiry handler, should call
- * do_hwgroup_request() instead (currently local to ide.c).
- */
-void ide_do_request (ide_hwgroup_t *);
-#define IDE_DO_REQUEST { SET_RECOVERY_TIMER(HWIF(drive)); ide_do_request(HWGROUP(drive)); }
-
-
 /*
  * This is used for (nearly) all data transfers from the IDE interface
  */
@@ -391,7 +399,7 @@ void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
  * This is used on exit from the driver, to designate the next irq handler
  * and also to start the safety timer.
  */
-void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler);
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout);
 
 /*
  * Error reporting, in human readable form (luxurious, but a memory hog).
@@ -400,18 +408,15 @@ byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat);
 
 /*
  * ide_error() takes action based on the error returned by the controller.
- *
- * Returns 1 if an ide reset operation has been initiated, in which case
- * the caller MUST simply return from the driver (through however many levels).
- * Returns 0 otherwise.
+ * The calling function must return afterwards, to restart the request.
  */
-int ide_error (ide_drive_t *drive, const char *msg, byte stat);
+void ide_error (ide_drive_t *drive, const char *msg, byte stat);
 
 /*
  * This routine busy-waits for the drive status to be not "busy".
  * It then checks the status for all of the "good" bits and none
  * of the "bad" bits, and if all is okay it returns 0.  All other
- * cases return 1 after invoking ide_error()
+ * cases return 1 after invoking ide_error() -- caller should return.
  *
  */
 int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
@@ -423,31 +428,51 @@ int ide_xlate_1024(kdev_t, int, const char *);
 
 /*
  * Start a reset operation for an IDE interface.
- * Returns 0 if the reset operation is still in progress,
- *  in which case the drive MUST return, to await completion.
- * Returns 1 if the reset is complete (success or failure).
+ * The caller should return immediately after invoking this.
  */
-int ide_do_reset (ide_drive_t *);
+void ide_do_reset (ide_drive_t *);
 
 /*
- * ide_alloc(): memory allocation for use *only* during driver initialization.
- * If "within_area" is non-zero, the memory will be allocated such that
- * it lies entirely within a "within_area" sized area (eg. 4096).  This is
- * needed for DMA stuff.  "within_area" must be a power of two (not validated).
- * All allocations are longword aligned.
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
  */
-void *ide_alloc (unsigned long bytecount, unsigned long within_area);
+void ide_init_drive_cmd (struct request *rq);
 
 /*
- * This function issues a specific IDE drive command onto the
- * tail of the request queue, and waits for it to be completed.
- * If arg is NULL, it goes through all the motions,
- * but without actually sending a command to the drive.
- *
- * The value of arg is passed to the internal handler as rq->buffer.
+ * "action" parameter type for ide_do_drive_cmd() below.
  */
-int ide_do_drive_cmd(kdev_t rdev, char *args);
+typedef enum
+       {ide_wait,      /* insert rq at end of list, and wait for it */
+        ide_next,      /* insert rq immediately after current request */
+        ide_preempt}   /* insert rq in front of current request */
+ ide_action_t;
 
+/*
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then then rq is queued at the end of
+ * the request queue, and the function sleeps until it has been
+ * processed.  This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed.  This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed.  As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the 
+ * ATAPI tape/cdrom driver code.
+ */
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action);
+/*
+ * Clean up after success/failure of an explicit drive cmd.
+ * stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_CMD).
+ */
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err);
 
 #ifdef CONFIG_BLK_DEV_IDECD
 /*
@@ -461,6 +486,57 @@ void ide_cdrom_release (struct inode *, struct file *, ide_drive_t *);
 void ide_cdrom_setup (ide_drive_t *);
 #endif /* CONFIG_BLK_DEV_IDECD */
 
+#ifdef CONFIG_BLK_DEV_IDETAPE
+
+/*
+ *     Functions in ide-tape.c which are invoked from ide.c:
+ */
+
+/*
+ *     idetape_identify_device is called during device probing stage to
+ *     probe for an ide atapi tape drive and to initialize global variables
+ *     in ide-tape.c which provide the link between the character device
+ *     and the correspoding block device.
+ *
+ *     Returns 1 if an ide tape was detected and is supported.
+ *     Returns 0 otherwise.
+ */
+int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id);
+
+/*
+ *     idetape_setup is called a bit later than idetape_identify_device,
+ *     during the search for disk partitions, to initialize various tape
+ *     state variables in ide_drive_t *drive.
+ */
+void idetape_setup (ide_drive_t *drive);
+
+/*
+ *     idetape_do_request is our request function. It is called by ide.c
+ *     to process a new request.
+ */
+
+void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
+
+/*
+ *     Block device interface functions.
+ */
+  
+int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+                       unsigned int cmd, unsigned long arg);
+int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
+void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
+
+/*
+ *     idetape_register_chrdev initializes the character device interface to
+ *     the ide tape drive.
+ */
+void idetape_register_chrdev (void);
+
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
 #ifdef CONFIG_BLK_DEV_TRITON
 void ide_init_triton (byte, byte);
 #endif /* CONFIG_BLK_DEV_TRITON */
index 4b5ae44833814ea30e8bb01a8dde59c7626cfc92..c43a7c70657b4d922185d94b75441c330774a0da 100644 (file)
@@ -232,20 +232,24 @@ void set_device_ro(kdev_t dev,int flag)
 static inline void drive_stat_acct(int cmd, unsigned long nr_sectors, short disk_index)
 {
        kstat.dk_drive[disk_index]++;
-       if (cmd == READ || cmd == READA) {
+       if (cmd == READ) {
                kstat.dk_drive_rio[disk_index]++;
                kstat.dk_drive_rblk[disk_index] += nr_sectors;
        }
-       else if (cmd == WRITE || cmd == WRITEA) {
+       else if (cmd == WRITE) {
                kstat.dk_drive_wio[disk_index]++;
                kstat.dk_drive_wblk[disk_index] += nr_sectors;
-       }
+       } else
+               printk("drive_stat_acct: cmd not R/W?\n");
 }
 
 /*
  * add-request adds a request to the linked list.
  * It disables interrupts so that it can muck with the
  * request-lists in peace.
+ *
+ * By this point, req->cmd is always either READ/WRITE, never READA/WRITEA,
+ * which is important for drive_stat_acct() above.
  */
 static void add_request(struct blk_dev_struct * dev, struct request * req)
 {
@@ -302,21 +306,6 @@ static void make_request(int major,int rw, struct buffer_head * bh)
        struct request * req;
        int rw_ahead, max_req;
 
-/* WRITEA/READA is special case - it is not really needed, so if the */
-/* buffer is locked, we just forget about it, else it's a normal read */
-       rw_ahead = (rw == READA || rw == WRITEA);
-       if (rw_ahead) {
-               if (buffer_locked(bh))
-                       return;
-               if (rw == READA)
-                       rw = READ;
-               else
-                       rw = WRITE;
-       }
-       if (rw!=READ && rw!=WRITE) {
-               printk("Bad block dev command, must be R/W/RA/WA\n");
-               return;
-       }
        count = bh->b_size >> 9;
        sector = bh->b_blocknr * count;
        if (blk_size[major])
@@ -330,21 +319,46 @@ static void make_request(int major,int rw, struct buffer_head * bh)
                return;
        /* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */
        lock_buffer(bh);
-       if ((rw == WRITE && !buffer_dirty(bh)) || (rw == READ && buffer_uptodate(bh))) {
-               unlock_buffer(bh);
-               return;
-       }
 
-/* we don't allow the write-requests to fill up the queue completely:
- * we want some room for reads: they take precedence. The last third
- * of the requests are only for reads.
- */
-       max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
+       rw_ahead = 0;   /* normal case; gets changed below for READA/WRITEA */
+       switch (rw) {
+               case READA:
+                       rw_ahead = 1;
+                       rw = READ;      /* drop into READ */
+               case READ:
+                       if (buffer_uptodate(bh)) {
+                               unlock_buffer(bh); /* Hmmph! Already have it */
+                               return;
+                       }
+                       kstat.pgpgin++;
+                       max_req = NR_REQUEST;   /* reads take precedence */
+                       break;
+               case WRITEA:
+                       rw_ahead = 1;
+                       rw = WRITE;     /* drop into WRITE */
+               case WRITE:
+                       if (!buffer_dirty(bh)) {
+                               unlock_buffer(bh); /* Hmmph! Nothing to write */
+                               return;
+                       }
+                       /* We don't allow the write-requests to fill up the
+                        * queue completely:  we want some room for reads,
+                        * as they take precedence. The last third of the
+                        * requests are only for reads.
+                        */
+                       kstat.pgpgout++;
+                       max_req = (NR_REQUEST * 2) / 3;
+                       break;
+               default:
+                       printk("make_request: bad block dev cmd, must be R/W/RA/WA\n");
+                       unlock_buffer(bh);
+                       return;
+       }
 
 /* look for a free request. */
        cli();
 
-/* The scsi disk drivers and the IDE driver completely remove the request
+/* The scsi disk and cdrom drivers completely remove the request
  * from the queue when they start processing an entry.  For this reason
  * it is safe to continue to add links to the top entry for those devices.
  */
@@ -357,11 +371,7 @@ static void make_request(int major,int rw, struct buffer_head * bh)
             || major == IDE3_MAJOR)
            && (req = blk_dev[major].current_request))
        {
-#ifdef CONFIG_BLK_DEV_HD
-               if (major == HD_MAJOR || major == FLOPPY_MAJOR)
-#else
-               if (major == FLOPPY_MAJOR)
-#endif CONFIG_BLK_DEV_HD
+               if (major != SCSI_DISK_MAJOR && major != SCSI_CDROM_MAJOR)
                        req = req->next;
                while (req) {
                        if (req->rq_dev == bh->b_dev &&
@@ -438,12 +448,18 @@ void ll_rw_page(int rw, kdev_t dev, unsigned long page, char * buffer)
                       kdevname(dev), sector);
                return;
        }
-       if (rw!=READ && rw!=WRITE)
-               panic("Bad block dev command, must be R/W");
-       if (rw == WRITE && is_read_only(dev)) {
-               printk("Can't page to read-only device %s\n",
-                      kdevname(dev));
-               return;
+       switch (rw) {
+               case READ:
+                       break;
+               case WRITE:
+                       if (is_read_only(dev)) {
+                               printk("Can't page to read-only device %s\n",
+                                       kdevname(dev));
+                               return;
+                       }
+                       break;
+               default:
+                       panic("ll_rw_page: bad block dev cmd, must be R/W");
        }
        req = get_request_wait(NR_REQUEST, dev);
 /* fill up the request-info, and add it to the queue */
@@ -524,10 +540,6 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
                if (bh[i]) {
                        set_bit(BH_Req, &bh[i]->b_state);
                        make_request(major, rw, bh[i]);
-                       if (rw == READ || rw == READA)
-                               kstat.pgpgin++;
-                       else
-                               kstat.pgpgout++;
                }
        }
        unplug_device(dev);
@@ -555,17 +567,19 @@ void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buf)
                printk("ll_rw_swap_file: trying to swap nonexistent block-device\n");
                return;
        }
-
-       if (rw != READ && rw != WRITE) {
-               printk("ll_rw_swap: bad block dev command, must be R/W");
-               return;
-       }
-       if (rw == WRITE && is_read_only(dev)) {
-               printk("Can't swap to read-only device %s\n",
-                      kdevname(dev));
-               return;
+       switch (rw) {
+               case READ:
+                       break;
+               case WRITE:
+                       if (is_read_only(dev)) {
+                               printk("Can't swap to read-only device %s\n",
+                                       kdevname(dev));
+                               return;
+                       }
+                       break;
+               default:
+                       panic("ll_rw_swap: bad block dev cmd, must be R/W");
        }
-       
        buffersize = PAGE_SIZE / nb;
 
        for (j=0, i=0; i<nb;)
index fa179509eeb3d40a84ddecbb7a9aae4a690046a8..d83765ac2ab26eb5af306278dcb87050592a9a36 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/triton.c       Version 1.03  Nov 16, 1995
+ *  linux/drivers/block/triton.c       Version 1.04  Dec 1, 1995
  *
  *  Copyright (c) 1995  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
  *
  * If you have any drive models to add, email your results to:  mlord@bnr.ca
  * Keep an eye on /var/adm/messages for "DMA disabled" messages.
+ *
+ * Some people have reported trouble with Intel Zappa motherboards.
+ * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
+ * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
+ * (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
+ *
+ * And, yes, Intel Zappa boards really *do* use the Triton IDE ports.
  */
 #define _TRITON_C
 #include <linux/config.h>
@@ -141,14 +148,12 @@ static void dma_intr (ide_drive_t *drive)
                                i -= rq->current_nr_sectors;
                                ide_end_request(1, HWGROUP(drive));
                        }
-                       IDE_DO_REQUEST;
                        return;
                }
                printk("%s: bad DMA status: 0x%02x\n", drive->name, dma_stat);
        }
        sti();
-       if (!ide_error(drive, "dma_intr", stat))
-               IDE_DO_REQUEST;
+       ide_error(drive, "dma_intr", stat);
 }
 
 /*
@@ -269,7 +274,7 @@ static int triton_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
        outl(virt_to_bus (HWIF(drive)->dmatable), dma_base + 4); /* PRD table */
        outb(reading, dma_base);                        /* specify r/w */
        outb(0x26, dma_base+2);                         /* clear status bits */
-       ide_set_handler (drive, &dma_intr);             /* issue cmd to drive */
+       ide_set_handler (drive, &dma_intr, WAIT_CMD);   /* issue cmd to drive */
        OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
        outb(inb(dma_base)|1, dma_base);                /* begin DMA */
        return 0;
@@ -307,7 +312,7 @@ void ide_init_triton (byte bus, byte fn)
        int rc = 0, h;
        unsigned short bmiba, pcicmd;
        unsigned int timings;
-       unsigned char *dmatable = NULL;
+       unsigned long dmatable = 0;
        extern ide_hwif_t ide_hwifs[];
 
        /*
@@ -331,7 +336,7 @@ void ide_init_triton (byte bus, byte fn)
        if ((rc = pcibios_read_config_dword(bus, fn, 0x40, &timings)))
                goto quit;
        if (!(timings & 0x80008000)) {
-               printk("ide: Triton IDE ports are not enabled\n");
+               printk("ide: neither Triton IDE port is enabled\n");
                goto quit;
        }
        printk("ide: Triton BM-IDE on PCI bus %d function %d\n", bus, fn);
@@ -363,15 +368,15 @@ void ide_init_triton (byte bus, byte fn)
                } else {
                        request_region(base, 8, hwif->name);
                        hwif->dma_base = base;
-                       if (dmatable == NULL) {
+                       if (!dmatable) {
                                /*
                                 * Since we know we are on a PCI bus, we could
                                 * actually use __get_free_pages() here instead
                                 * of __get_dma_pages() -- no ISA limitations.
                                 */
-                               dmatable = (void *) __get_dma_pages(GFP_KERNEL, 0);
+                               dmatable = __get_dma_pages(GFP_KERNEL, 0);
                        }
-                       if (dmatable != NULL) {
+                       if (dmatable) {
                                hwif->dmatable = (unsigned long *) dmatable;
                                dmatable += (PRD_ENTRIES * PRD_BYTES);
                                outl(virt_to_bus(hwif->dmatable), base + 4);
index b9c1ece61996ea56369af23e4981c0cb7c40d0d4..192a0b1c319054b9057c0c76f82043c9041d0aa1 100644 (file)
@@ -1,5 +1,5 @@
-#define AZT_VERSION "2.0"
-/*      $Id: aztcd.c,v 2.0 1995/11/10 19:33:41 root Exp root $
+#define AZT_VERSION "2.1"
+/*      $Id: aztcd.c,v 2.10 1995/12/03 11:55:09 root Exp root $
        linux/drivers/block/aztcd.c - AztechCD268 CDROM driver
 
        Copyright (C) 1994,1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
                 with kernel 1.3.33. Will definitely not work with older kernels.
                 Programming done by Linus himself.
                 Werner Zimmermann, October 11, 1995
-       V1.90   Support for Conrad TXC drives, thank's to Jochen Koch and Olaf Koluza.
+       V1.90   Support for Conrad TXC drives, thank's to Jochen Koch and Olaf Kaluza.
                Werner Zimmermann, October 21, 1995
         V2.00   Changed #include "blk.h" to <linux/blk.h> as the directory
                 structure was changed. README.aztcd is now /usr/src/docu-
                 mentation/cdrom/aztcd
                 Werner Zimmermann, November 10, 95
-
+        V2.10   Started to modify azt_poll to prevent reading beyond end of
+                tracks.
+                Werner Zimmermann, December 3, 95
        NOTE: 
        Points marked with ??? are questionable !
 */
@@ -258,6 +260,7 @@ static void azt_invalidate_buffers(void);
 static void do_aztcd_request(void);
 static void azt_hsg2msf(long hsg, struct msf *msf);
 static void azt_bin2bcd(unsigned char *p);
+static long azt_msf2hsg(struct msf *mp);
 static int  azt_bcd2bin(unsigned char bcd);
 static int  aztStatus(void);
 static int  getAztStatus(void);
@@ -604,10 +607,6 @@ static int aztPlay(struct azt_Play_msf *arg)
 }
 
 
-long azt_msf2hsg(struct msf *mp)
-{ return azt_bcd2bin(mp -> frame) + azt_bcd2bin(mp -> sec) * 75
-                                 + azt_bcd2bin(mp -> min) * 4500 - CD_BLOCK_OFFSET;
-}
 
 static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
 {       int i, st;
@@ -1180,11 +1179,28 @@ static void azt_poll(void)
 
          if (CURRENT_VALID) {
            struct azt_Play_msf msf;
+           int i;
            azt_next_bn = CURRENT -> sector / 4;
            azt_hsg2msf(azt_next_bn, &msf.start);
-           azt_read_count=AZT_BUF_SIZ;    /*??? fast, because we read ahead*/
-/*          azt_read_count= CURRENT->nr_sectors;      slow
-*/
+           i = 0;
+           /* find out in which track we are */
+           while (azt_msf2hsg(&msf.start)>azt_msf2hsg(&Toc[++i].trackTime)) {};
+           if (azt_msf2hsg(&msf.start)<azt_msf2hsg(&Toc[i].trackTime)-AZT_BUF_SIZ)
+               { azt_read_count=AZT_BUF_SIZ;  /*fast, because we read ahead*/
+               /*azt_read_count=CURRENT->nr_sectors;    slow, no read ahead*/
+               }
+           else /* don't read beyond end of track */           
+#if AZT_MULTISESSION 
+               { azt_read_count=(azt_msf2hsg(&Toc[i].trackTime)/4)*4-azt_msf2hsg(&msf.start);  
+                 if (azt_read_count < 0) azt_read_count=0;
+                if (azt_read_count > AZT_BUF_SIZ) azt_read_count=AZT_BUF_SIZ;
+                 printk("aztcd: warning - trying to read beyond end of track\n");
+/*               printk("%i %i %li %li\n",i,azt_read_count,azt_msf2hsg(&msf.start),azt_msf2hsg(&Toc[i].trackTime));
+*/             }
+#else
+              { azt_read_count=AZT_BUF_SIZ;
+              }
+#endif
            msf.end.min = 0;
            msf.end.sec = 0;            
            msf.end.frame = azt_read_count ;/*Mitsumi here reads 0xffffff sectors*/
@@ -1629,26 +1645,26 @@ int aztcd_init(void)
        else          max_count=count;
        printk("aztcd: FirmwareVersion=");
        for (count=1;count<max_count;count++) printk("%c",result[count]);
-       printk("<<<\n");
+       printk("<<>> ");
 
        if ((result[1]=='A')&&(result[2]=='Z')&&(result[3]=='T'))
-        { printk("aztcd: AZTECH drive detected\n"); /*AZTECH*/    
+        { printk("AZTECH drive detected\n"); /*AZTECH*/    
         }
        else if ((result[2]=='C')&&(result[3]=='D')&&(result[4]=='D'))
-        { printk("aztcd: ORCHID or WEARNES drive detected\n"); /*ORCHID or WEARNES*/
+        { printk("ORCHID or WEARNES drive detected\n"); /*ORCHID or WEARNES*/
         }
        else if ((result[1]==0x03)&&(result[2]=='5'))
-        { printk("aztcd: TXC drive detected\n"); /*Conrad TXC*/
+        { printk("TXC drive detected\n"); /*Conrad TXC*/
         }
        else                                               /*OTHERS or none*/
-        { printk("aztcd: : unknown drive or firmware version detected\n");
-          printk("                      azt may not run stable, if you want to try anyhow,\n");
-          printk("                      boot with: aztcd=<BaseAddress>,0x79\n");
+        { printk("\nunknown drive or firmware version detected\n");
+          printk("aztcd may not run stable, if you want to try anyhow,\n");
+          printk("boot with: aztcd=<BaseAddress>,0x79\n");
           if ((azt_cont!=0x79))     
             { printk("aztcd: FirmwareVersion=");
               for (count=1;count<5;count++) printk("%c",result[count]);
-              printk("\n");
-              printk("aztcd: Aborted\n");
+              printk("<<>> ");
+              printk("Aborted\n");
                return -EIO;
             }
         }
@@ -1666,8 +1682,8 @@ int aztcd_init(void)
        azt_invalidate_buffers();
        aztPresent = 1;
        aztCloseDoor();
-       printk("aztcd: End Init\n");
-        return (0);
+/*     printk("aztcd: End Init\n");
+*/      return (0);
 }
 
 
@@ -1687,6 +1703,10 @@ static void azt_hsg2msf(long hsg, struct msf *msf)
        azt_bin2bcd(&msf -> frame);
 }
 
+static long azt_msf2hsg(struct msf *mp)
+{ return azt_bcd2bin(mp -> frame) + azt_bcd2bin(mp -> sec) * 75
+                                 + azt_bcd2bin(mp -> min) * 4500 - CD_BLOCK_OFFSET;
+}
 
 static void azt_bin2bcd(unsigned char *p)
 {       int u, t;
@@ -1700,8 +1720,6 @@ static int azt_bcd2bin(unsigned char bcd)
 {       return (bcd >> 4) * 10 + (bcd & 0xF);
 }
 
-
-
 /*
  * Read a value from the drive.  Should return quickly, so a busy wait
  * is used to avoid excessive rescheduling. The read command itself must
@@ -2047,7 +2065,7 @@ static int aztGetToc(int multi)
       }
 
   Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
-
+  Toc[DiskInfo.last].trackTime    = DiskInfo.diskLength;
 
 #ifdef AZT_DEBUG_MULTISESSION 
   printk("aztcd: exiting aztGetToc\n");
index 080f3aa816481be37f423a8ea012741e779c2071..957c943c98d24395d8aa5f66366c54307b0a64f1 100644 (file)
@@ -29,3 +29,10 @@ if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
     comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/'
 fi
 fi
+bool 'Advanced Power Management BIOS support' CONFIG_APM
+if [ "$CONFIG_APM" = "y" ]; then
+  bool '   Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND
+  bool '   Enable PM at boot time' CONFIG_APM_DO_ENABLE
+  bool '   Make CPU Idle calls when idle' CONFIG_APM_CPU_IDLE
+  bool '   Enable console blanking using APM' CONFIG_APM_DISPLAY_BLANK
+fi
index bbd9e352744734ea640c16b5cb8963621737a363..c6e40d134007c8ff0ca2adcbdf09a79ad8b45e19 100644 (file)
@@ -19,6 +19,7 @@ M_OBJS   :=
 L_OBJS   := tty_io.o n_tty.o console.o keyboard.o serial.o \
        tty_ioctl.o pty.o vt.o mem.o vc_screen.o random.o \
        defkeymap.o consolemap.o selection.o
+SYMTAB_OBJS :=
 
 ifeq ($(CONFIG_CYCLADES),y)
 L_OBJS += cyclades.o
@@ -96,6 +97,11 @@ ifdef CONFIG_QIC02_TAPE
 L_OBJS += tpqic02.o 
 endif
 
+ifdef CONFIG_APM
+L_OBJS += apm_bios.o
+SYMTAB_OBJS += apm_bios.o
+endif
+
 ifdef M
 L_OBJS += mouse.o
 else
diff --git a/drivers/char/apm_bios.c b/drivers/char/apm_bios.c
new file mode 100644 (file)
index 0000000..6196f17
--- /dev/null
@@ -0,0 +1,1086 @@
+/*
+ * APM BIOS driver for Linux
+ * Copyright 1994, 1995 Stephen Rothwell (Stephen.Rothwell@pd.necisa.oz.au)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * $Id: apm_bios.c,v 0.22 1995/03/09 14:12:02 sfr Exp $
+ *
+ * October 1995, Rik Faith (faith@cs.unc.edu):
+ *    Minor enhancements and updates (to the patch set) for 1.3.x
+ *
+ * Reference:
+ *
+ *   Intel Corporation, Microsoft Corporation. Advanced Power Management
+ *   (APM) BIOS Interface Specification, Revision 1.1, September 1993.
+ *   Intel Order Number 241704-001.  Microsoft Part Number 781-110-X01.
+ *
+ * [This document is available free from Intel by calling 800.628.8686 (fax
+ * 916.356.6100) or 800.548.4725.  It is also available from Microsoft by
+ * calling 206.882.8080; and is ftpable from
+ * ftp://ftp.intel.com/pub/IAL/software_specs/apmv11.doc]
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/fcntl.h>
+#include <linux/malloc.h>
+#include <linux/linkage.h>
+#include <linux/apm_bios.h>
+
+static struct symbol_table     apm_syms = {
+#include <linux/symtab_begin.h>
+       X(apm_register_callback),
+       X(apm_unregister_callback),
+#include <linux/symtab_end.h>
+};
+
+extern unsigned long get_cmos_time(void);
+
+/* Configurable options:
+ *  
+ * CONFIG_APM_IGNORE_USER_SUSPEND: define to ignore USER SUSPEND requests.
+ * This is necessary on the NEC Versa M series, which generates these when
+ * resuming from SYSTEM SUSPEND.  However, enabling this on other laptops
+ * will cause the laptop to generate a CRITICAL SUSPEND when an appropriate
+ * USER SUSPEND is ignored -- this may prevent the APM driver from updating
+ * the system time on a RESUME.
+ *
+ * CONFIG_APM_DO_ENABLE: enable APM features at boot time.  From page 36 of
+ * the specification: "When disabled, the APM BIOS does not automatically
+ * power manage devices, enter the Standby State, enter the Suspend State,
+ * or take power saving steps in response to CPU Idle calls."  This driver
+ * will make CPU Idle calls when Linux is idle (unless this feature is
+ * turned off -- see below).  This should always save battery power, but
+ * more complicated APM features will be dependent on your BIOS
+ * implementation.  You may need to turn this option off if your computer
+ * hangs at boot time when using APM support, or if it beeps continuously
+ * instead of suspending.  Turn this off if you have a NEC UltraLite Versa
+ * 33/C or a Toshiba T400CDT.  This is off by default since most machines
+ * do fine without this feature.
+ *
+ * CONFIG_APM_CPU_IDLE: enable calls to APM CPU Idle/CPU Busy inside the
+ * idle loop.  On some machines, this can activate improved power savings,
+ * such as a slowed CPU clock rate, when the machine is idle.  These idle
+ * call is made after the idle loop has run for some length of time (e.g.,
+ * 333 mS).  On some machines, this will cause a hang at boot time or
+ * whenever the CPU becomes idle.
+ *
+ * CONFIG_APM_DISPLAY_BLANK: enable console blanking using the APM.  Some
+ * laptops can use this to turn of the LCD backlight when the VC screen
+ * blanker blanks the screen.  Note that this is only used by the VC screen
+ * blanker, and probably won't turn off the backlight when using X11.
+ *
+ * If you are debugging the APM support for your laptop, note that code for
+ * all of these options is contained in this file, so you can #define or
+ * #undef these on the next line to avoid recompiling the whole kernel.
+ *
+ */
+
+/* KNOWN PROBLEM MACHINES:
+ *
+ * U: TI 4000M TravelMate: BIOS is *NOT* APM compliant
+ *                         [Confirmed by TI representative]
+ * U: ACER 486DX4/75: uses dseg 0040, in violation of APM specification
+ *                    [Confirmed by BIOS disassembly]
+ * P: Toshiba 1950S: battery life information only gets updated after resume
+ *
+ * Legend: U = unusable with APM patches
+ *         P = partially usable with APM patches
+ */
+
+/*
+ * define to have debug messages
+ */
+#undef APM_DEBUG
+
+/*
+ * define to always call the APM BIOS busy routine even if the clock was
+ * not slowed by the idle routine
+ */
+#define ALWAYS_CALL_BUSY
+
+/*
+ * define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
+ * should turn interrupts on before it does a 'hlt')
+ */
+#define APM_NOINTS
+
+/*
+ * define to make the APM BIOS calls zero all data segment registers (do
+ * that an incorrect BIOS implementation will cause a kernel panic if it
+ * tries to write to arbitrary memory)
+ */
+#define APM_ZERO_SEGS
+
+/*
+ * Need to poll the APM BIOS every second
+ */
+#define APM_CHECK_TIMEOUT      (HZ)
+
+/*
+ * These are the actual BIOS calls in assembler.  Depending on
+ * APM_ZERO_SEGS and APM_NOINTS, we are being really paranoid here!  Not
+ * only are interrupts disabled, but all the segment registers (except SS)
+ * are saved and zeroed this means that if the BIOS tries to reference any
+ * data without explicitly loading the segment registers, the kernel will
+ * fault immediately rather than have some unforeseen circumstances for the
+ * rest of the kernel.  And it will be very obvious!  :-) Doing this
+ * depends on CS referring to the same physical memory as DS so that DS can
+ * be zeroed before the call. Unfortunately, we can't do anything about the
+ * stack segment/pointer.  Also, we tell the compiler that everything could
+ * change.
+ */
+#ifdef APM_NOINTS
+#      define APM_DO_CLI       "cli\n\t"
+#else
+#      define APM_DO_CLI
+#endif
+#ifdef APM_ZERO_SEGS
+#      define APM_DO_ZERO_SEGS \
+               "pushl %%ds\n\t" \
+               "pushl %%es\n\t" \
+               "pushl %%fs\n\t" \
+               "pushl %%gs\n\t" \
+               "xorl %%edx, %%edx\n\t" \
+               "mov %%dx, %%ds\n\t" \
+               "mov %%dx, %%es\n\t" \
+               "mov %%dx, %%fs\n\t" \
+               "mov %%dx, %%gs\n\t"
+#      define APM_DO_RESTORE_SEGS      \
+               "popl %%gs\n\t" \
+               "popl %%fs\n\t" \
+               "popl %%es\n\t" \
+               "popl %%ds\n\t"
+#else
+#      define APM_DO_ZERO_SEGS
+#      define APM_DO_RESTORE_SEGS
+#endif
+
+#define APM_BIOS_CALL(error_reg) \
+       __asm__ __volatile__( \
+               APM_DO_ZERO_SEGS \
+               "pushfl\n\t" \
+               APM_DO_CLI \
+               "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t" \
+               "setc %%" # error_reg "\n\t" \
+               "popfl\n\t" \
+               APM_DO_RESTORE_SEGS
+#define APM_BIOS_CALL_END \
+               : "ax", "bx", "cx", "dx", "si", "di", "bp", "memory")
+
+#ifdef CONFIG_APM_CPU_IDLE
+#define APM_SET_CPU_IDLE(error) \
+       APM_BIOS_CALL(al) \
+       : "=a" (error) \
+       : "0" (0x5305) \
+       APM_BIOS_CALL_END
+#endif
+
+#define APM_SET_CPU_BUSY(error) \
+       APM_BIOS_CALL(al) \
+       : "=a" (error) \
+       : "0" (0x5306) \
+       APM_BIOS_CALL_END
+
+#define APM_SET_POWER_STATE(state, error) \
+       APM_BIOS_CALL(al) \
+       : "=a" (error) \
+       : "0" (0x5307), "b" (0x0001), "c" (state) \
+       APM_BIOS_CALL_END
+
+#ifdef CONFIG_APM_DISPLAY_BLANK
+#define APM_SET_DISPLAY_POWER_STATE(state, error) \
+       APM_BIOS_CALL(al) \
+       : "=a" (error) \
+       : "0" (0x5307), "b" (0x01ff), "c" (state) \
+       APM_BIOS_CALL_END
+#endif
+
+#ifdef CONFIG_APM_DO_ENABLE
+#define APM_ENABLE_POWER_MANAGEMENT(device, error) \
+       APM_BIOS_CALL(al) \
+       : "=a" (error) \
+       : "0" (0x5308), "b" (device), "c" (1) \
+       APM_BIOS_CALL_END
+#endif
+
+#define APM_GET_POWER_STATUS(bx, cx, dx, error) \
+       APM_BIOS_CALL(al) \
+       : "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx) \
+       : "0" (0x530a), "1" (1) \
+       APM_BIOS_CALL_END
+
+#define APM_GET_EVENT(event, error)    \
+       APM_BIOS_CALL(al) \
+       : "=a" (error), "=b" (event) \
+       : "0" (0x530b) \
+       APM_BIOS_CALL_END
+
+#define APM_DRIVER_VERSION(ver, ax, error) \
+       APM_BIOS_CALL(bl) \
+       : "=a" (ax), "=b" (error) \
+       : "0" (0x530e), "1" (0), "c" (ver) \
+       APM_BIOS_CALL_END
+
+#define APM_ENGAGE_POWER_MANAGEMENT(device, error) \
+       APM_BIOS_CALL(al) \
+       : "=a" (error) \
+       : "0" (0x530f), "b" (device), "c" (1) \
+       APM_BIOS_CALL_END
+
+/*
+ * Forward declarations
+ */
+static void    suspend(void);
+static void    standby(void);
+static void    set_time(void);
+
+static void    check_events(void);
+static void    do_apm_timer(unsigned long);
+
+static int     do_open(struct inode *, struct file *);
+static void    do_release(struct inode *, struct file *);
+static int     do_read(struct inode *, struct file *, char *, int);
+static int     do_select(struct inode *, struct file *, int,
+                         select_table *);
+static int     do_ioctl(struct inode *, struct file *, u_int, u_long);
+
+extern int     apm_register_callback(int (*)(apm_event_t));
+extern void    apm_unregister_callback(int (*)(apm_event_t));
+
+/*
+ * Local variables
+ */
+static asmlinkage struct {
+       unsigned long   offset;
+       unsigned short  segment;
+}                              apm_bios_entry;
+static int                     apm_enabled = 0;
+#ifdef CONFIG_APM_CPU_IDLE
+static int                     clock_slowed = 0;
+#endif
+static int                     apm_major;
+static int                     suspends_pending = 0;
+static int                     standbys_pending = 0;
+
+static long                    clock_cmos_diff;
+static int                     got_clock_diff = 0;
+
+static struct wait_queue *     process_list = NULL;
+static struct apm_bios_struct *        user_list = NULL;
+
+static struct timer_list       apm_timer;
+
+static char                    driver_version[] = "0.6b";
+
+#ifdef APM_DEBUG
+static char *  apm_event_name[] = {
+       "system standby",
+       "system suspend",
+       "normal resume",
+       "critical resume",
+       "low battery",
+       "power status change",
+       "update time",
+       "critical suspend",
+       "user standby",
+       "user suspend",
+       "system standby resume"
+};
+#define NR_APM_EVENT_NAME      \
+               (sizeof(apm_event_name) / sizeof(apm_event_name[0]))
+#endif
+
+static struct file_operations apm_bios_fops = {
+       NULL,           /* lseek */
+       do_read,
+       NULL,           /* write */
+       NULL,           /* readdir */
+       do_select,
+       do_ioctl,
+       NULL,           /* mmap */
+       do_open,
+       do_release,
+       NULL,           /* fsync */
+       NULL            /* fasync */
+};
+
+typedef struct callback_list_t {
+       int (*                          callback)(apm_event_t);
+       struct callback_list_t *        next;
+} callback_list_t;
+
+static callback_list_t *       callback_list = NULL;
+
+typedef struct lookup_t {
+       int     key;
+       char *  msg;
+} lookup_t;
+
+static const lookup_t error_table[] = {
+/* N/A { APM_SUCCESS,          "Operation succeeded" }, */
+       { APM_DISABLED,         "Power management disabled" },
+       { APM_CONNECTED,        "Real mode interface already connected" },
+       { APM_NOT_CONNECTED,    "Interface not connected" },
+       { APM_16_CONNECTED,     "16 bit interface already connected" },
+/* N/A { APM_16_UNSUPPORTED,   "16 bit interface not supported" }, */
+       { APM_32_CONNECTED,     "32 bit interface already connected" },
+       { APM_32_UNSUPPORTED,   "32 bit interface not supported" },
+       { APM_BAD_DEVICE,       "Unrecognized device ID" },
+       { APM_BAD_PARAM,        "Parameter out of range" },
+       { APM_NOT_ENGAGED,      "Interface not engaged" },
+       { APM_BAD_STATE,        "Unable to enter requested state" },
+/* N/A { APM_NO_EVENTS,        "No events pending" }, */
+       { APM_NOT_PRESENT,      "No APM present" }
+};
+#define ERROR_COUNT    (sizeof(error_table)/sizeof(lookup_t))
+
+static int apm_driver_version(u_short *val)
+{
+       u_short error;
+
+       APM_DRIVER_VERSION(*val, *val, error);
+
+       if (error & 0xff)
+               return (*val >> 8);
+       return APM_SUCCESS;
+}
+
+static int apm_get_event(apm_event_t *event)
+{
+       u_short error;
+
+       APM_GET_EVENT(*event, error);
+       if (error & 0xff)
+               return (error >> 8);
+       return APM_SUCCESS;
+}
+
+static int apm_set_power_state(u_short state)
+{
+       u_short error;
+
+       APM_SET_POWER_STATE(state, error);
+       if (error & 0xff)
+               return (error >> 8);
+       return APM_SUCCESS;
+}
+
+#ifdef CONFIG_APM_DISPLAY_BLANK
+static int apm_set_display_power_state(u_short state)
+{
+       u_short error;
+
+       APM_SET_DISPLAY_POWER_STATE(state, error);
+       if (error & 0xff)
+               return (error >> 8);
+       return APM_SUCCESS;
+}
+#endif
+
+#ifdef CONFIG_APM_DO_ENABLE
+static int apm_enable_power_management(void)
+{
+       u_short error;
+
+       APM_ENABLE_POWER_MANAGEMENT((apm_bios_info.version > 0x100)
+                                   ? 0x0001 : 0xffff,
+                                   error);
+       if (error & 0xff)
+               return (error >> 8);
+       return APM_SUCCESS;
+}
+#endif
+
+static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
+{
+       u_short error;
+
+       APM_GET_POWER_STATUS(*status, *bat, *life, error);
+       if (error & 0xff)
+               return (error >> 8);
+       return APM_SUCCESS;
+}
+
+static int apm_engage_power_management(u_short device)
+{
+       u_short error;
+
+       APM_ENGAGE_POWER_MANAGEMENT(device, error);
+       if (error & 0xff)
+               return (error >> 8);
+       return APM_SUCCESS;
+}
+
+static void apm_error(char *str, int err)
+{
+       int     i;
+
+       for (i = 0; i < ERROR_COUNT; i++)
+               if (error_table[i].key == err) break;
+       if (i < ERROR_COUNT)
+               printk("apm_bios: %s: %s\n", str, error_table[i].msg);
+       else
+               printk("apm_bios: %s: unknown error code %#2.2x\n", str, err);
+}
+
+int apm_display_blank(void)
+{
+#ifdef CONFIG_APM_DISPLAY_BLANK
+       int     error;
+
+       if (apm_bios_info.version == 0)
+               return 0;
+       error = apm_set_display_power_state(APM_STATE_STANDBY);
+       if (error == APM_SUCCESS)
+               return 1;
+       apm_error("set display standby", error);
+#endif
+       return 0;
+}
+
+int apm_display_unblank(void)
+{
+#ifdef CONFIG_APM_DISPLAY_BLANK
+       int error;
+
+       if (apm_bios_info.version == 0)
+               return 0;
+       error = apm_set_display_power_state(APM_STATE_READY);
+       if (error == APM_SUCCESS)
+               return 1;
+       apm_error("set display ready", error);
+#endif
+       return 0;
+}
+
+int apm_register_callback(int (*callback)(apm_event_t))
+{
+       callback_list_t *       new;
+
+       new = kmalloc(sizeof(callback_list_t), GFP_KERNEL);
+       if (new == NULL)
+               return -ENOMEM;
+       new->callback = callback;
+       new->next = callback_list;
+       callback_list = new;
+       return 0;
+}
+
+void apm_unregister_callback(int (*callback)(apm_event_t))
+{
+       callback_list_t **      ptr;
+       callback_list_t *       old;
+
+       ptr = &callback_list;
+       for (ptr = &callback_list; *ptr != NULL; ptr = &(*ptr)->next)
+               if ((*ptr)->callback == callback)
+                       break;
+       old = *ptr;
+       *ptr = old->next;
+       kfree_s(old, sizeof(callback_list_t));
+}
+       
+static int queue_empty(struct apm_bios_struct * as)
+{
+       return as->event_head == as->event_tail;
+}
+
+static apm_event_t get_queued_event(struct apm_bios_struct * as)
+{
+       as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+       return as->events[as->event_tail];
+}
+
+static int queue_event(apm_event_t event)
+{
+       struct apm_bios_struct *        as;
+       
+       if (user_list == NULL)
+               return 0;
+       for (as = user_list; as != NULL; as = as->next) {
+               as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
+               if (as->event_head == as->event_tail)
+                       as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+               as->events[as->event_head] = event;
+               if (!as->suser)
+                       continue;
+               switch (event) {
+               case APM_SYS_SUSPEND:
+               case APM_USER_SUSPEND:
+                       as->suspends_pending++;
+                       suspends_pending++;
+                       break;
+
+               case APM_SYS_STANDBY:
+               case APM_USER_STANDBY:
+                       as->standbys_pending++;
+                       standbys_pending++;
+                       break;
+               }
+       }
+       wake_up_interruptible(&process_list);
+       return 1;
+}
+
+static void set_time(void)
+{
+       unsigned long   flags;
+
+       if (!got_clock_diff)    /* Don't know time zone, can't set clock */
+               return;
+
+       save_flags(flags);
+       cli();
+       CURRENT_TIME = get_cmos_time() + clock_cmos_diff;
+       restore_flags(flags);
+}
+
+static void suspend(void)
+{
+       unsigned long   flags;
+       int             err;
+
+                               /* Estimate time zone so that set_time can
+                                   update the clock */
+       save_flags(flags);
+       cli();
+       clock_cmos_diff = CURRENT_TIME - get_cmos_time();
+       got_clock_diff = 1;
+       restore_flags(flags);
+       
+       err = apm_set_power_state(APM_STATE_SUSPEND);
+       if (err)
+               apm_error("suspend", err);
+       set_time();
+}
+
+static void standby(void)
+{
+       int     err;
+
+       err = apm_set_power_state(APM_STATE_STANDBY);
+       if (err)
+               apm_error("standby", err);
+}
+
+static apm_event_t get_event(void)
+{
+       int             error;
+       apm_event_t     event;
+
+       static int notified = 0;
+
+       error = apm_get_event(&event);
+       if (error == APM_SUCCESS)
+               return event;
+
+       if ((error != APM_NO_EVENTS) && (notified++ == 0))
+               apm_error("get_event", error);
+
+       return 0;
+}
+
+static void send_event(apm_event_t event, apm_event_t undo)
+{
+       callback_list_t *       call;
+       callback_list_t *       fix;
+    
+       for (call = callback_list; call != NULL; call = call->next) {
+               if (call->callback(event) && undo) {
+                       for (fix = callback_list; fix != call; fix = fix->next)
+                               fix->callback(undo);
+                       if (apm_bios_info.version > 0x100)
+                               apm_set_power_state(APM_STATE_REJECT);
+                       return;
+               }
+       }
+
+       queue_event(event);
+}
+
+static void check_events(void)
+{
+       apm_event_t     event;
+
+       while ((event = get_event()) != 0) {
+               switch (event) {
+               case APM_SYS_STANDBY:
+               case APM_USER_STANDBY:
+                       send_event(event, APM_STANDBY_RESUME);
+                       if (standbys_pending <= 0)
+                               standby();
+                       break;
+
+               case APM_USER_SUSPEND:
+#ifdef CONFIG_APM_IGNORE_USER_SUSPEND
+                       apm_set_power_state(APM_STATE_REJECT);
+                       break;
+#endif
+               case APM_SYS_SUSPEND:
+                       send_event(event, APM_NORMAL_RESUME);
+                       if (suspends_pending <= 0)
+                               suspend();
+                       break;
+
+               case APM_NORMAL_RESUME:
+               case APM_CRITICAL_RESUME:
+               case APM_STANDBY_RESUME:
+                       set_time();
+                       send_event(event, 0);
+                       break;
+
+               case APM_LOW_BATTERY:
+               case APM_POWER_STATUS_CHANGE:
+                       send_event(event, 0);
+                       break;
+
+               case APM_UPDATE_TIME:
+                       set_time();
+                       break;
+
+               case APM_CRITICAL_SUSPEND:
+                       suspend();
+                       break;
+               }
+#ifdef APM_DEBUG
+               if (event <= NR_APM_EVENT_NAME)
+                       printk("APM BIOS received %s notify\n",
+                              apm_event_name[event - 1]);
+               else
+                       printk("APM BIOS received unknown event 0x%02x\n",
+                              event);
+#endif
+       }
+}
+
+static void do_apm_timer(unsigned long unused)
+{
+       int     err;
+
+       static int      pending_count = 0;
+
+       if (((standbys_pending > 0) || (suspends_pending > 0))
+           && (apm_bios_info.version > 0x100)
+           && (pending_count-- <= 0)) {
+               pending_count = 4;
+
+               err = apm_set_power_state(APM_STATE_BUSY);
+               if (err)
+                       apm_error("busy", err);
+       }
+       check_events();
+
+       init_timer(&apm_timer);
+       apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
+       add_timer(&apm_timer);
+}
+
+int apm_do_idle(void)
+{
+#ifdef CONFIG_APM_CPU_IDLE
+       unsigned short  error;
+
+       if (!apm_enabled)
+               return 0;
+
+       APM_SET_CPU_IDLE(error);
+       if (error & 0xff)
+               return 0;
+
+       clock_slowed = (apm_bios_info.flags & APM_IDLE_SLOWS_CLOCK) != 0;
+       return 1;
+#else
+       return 0;
+#endif
+}
+
+void apm_do_busy(void)
+{
+#ifdef CONFIG_APM_CPU_IDLE
+       unsigned short  error;
+
+#ifndef ALWAYS_CALL_BUSY
+       if (!clock_slowed)
+               return;
+#endif
+
+       APM_SET_CPU_BUSY(error);
+
+       clock_slowed = 0;
+#endif
+}
+
+static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
+{
+       if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
+               printk("apm_bios: %s passed bad filp", func);
+               return 1;
+       }
+       return 0;
+}
+
+static int do_read(struct inode *inode, struct file *fp, char *buf, int count)
+{
+       struct apm_bios_struct *        as;
+       int                     i;
+       apm_event_t             event;
+       struct wait_queue       wait = { current,       NULL };
+
+       as = fp->private_data;
+       if (check_apm_bios_struct(as, "read"))
+               return -EIO;
+       if (count < sizeof(apm_event_t))
+               return -EINVAL;
+       if (queue_empty(as)) {
+               if (fp->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+               add_wait_queue(&process_list, &wait);
+repeat:
+               current->state = TASK_INTERRUPTIBLE;
+               if (queue_empty(as)
+                   && !(current->signal & ~current->blocked)) {
+                       schedule();
+                       goto repeat;
+               }
+               current->state = TASK_RUNNING;
+               remove_wait_queue(&process_list, &wait);
+       }
+       i = count;
+       while ((i >= sizeof(event)) && !queue_empty(as)) {
+               event = get_queued_event(as);
+               memcpy_tofs(buf, &event, sizeof(event));
+               buf += sizeof(event);
+               i -= sizeof(event);
+       }
+       if (i < count)
+               return count - i;
+       if (current->signal & ~current->blocked)
+               return -ERESTARTSYS;
+       return 0;
+}
+
+static int do_select(struct inode *inode, struct file *fp, int sel_type,
+                    select_table * wait)
+{
+       struct apm_bios_struct *        as;
+
+       as = fp->private_data;
+       if (check_apm_bios_struct(as, "select"))
+               return 0;
+       if (sel_type != SEL_IN)
+               return 0;
+       if (!queue_empty(as))
+               return 1;
+       select_wait(&process_list, wait);
+       return 0;
+}
+
+static int do_ioctl(struct inode * inode, struct file *filp,
+                   u_int cmd, u_long arg)
+{
+       struct apm_bios_struct *        as;
+
+       as = filp->private_data;
+       if (check_apm_bios_struct(as, "ioctl"))
+               return -EIO;
+       switch (cmd) {
+       case APM_IOC_STANDBY:
+               if (as->standbys_pending > 0) {
+                       as->standbys_pending--;
+                       standbys_pending--;
+                       if (standbys_pending <= 0)
+                               standby();
+               }
+               break;
+       case APM_IOC_SUSPEND:
+               if (as->suspends_pending > 0) {
+                       as->suspends_pending--;
+                       suspends_pending--;
+                       if (suspends_pending <= 0)
+                               suspend();
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void do_release(struct inode * inode, struct file * filp)
+{
+       struct apm_bios_struct *        as;
+
+       as = filp->private_data;
+       filp->private_data = NULL;
+       if (check_apm_bios_struct(as, "release"))
+               return;
+       if (as->standbys_pending > 0) {
+               standbys_pending -= as->standbys_pending;
+               if (standbys_pending <= 0)
+                       standby();
+       }
+       if (as->suspends_pending > 0) {
+               suspends_pending -= as->suspends_pending;
+               if (suspends_pending <= 0)
+                       suspend();
+       }
+       if (user_list == as)
+               user_list = as->next;
+       else {
+               struct apm_bios_struct *        as1;
+
+               for (as1 = user_list;
+                    (as1 != NULL) && (as1->next != as);
+                    as1 = as1->next)
+                       ;
+               if (as1 == NULL)
+                       printk("apm_bios: filp not in user list");
+               else
+                       as1->next = as->next;
+       }
+       kfree_s(as, sizeof(*as));
+}
+
+static int do_open(struct inode * inode, struct file * filp)
+{
+       struct apm_bios_struct *        as;
+
+       as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
+       if (as == NULL) {
+               printk("apm_bios: cannot allocate struct of size %d bytes",
+                      sizeof(*as));
+               return -ENOMEM;
+       }
+       as->magic = APM_BIOS_MAGIC;
+       as->event_tail = as->event_head = 0;
+       as->suspends_pending = as->standbys_pending = 0;
+       as->suser = suser();
+       as->next = user_list;
+       user_list = as;
+       filp->private_data = as;
+       return 0;
+}
+
+int apm_proc(char *buf)
+{
+       char *          p;
+       char *          power_stat;
+       char *          bat_stat;
+       unsigned short  bx;
+       unsigned short  cx;
+       unsigned short  dx;
+       unsigned short  error;
+
+       if (apm_bios_info.version == 0)
+               return 0;
+       p = buf;
+       p += sprintf(p, "BIOS version: %d.%d\nFlags: 0x%02x\n",
+                    (apm_bios_info.version >> 8) & 0xff,
+                    apm_bios_info.version & 0xff,
+                    apm_bios_info.flags);
+       if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0)
+               return p - buf;
+       p += sprintf(p, "Entry %x:%lx cseg16 %x dseg %x",
+                    apm_bios_info.cseg, apm_bios_info.offset,
+                    apm_bios_info.cseg_16, apm_bios_info.dseg);
+       if (apm_bios_info.version > 0x100)
+               p += sprintf(p, " cseg len %x, dseg len %x",
+                            apm_bios_info.cseg_len, apm_bios_info.dseg_len);
+       *p++ = '\n';
+       error = apm_get_power_status(&bx, &cx, &dx);
+       if (error) {
+               strcpy(p, "Power status not available\n");
+               p += strlen(p);
+               return p - buf;
+       }
+       switch ((bx >> 8) & 0xff) {
+       case 0: power_stat = "off line"; break;
+       case 1: power_stat = "on line"; break;
+       case 2: power_stat = "on backup power"; break;
+       default: power_stat = "unknown"; break;
+       }
+       switch (bx & 0xff) {
+       case 0: bat_stat = "high"; break;
+       case 1: bat_stat = "low"; break;
+       case 2: bat_stat = "critical"; break;
+       case 3: bat_stat = "charging"; break;
+       default: bat_stat = "unknown"; break;
+       }
+       p += sprintf(p, "AC: %s\nBattery status: %s\nBattery life: ",
+                    power_stat, bat_stat);
+       if ((cx & 0xff) == 0xff) {
+               strcpy(p, "unknown");
+               p += strlen(p);
+       } else
+               p += sprintf(p, "%d%%", cx & 0xff);
+       *p++ = '\n';
+       if (apm_bios_info.version > 0x100) {
+               p += sprintf(p, "Battery flag: 0x%02x\nBattery life: ",
+                            (cx >> 8) & 0xff);
+               if (dx == 0xffff) {
+                       strcpy(p, "unknown");
+                       p += strlen(p);
+               }
+               else
+                       p += sprintf(p, "%d %s", dx & 0x7fff,
+                                    ((dx & 0x8000) == 0)
+                                    ? "seconds" : "minutes");
+               *p++ = '\n';
+       }
+       return p - buf;
+}
+
+static int apm_setup(void)
+{
+       unsigned short  bx;
+       unsigned short  cx;
+       unsigned short  dx;
+       unsigned short  error;
+       char *          power_stat;
+       char *          bat_stat;
+
+       if (apm_bios_info.version == 0) {
+               printk("APM BIOS not found.\n");
+               return -1;
+       }
+       printk("APM BIOS version %c.%c Flags 0x%02x (Driver version %s)\n",
+              ((apm_bios_info.version >> 8) & 0xff) + '0',
+              (apm_bios_info.version & 0xff) + '0',
+              apm_bios_info.flags,
+              driver_version);
+       if ((apm_bios_info.flags & APM_32_BIT_SUPPORT) == 0) {
+               printk("    No 32 bit BIOS support\n");
+               return -1;
+       }
+
+       /*
+        * Fix for the Compaq Contura 3/25c which reports BIOS version 0.1
+        * but is reportedly a 1.0 BIOS.
+        */
+       if (apm_bios_info.version == 0x001)
+               apm_bios_info.version = 0x100;
+
+       printk("    Entry %x:%lx cseg16 %x dseg %x",
+              apm_bios_info.cseg, apm_bios_info.offset,
+              apm_bios_info.cseg_16, apm_bios_info.dseg);
+       if (apm_bios_info.version > 0x100)
+               printk(" cseg len %x, dseg len %x",
+                      apm_bios_info.cseg_len, apm_bios_info.dseg_len);
+       printk("\n");
+
+       apm_bios_entry.offset = apm_bios_info.offset;
+       apm_bios_entry.segment = APM_CS;
+       set_base(gdt[APM_CS >> 3],
+                0xc0000000 + ((unsigned long)apm_bios_info.cseg << 4));
+       set_base(gdt[APM_CS_16 >> 3],
+                0xc0000000 + ((unsigned long)apm_bios_info.cseg_16 << 4));
+       set_base(gdt[APM_DS >> 3],
+                0xc0000000 + ((unsigned long)apm_bios_info.dseg << 4));
+       if (apm_bios_info.version == 0x100) {
+               set_limit(gdt[APM_CS >> 3], 64 * 1024);
+               set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
+               set_limit(gdt[APM_DS >> 3], 64 * 1024);
+       } else {
+               set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
+               /*
+                * This is not clear from the spec, but at least one
+                * machine needs this to be a 64k segment.
+                */
+               set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
+               set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
+               apm_bios_info.version = 0x0101;
+               error = apm_driver_version(&apm_bios_info.version);
+               if (error != 0)
+                       apm_bios_info.version = 0x100;
+               else {
+                       apm_engage_power_management(0x0001);
+                       printk( "    Connection version %d.%d\n",
+                               (apm_bios_info.version >> 8) & 0xff,
+                               apm_bios_info.version & 0xff );
+                       apm_bios_info.version = 0x0101;
+               }
+       }
+
+       error = apm_get_power_status(&bx, &cx, &dx);
+       if (error)
+               printk("    Power status not available\n");
+       else {
+               switch ((bx >> 8) & 0xff) {
+               case 0: power_stat = "off line"; break;
+               case 1: power_stat = "on line"; break;
+               case 2: power_stat = "on backup power"; break;
+               default: power_stat = "unknown"; break;
+               }
+               switch (bx & 0xff) {
+               case 0: bat_stat = "high"; break;
+               case 1: bat_stat = "low"; break;
+               case 2: bat_stat = "critical"; break;
+               case 3: bat_stat = "charging"; break;
+               default: bat_stat = "unknown"; break;
+               }
+               printk("    AC %s, battery status %s, battery life ",
+                      power_stat, bat_stat);
+               if ((cx & 0xff) == 0xff)
+                       printk("unknown\n");
+               else
+                       printk("%d%%\n", cx & 0xff);
+               if (apm_bios_info.version > 0x100) {
+                       printk("    battery flag 0x%02x, battery life ",
+                              (cx >> 8) & 0xff);
+                       if (dx == 0xffff)
+                               printk("unknown\n");
+                       else
+                               printk("%d %s\n", dx & 0x7fff,
+                                      ((dx & 0x8000) == 0)
+                                      ? "seconds" : "minutes");
+               }
+       }
+
+#ifdef CONFIG_APM_DO_ENABLE
+       /*
+        * This call causes my NEC UltraLite Versa 33/C to hang if it is
+        * booted with PM disabled but not in the docking station.
+        * Unfortunate ...
+        */
+       error = apm_enable_power_management();
+       if (error)
+               apm_error("enable power management", error);
+#endif
+
+       init_timer(&apm_timer);
+       apm_timer.function = do_apm_timer;
+       apm_timer.expires = APM_CHECK_TIMEOUT + jiffies;
+       add_timer(&apm_timer);
+
+       register_symtab(&apm_syms);
+
+       apm_enabled = 1;
+
+       if ((apm_major = register_chrdev(0, "apm_bios", &apm_bios_fops)) < 0)
+               printk("APM BIOS: Cannot allocate major device number\n");
+
+       return 0;
+}
+
+void apm_bios_init(void)
+{
+       apm_setup();
+}
index e972bb388a9b1d6650c629beb5b558476cc829af..e4e15644cf455f4de0da524433dc697e47464ee8 100644 (file)
@@ -93,6 +93,9 @@
 #include <linux/major.h>
 #include <linux/mm.h>
 #include <linux/ioport.h>
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+#endif
 
 #include <asm/io.h>
 #include <asm/system.h>
@@ -2076,6 +2079,11 @@ void do_blank_screen(int nopowersave)
 {
        int currcons;
 
+#ifdef CONFIG_APM
+       if (apm_display_blank())
+               return;
+#endif
+
        if (console_blanked)
                return;
 
@@ -2111,6 +2119,11 @@ void do_unblank_screen(void)
        int resetorg;
        long offset;
 
+#ifdef CONFIG_APM
+       if (apm_display_unblank())
+               return;
+#endif
+
        if (!console_blanked)
                return;
        if (!vc_cons_allocated(fg_console)) {
index 48e4d7ad9a5fe9c94ceea25e6390c95b19a7ffbc..dcb25c310a23af9456deaf022f7d32f7cf069ed2 100644 (file)
@@ -503,6 +503,16 @@ static int lp_ioctl(struct inode *inode, struct file *file,
                                        memset(&LP_STAT(minor), 0, sizeof(struct lp_stats));
                        }
                        break;
+               case LPGETFLAGS:
+                       retval = verify_area(VERIFY_WRITE, (void *) arg,
+                           sizeof(int));
+                       if (retval)
+                               return retval;
+                       else {
+                               int status = LP_F(minor);
+                               memcpy_tofs((int *) arg, &status, sizeof(int));
+                       }
+                       break;
                default:
                        retval = -EINVAL;
        }
index 3f66a2288468fc4715f39dc935b21c63f23f24ff..2a932e0d72ae7526f69e132a7c5403c7c7287d7f 100644 (file)
@@ -79,9 +79,9 @@ static const char *version =
     "3c501.c: 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov).\n";
 
 /*
-  Braindamage remaining:
-  The 3c501 board.
 */
*     Braindamage remaining:
*     The 3c501 board.
+ */
 
 #include <linux/module.h>
 
@@ -110,7 +110,10 @@ static unsigned int netcard_portlist[] =
    { 0x280, 0x300, 0};
 
 \f
-/* Index to functions. */
+/*
+ *     Index to functions. 
+ */
 int el1_probe(struct device *dev);
 static int  el1_probe1(struct device *dev, int ioaddr);
 static int  el_open(struct device *dev);
@@ -129,8 +132,12 @@ static void set_multicast_list(struct device *dev);
 #endif                 /* Anything above 5 is wordy death! */
 static int el_debug = EL_DEBUG;
  
-/* Board-specific info in dev->priv. */
-struct net_local {
+/* 
+ *     Board-specific info in dev->priv. 
+ */
+struct net_local 
+{
     struct enet_statistics stats;
     int tx_pkt_start;          /* The length of the current Tx packet. */
     int collisions;            /* Tx collisions this packet */
@@ -158,7 +165,10 @@ struct net_local {
 #define EL1_SAPROM     0x0C
 #define EL1_DATAPORT   0x0f
 
-/* Writes to the ax command register. */
+/*
+ *     Writes to the ax command register.
+ */
 #define AX_OFF 0x00                    /* Irq off, buffer access on */
 #define AX_SYS  0x40                   /* Load the buffer */
 #define AX_XMIT 0x44                   /* Transmit a packet */
@@ -166,14 +176,20 @@ struct net_local {
 #define AX_LOOP        0x0C                    /* Loopback mode */
 #define AX_RESET 0x80
 
-/* Normal receive mode written to RX_STATUS.  We must intr on short packets
-   to avoid bogus rx lockups. */
+/*
+ *     Normal receive mode written to RX_STATUS.  We must intr on short packets
+ *     to avoid bogus rx lockups.
+ */
 #define RX_NORM 0xA8           /* 0x68 == all addrs, 0xA8 only to me. */
 #define RX_PROM 0x68           /* Senior Prom, uhmm promiscuous mode. */
 #define RX_MULT 0xE8           /* Accept multicast packets. */
-#define TX_NORM 0x0A   /* Interrupt on everything that might hang the chip */
+#define TX_NORM 0x0A           /* Interrupt on everything that might hang the chip */
 
-/* TX_STATUS register. */
+/*
+ *     TX_STATUS register. 
+ */
 #define TX_COLLISION 0x02
 #define TX_16COLLISIONS 0x04
 #define TX_READY 0x08
@@ -183,468 +199,551 @@ struct net_local {
 #define RX_GOOD        0x30            /* Good packet 0x20, or simple overflow 0x10. */
 \f
 
-/* The boilerplate probe code. */
+/*
+ *     The boilerplate probe code.
+ */
 #ifdef HAVE_DEVLIST
-struct netdev_entry el1_drv =
-{"3c501", el1_probe1, EL1_IO_EXTENT, netcard_portlist};
+struct netdev_entry el1_drv = {"3c501", el1_probe1, EL1_IO_EXTENT, netcard_portlist};
 #else
-int
-el1_probe(struct device *dev)
+
+int el1_probe(struct device *dev)
 {
-    int i;
-    int base_addr = dev ? dev->base_addr : 0;
-
-    if (base_addr > 0x1ff)     /* Check a single specified location. */
-       return el1_probe1(dev, base_addr);
-    else if (base_addr != 0)   /* Don't probe at all. */
-       return ENXIO;
-
-    for (i = 0; netcard_portlist[i]; i++) {
-       int ioaddr = netcard_portlist[i];
-       if (check_region(ioaddr, EL1_IO_EXTENT))
-           continue;
-       if (el1_probe1(dev, ioaddr) == 0)
-           return 0;
-    }
-
-    return ENODEV;
+       int i;
+       int base_addr = dev ? dev->base_addr : 0;
+
+       if (base_addr > 0x1ff)  /* Check a single specified location. */
+               return el1_probe1(dev, base_addr);
+       else if (base_addr != 0)        /* Don't probe at all. */
+               return ENXIO;
+
+       for (i = 0; netcard_portlist[i]; i++) 
+       {
+               int ioaddr = netcard_portlist[i];
+               if (check_region(ioaddr, EL1_IO_EXTENT))
+                       continue;
+               if (el1_probe1(dev, ioaddr) == 0)
+                       return 0;
+       }
+
+       return ENODEV;
 }
 #endif
 
-/* The actual probe. */ 
-static int
-el1_probe1(struct device *dev, int ioaddr)
+/*
+ *     The actual probe. 
+ */ 
+
+static int el1_probe1(struct device *dev, int ioaddr)
 {
-    #ifndef MODULE
-
-    const char *mname;         /* Vendor name */
-    unsigned char station_addr[6];
-    int autoirq = 0;
-    int i;
-
-    /* Read the station address PROM data from the special port.  */
-    for (i = 0; i < 6; i++) {
-       outw(i, ioaddr + EL1_DATAPTR);
-       station_addr[i] = inb(ioaddr + EL1_SAPROM);
-    }
-    /* Check the first three octets of the S.A. for 3Com's prefix, or
-       for the Sager NP943 prefix. */ 
-    if (station_addr[0] == 0x02  &&  station_addr[1] == 0x60
-       && station_addr[2] == 0x8c) {
-       mname = "3c501";
-    } else if (station_addr[0] == 0x00  &&  station_addr[1] == 0x80
-       && station_addr[2] == 0xC8) {
-       mname = "NP943";
-    } else
-       return ENODEV;
+#ifndef MODULE
 
-    /* Grab the region so we can find the another board if autoIRQ fails. */
-    request_region(ioaddr, EL1_IO_EXTENT,"3c501");
+       const char *mname;              /* Vendor name */
+       unsigned char station_addr[6];
+       int autoirq = 0;
+       int i;
 
-    /* We auto-IRQ by shutting off the interrupt line and letting it float
-       high. */
-    if (dev->irq < 2) {
+       /*
+        *      Read the station address PROM data from the special port.  
+        */
+        
+       for (i = 0; i < 6; i++) 
+       {
+               outw(i, ioaddr + EL1_DATAPTR);
+               station_addr[i] = inb(ioaddr + EL1_SAPROM);
+       }
+       /*
+        *      Check the first three octets of the S.A. for 3Com's prefix, or
+        *      for the Sager NP943 prefix. 
+        */ 
+        
+       if (station_addr[0] == 0x02  &&  station_addr[1] == 0x60
+               && station_addr[2] == 0x8c) 
+       {
+               mname = "3c501";
+       } else if (station_addr[0] == 0x00  &&  station_addr[1] == 0x80
+       && station_addr[2] == 0xC8) 
+       {
+               mname = "NP943";
+       }
+       else
+               return ENODEV;
 
-       autoirq_setup(2);
+       /*
+        *      Grab the region so we can find the another board if autoIRQ fails. 
+        */
 
-       inb(RX_STATUS);         /* Clear pending interrupts. */
-       inb(TX_STATUS);
-       outb(AX_LOOP + 1, AX_CMD);
+       request_region(ioaddr, EL1_IO_EXTENT,"3c501");
 
-       outb(0x00, AX_CMD);
+       /*      
+        *      We auto-IRQ by shutting off the interrupt line and letting it float
+        *      high.
+        */
 
-       autoirq = autoirq_report(1);
+       if (dev->irq < 2) 
+       {
+               autoirq_setup(2);
+               inb(RX_STATUS);         /* Clear pending interrupts. */
+               inb(TX_STATUS);
+               outb(AX_LOOP + 1, AX_CMD);
 
-       if (autoirq == 0) {
-           printk("%s probe at %#x failed to detect IRQ line.\n",
-                  mname, ioaddr);
-           return EAGAIN;
+               outb(0x00, AX_CMD);
+       
+               autoirq = autoirq_report(1);
+
+               if (autoirq == 0) 
+               {
+                       printk("%s probe at %#x failed to detect IRQ line.\n",
+                               mname, ioaddr);
+                       return EAGAIN;
+               }
        }
-    }
 
-    outb(AX_RESET+AX_LOOP, AX_CMD);                    /* Loopback mode. */
+       outb(AX_RESET+AX_LOOP, AX_CMD);                 /* Loopback mode. */
+       dev->base_addr = ioaddr;
+       memcpy(dev->dev_addr, station_addr, ETH_ALEN);
 
-    dev->base_addr = ioaddr;
-    memcpy(dev->dev_addr, station_addr, ETH_ALEN);
-    if (dev->mem_start & 0xf)
-       el_debug = dev->mem_start & 0x7;
-    if (autoirq)
-       dev->irq = autoirq;
+       if (dev->mem_start & 0xf)
+               el_debug = dev->mem_start & 0x7;
+       if (autoirq)
+               dev->irq = autoirq;
 
-    printk("%s: %s EtherLink at %#lx, using %sIRQ %d.\n",
-          dev->name, mname, dev->base_addr,
-          autoirq ? "auto":"assigned ", dev->irq);
+       printk("%s: %s EtherLink at %#lx, using %sIRQ %d.\n", dev->name, mname, dev->base_addr,
+                       autoirq ? "auto":"assigned ", dev->irq);
           
 #ifdef CONFIG_IP_MULTICAST
-    printk("WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n");
+       printk("WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n");
 #endif    
 
-    if (el_debug)
-       printk("%s", version);
+       if (el_debug)
+               printk("%s", version);
+
+       /*
+        *      Initialize the device structure. 
+        */
+        
+       dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+       if (dev->priv == NULL)
+               return -ENOMEM;
+       memset(dev->priv, 0, sizeof(struct net_local));
+
+       /*
+        *      The EL1-specific entries in the device structure. 
+        */
+        
+       dev->open = &el_open;
+       dev->hard_start_xmit = &el_start_xmit;
+       dev->stop = &el1_close;
+       dev->get_stats = &el1_get_stats;
+       dev->set_multicast_list = &set_multicast_list;
 
-    /* Initialize the device structure. */
-    dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
-    if (dev->priv == NULL)
-       return -ENOMEM;
-    memset(dev->priv, 0, sizeof(struct net_local));
+       /*
+        *      Setup the generic properties 
+        */
 
-    /* The EL1-specific entries in the device structure. */
-    dev->open = &el_open;
-    dev->hard_start_xmit = &el_start_xmit;
-    dev->stop = &el1_close;
-    dev->get_stats = &el1_get_stats;
-    dev->set_multicast_list = &set_multicast_list;
-    /* Setup the generic properties */
-    ether_setup(dev);
+       ether_setup(dev);
 
 #endif /* !MODULE */
-    return 0;
+
+       return 0;
 }
 
-/* Open/initialize the board. */
-static int
-el_open(struct device *dev)
+/*
+ *     Open/initialize the board. 
+ */
+static int el_open(struct device *dev)
 {
-    int ioaddr = dev->base_addr;
+       int ioaddr = dev->base_addr;
 
-    if (el_debug > 2)
-       printk("%s: Doing el_open()...", dev->name);
+       if (el_debug > 2)
+               printk("%s: Doing el_open()...", dev->name);
 
-    if (request_irq(dev->irq, &el_interrupt, 0, "3c501")) {
-       return -EAGAIN;
-    }
-    irq2dev_map[dev->irq] = dev;
+       if (request_irq(dev->irq, &el_interrupt, 0, "3c501")) 
+               return -EAGAIN;
 
-    el_reset(dev);
+       irq2dev_map[dev->irq] = dev;
+       el_reset(dev);
 
-    dev->start = 1;
+       dev->start = 1;
 
-    outb(AX_RX, AX_CMD);       /* Aux control, irq and receive enabled */
-    MOD_INC_USE_COUNT;
-    return 0;
+       outb(AX_RX, AX_CMD);    /* Aux control, irq and receive enabled */
+       MOD_INC_USE_COUNT;
+       return 0;
 }
 
-static int
-el_start_xmit(struct sk_buff *skb, struct device *dev)
+static int el_start_xmit(struct sk_buff *skb, struct device *dev)
 {
-    struct net_local *lp = (struct net_local *)dev->priv;
-    int ioaddr = dev->base_addr;
-    unsigned long flags;
-
-    if (dev->tbusy) {
-       if (jiffies - dev->trans_start < 20) {
-           if (el_debug > 2)
-               printk(" transmitter busy, deferred.\n");
-           return 1;
+       struct net_local *lp = (struct net_local *)dev->priv;
+       int ioaddr = dev->base_addr;
+       unsigned long flags;
+
+       if (dev->tbusy) 
+       {
+               if (jiffies - dev->trans_start < 20) 
+               {
+                       if (el_debug > 2)
+                               printk(" transmitter busy, deferred.\n");
+                       return 1;
+               }
+               if (el_debug)
+                       printk ("%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n",
+                               dev->name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS));
+               lp->stats.tx_errors++;
+               outb(TX_NORM, TX_CMD);
+               outb(RX_NORM, RX_CMD);
+               outb(AX_OFF, AX_CMD);   /* Just trigger a false interrupt. */
+               outb(AX_RX, AX_CMD);    /* Aux control, irq and receive enabled */
+               dev->tbusy = 0;
+               dev->trans_start = jiffies;
        }
-       if (el_debug)
-           printk ("%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n",
-                   dev->name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS));
-       lp->stats.tx_errors++;
-       outb(TX_NORM, TX_CMD);
-       outb(RX_NORM, RX_CMD);
-       outb(AX_OFF, AX_CMD);   /* Just trigger a false interrupt. */
-       outb(AX_RX, AX_CMD);    /* Aux control, irq and receive enabled */
-       dev->tbusy = 0;
-       dev->trans_start = jiffies;
-    }
 
-    if (skb == NULL) {
-       dev_tint(dev);
-       return 0;
-    }
-
-    save_flags(flags);
-    /* Avoid incoming interrupts between us flipping tbusy and flipping
-       mode as the driver assumes tbusy is a faithful indicator of card
-       state */
-    cli();
-    /* Avoid timer-based retransmission conflicts. */
-    if (set_bit(0, (void*)&dev->tbusy) != 0)
-    {
-       restore_flags(flags);
-       printk("%s: Transmitter access conflict.\n", dev->name);
-    }
-    else {
-       int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
-       unsigned char *buf = skb->data;
+       if (skb == NULL) 
+       {
+               dev_tint(dev);
+               return 0;
+       }
 
-load_it_again_sam:
-       lp->tx_pkt_start = gp_start;
-       lp->collisions = 0;
+       save_flags(flags);
 
        /*
-        *      Command mode with status cleared should [in theory]
-        *      mean no more interrupts can be pending on the card.
+        *      Avoid incoming interrupts between us flipping tbusy and flipping
+        *      mode as the driver assumes tbusy is a faithful indicator of card
+        *      state
         */
-       outb(AX_SYS, AX_CMD);
-       inb(RX_STATUS);
-       inb(TX_STATUS);
-       
-       lp->loading=1;
+        
+       cli();
        
-       /* 
-        *      Turn interrupts back on while we spend a pleasant afternoon
-        *      loading bytes into the board 
+       /*
+        *      Avoid timer-based retransmission conflicts. 
         */
-       restore_flags(flags);
-       outw(0x00, RX_BUF_CLR);         /* Set rx packet area to 0. */
-       outw(gp_start, GP_LOW);         /* aim - packet will be loaded into buffer start */
-       outsb(DATAPORT,buf,skb->len);   /* load buffer (usual thing each byte increments the pointer) */
-       outw(gp_start, GP_LOW);         /* the board reuses the same register */
-       if(lp->loading==2)              /* A receive upset our load, despite our best efforts */
+        
+       if (set_bit(0, (void*)&dev->tbusy) != 0)
        {
-               if(el_debug>2)
-                       printk("%s: burped during tx load.\n", dev->name);
-               goto load_it_again_sam; /* Sigh... */
+               restore_flags(flags);
+               printk("%s: Transmitter access conflict.\n", dev->name);
        }
-       outb(AX_XMIT, AX_CMD);          /* fire ... Trigger xmit.  */
-       dev->trans_start = jiffies;
-    }
-
-    if (el_debug > 2)
-       printk(" queued xmit.\n");
-    dev_kfree_skb (skb, FREE_WRITE);
-    return 0;
-}
-\f
+       else
+       {
+               int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
+               unsigned char *buf = skb->data;
 
-/* The typical workload of the driver:
-   Handle the ether interface interrupts. */
-static void
-el_interrupt(int irq, struct pt_regs *regs)
-{
-    struct device *dev = (struct device *)(irq2dev_map[irq]);
-    struct net_local *lp;
-    int ioaddr;
-    int axsr;                  /* Aux. status reg. */
+load_it_again_sam:
+               lp->tx_pkt_start = gp_start;
+               lp->collisions = 0;
 
-    if (dev == NULL  ||  dev->irq != irq) {
-       printk ("3c501 driver: irq %d for unknown device.\n", irq);
-       return;
-    }
+               /*
+                *      Command mode with status cleared should [in theory]
+                *      mean no more interrupts can be pending on the card.
+                */
+       
+               outb(AX_SYS, AX_CMD);
+               inb(RX_STATUS);
+               inb(TX_STATUS);
+       
+               lp->loading=1;
+       
+               /* 
+                *      Turn interrupts back on while we spend a pleasant afternoon
+                *      loading bytes into the board 
+                */
+
+               restore_flags(flags);
+               outw(0x00, RX_BUF_CLR);         /* Set rx packet area to 0. */
+               outw(gp_start, GP_LOW);         /* aim - packet will be loaded into buffer start */
+               outsb(DATAPORT,buf,skb->len);   /* load buffer (usual thing each byte increments the pointer) */
+               outw(gp_start, GP_LOW);         /* the board reuses the same register */
+               if(lp->loading==2)              /* A receive upset our load, despite our best efforts */
+               {
+                       if(el_debug>2)
+                               printk("%s: burped during tx load.\n", dev->name);
+                       goto load_it_again_sam; /* Sigh... */
+               }
+               outb(AX_XMIT, AX_CMD);          /* fire ... Trigger xmit.  */
+               dev->trans_start = jiffies;
+       }
 
-    ioaddr = dev->base_addr;
-    lp = (struct net_local *)dev->priv;
-    axsr = inb(AX_STATUS);
+       if (el_debug > 2)
+               printk(" queued xmit.\n");
+       dev_kfree_skb (skb, FREE_WRITE);
+       return 0;
+}
+\f
 
-    if (el_debug > 3)
-      printk("%s: el_interrupt() aux=%#02x", dev->name, axsr);
-    if (dev->interrupt)
-       printk("%s: Reentering the interrupt driver!\n", dev->name);
-    dev->interrupt = 1;
-    
-    lp->loading=2;             /* So we can spot loading interruptions */
+/*
+ *     The typical workload of the driver:
+ *     Handle the ether interface interrupts. 
+ */
 
-    if (dev->tbusy) {
-    
-       /*
-        *      Board in transmit mode.
-        */
-        
-       int txsr = inb(TX_STATUS);
+static void el_interrupt(int irq, struct pt_regs *regs)
+{
+       struct device *dev = (struct device *)(irq2dev_map[irq]);
+       struct net_local *lp;
+       int ioaddr;
+       int axsr;                       /* Aux. status reg. */
 
-       if (el_debug > 6)
-           printk(" txsr=%02x gp=%04x rp=%04x", txsr, inw(GP_LOW),
-                  inw(RX_LOW));
+       if (dev == NULL  ||  dev->irq != irq) 
+       {
+               printk ("3c501 driver: irq %d for unknown device.\n", irq);
+               return;
+       }
+
+       ioaddr = dev->base_addr;
+       lp = (struct net_local *)dev->priv;
 
-       if ((axsr & 0x80) && (txsr & TX_READY) == 0) {
-       /*
-        *      FIXME: is there a logic to whether to keep on trying or
-        *      reset immediately ?
-        */
-           printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x"
-                  " gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
-                  inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR));
-           dev->tbusy = 0;
-           mark_bh(NET_BH);
-       } else if (txsr & TX_16COLLISIONS) {
-       /*
-        *      Timed out
-        */
-           if (el_debug)
-               printk("%s: Transmit failed 16 times, ethernet jammed?\n",
-                      dev->name);
-           outb(AX_SYS, AX_CMD);
-           lp->stats.tx_aborted_errors++;
-       } else if (txsr & TX_COLLISION) {       /* Retrigger xmit. */
-           if (el_debug > 6)
-               printk(" retransmitting after a collision.\n");
-       /*
-        *      Poor little chip can't reset its own start pointer
-        */
-           outb(AX_SYS, AX_CMD);
-           outw(lp->tx_pkt_start, GP_LOW);
-           outb(AX_XMIT, AX_CMD);
-           lp->stats.collisions++;
-           dev->interrupt = 0;
-           return;
-       } else {
        /*
-        *      It worked.. we will now fall through and receive
+        *      What happened ?
         */
-           lp->stats.tx_packets++;
-           if (el_debug > 6)
-               printk(" Tx succeeded %s\n",
-                      (txsr & TX_RDY) ? "." : "but tx is busy!");
+        
+       axsr = inb(AX_STATUS);
+
        /*
-        *      This is safe the interrupt is atomic WRT itself.
+        *      Log it
         */
-           dev->tbusy = 0;
-           mark_bh(NET_BH);    /* In case more to transmit */
-       }
-    } else {
+
+       if (el_debug > 3)
+               printk("%s: el_interrupt() aux=%#02x", dev->name, axsr);
+       if (dev->interrupt)
+               printk("%s: Reentering the interrupt driver!\n", dev->name);
+       dev->interrupt = 1;
     
-       /*
-        *      In receive mode.
-        */
+       lp->loading=2;          /* So we can spot loading interruptions */
+
+       if (dev->tbusy) 
+       {
+    
+               /*
+                *      Board in transmit mode.
+                */
         
-       int rxsr = inb(RX_STATUS);
-       if (el_debug > 5)
-           printk(" rxsr=%02x txsr=%02x rp=%04x", rxsr, inb(TX_STATUS),
-                  inw(RX_LOW));
+               int txsr = inb(TX_STATUS);
+
+               if (el_debug > 6)
+                       printk(" txsr=%02x gp=%04x rp=%04x", txsr, inw(GP_LOW),inw(RX_LOW));
+
+               if ((axsr & 0x80) && (txsr & TX_READY) == 0) 
+               {
+                       /*
+                        *      FIXME: is there a logic to whether to keep on trying or
+                        *      reset immediately ?
+                        */
+                       printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x"
+                          " gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
+                       inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR));
+                       dev->tbusy = 0;
+                       mark_bh(NET_BH);
+               } 
+               else if (txsr & TX_16COLLISIONS) 
+               {
+                       /*
+                        *      Timed out
+                        */
+                       if (el_debug)
+                               printk("%s: Transmit failed 16 times, ethernet jammed?\n",dev->name);
+                       outb(AX_SYS, AX_CMD);
+                       lp->stats.tx_aborted_errors++;
+               }
+               else if (txsr & TX_COLLISION) 
+               {       
+                       /*
+                        *      Retrigger xmit. 
+                        */
+                        
+                       if (el_debug > 6)
+                               printk(" retransmitting after a collision.\n");
+                       /*
+                        *      Poor little chip can't reset its own start pointer
+                        */
+                       
+                       outb(AX_SYS, AX_CMD);
+                       outw(lp->tx_pkt_start, GP_LOW);
+                       outb(AX_XMIT, AX_CMD);
+                       lp->stats.collisions++;
+                       dev->interrupt = 0;
+                       return;
+               }
+               else
+               {
+                       /*
+                        *      It worked.. we will now fall through and receive
+                        */
+                       lp->stats.tx_packets++;
+                       if (el_debug > 6)
+                               printk(" Tx succeeded %s\n",
+                                       (txsr & TX_RDY) ? "." : "but tx is busy!");
+                       /*
+                        *      This is safe the interrupt is atomic WRT itself.
+                        */
+
+                       dev->tbusy = 0;
+                       mark_bh(NET_BH);        /* In case more to transmit */
+               }
+       }
+       else
+       {
+               /*
+                *      In receive mode.
+                */
+        
+               int rxsr = inb(RX_STATUS);
+               if (el_debug > 5)
+                       printk(" rxsr=%02x txsr=%02x rp=%04x", rxsr, inb(TX_STATUS),inw(RX_LOW));
+               /*
+                *      Just reading rx_status fixes most errors. 
+                */
+               if (rxsr & RX_MISSED)
+                       lp->stats.rx_missed_errors++;
+               if (rxsr & RX_RUNT) 
+               {       /* Handled to avoid board lock-up. */
+                       lp->stats.rx_length_errors++;
+                       if (el_debug > 5) 
+                               printk(" runt.\n");
+               } 
+               else if (rxsr & RX_GOOD) 
+               {
+                       /*
+                        *      Receive worked.
+                        */
+                       el_receive(dev);
+               }
+               else
+               {
+                       /*
+                        *      Nothing?  Something is broken!
+                        */
+                       if (el_debug > 2)
+                               printk("%s: No packet seen, rxsr=%02x **resetting 3c501***\n",
+                                       dev->name, rxsr);
+                       el_reset(dev);
+               }
+               if (el_debug > 3)
+                       printk(".\n");
+       }
 
        /*
-        *      Just reading rx_status fixes most errors. 
-        */
-       if (rxsr & RX_MISSED)
-           lp->stats.rx_missed_errors++;
-       if (rxsr & RX_RUNT) {   /* Handled to avoid board lock-up. */
-           lp->stats.rx_length_errors++;
-           if (el_debug > 5) printk(" runt.\n");
-       } else if (rxsr & RX_GOOD) {
-       /*
-        *      Receive worked.
+        *      Move into receive mode 
         */
-           el_receive(dev);
-       } else {                        /* Nothing?  Something is broken! */
-           if (el_debug > 2)
-               printk("%s: No packet seen, rxsr=%02x **resetting 3c501***\n",
-                      dev->name, rxsr);
-           el_reset(dev);
-       }
-       if (el_debug > 3)
-           printk(".\n");
-    }
-
-    /*
-     * Move into receive mode 
-     */
-    outb(AX_RX, AX_CMD);
-    outw(0x00, RX_BUF_CLR);
-    inb(RX_STATUS);            /* Be certain that interrupts are cleared. */
-    inb(TX_STATUS);
-    dev->interrupt = 0;
-    return;
+
+       outb(AX_RX, AX_CMD);
+       outw(0x00, RX_BUF_CLR);
+       inb(RX_STATUS);         /* Be certain that interrupts are cleared. */
+       inb(TX_STATUS);
+       dev->interrupt = 0;
+       return;
 }
 
 
-/* We have a good packet. Well, not really "good", just mostly not broken.
-   We must check everything to see if it is good. */
-static void
-el_receive(struct device *dev)
+/*
+ *     We have a good packet. Well, not really "good", just mostly not broken.
+ *     We must check everything to see if it is good. 
+ */
+
+static void el_receive(struct device *dev)
 {
-    struct net_local *lp = (struct net_local *)dev->priv;
-    int ioaddr = dev->base_addr;
-    int pkt_len;
-    struct sk_buff *skb;
+       struct net_local *lp = (struct net_local *)dev->priv;
+       int ioaddr = dev->base_addr;
+       int pkt_len;
+       struct sk_buff *skb;
 
-    pkt_len = inw(RX_LOW);
+       pkt_len = inw(RX_LOW);
 
-    if (el_debug > 4)
-      printk(" el_receive %d.\n", pkt_len);
+       if (el_debug > 4)
+               printk(" el_receive %d.\n", pkt_len);
 
-    if ((pkt_len < 60)  ||  (pkt_len > 1536)) {
-       if (el_debug)
-         printk("%s: bogus packet, length=%d\n", dev->name, pkt_len);
-       lp->stats.rx_over_errors++;
-       return;
-    }
+       if ((pkt_len < 60)  ||  (pkt_len > 1536)) 
+       {
+               if (el_debug)
+                       printk("%s: bogus packet, length=%d\n", dev->name, pkt_len);
+               lp->stats.rx_over_errors++;
+               return;
+       }
     
-    /*
-     * Command mode so we can empty the buffer
-     */
+       /*
+        *      Command mode so we can empty the buffer
+        */
      
-    outb(AX_SYS, AX_CMD);
-
-    skb = dev_alloc_skb(pkt_len+2);
-    /*
-     * Start of frame
-     */
-    outw(0x00, GP_LOW);
-    if (skb == NULL) {
-       printk("%s: Memory squeeze, dropping packet.\n", dev->name);
-       lp->stats.rx_dropped++;
-       return;
-    } else {
-       skb_reserve(skb,2);     /* Force 16 byte alignment */
-       skb->dev = dev;
+       outb(AX_SYS, AX_CMD);
+       skb = dev_alloc_skb(pkt_len+2);
 
        /*
-        *      The read increments through the bytes. The interrupt
-        *      handler will fix the pointer when it returns to 
-        *      receive mode.
+        *      Start of frame
         */
-        
-       insb(DATAPORT, skb_put(skb,pkt_len), pkt_len);
-       skb->protocol=eth_type_trans(skb,dev);
-       netif_rx(skb);
-       lp->stats.rx_packets++;
-    }
-    return;
+
+       outw(0x00, GP_LOW);
+       if (skb == NULL) 
+       {
+               printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+               lp->stats.rx_dropped++;
+               return;
+       }
+       else
+       {
+               skb_reserve(skb,2);     /* Force 16 byte alignment */
+               skb->dev = dev;
+               /*
+                *      The read increments through the bytes. The interrupt
+                *      handler will fix the pointer when it returns to 
+                *      receive mode.
+                */
+               insb(DATAPORT, skb_put(skb,pkt_len), pkt_len);
+               skb->protocol=eth_type_trans(skb,dev);
+               netif_rx(skb);
+               lp->stats.rx_packets++;
+       }
+       return;
 }
 
-static void 
-el_reset(struct device *dev)
+static void  el_reset(struct device *dev)
 {
-    int ioaddr = dev->base_addr;
+       int ioaddr = dev->base_addr;
 
-    if (el_debug> 2)
-       printk("3c501 reset...");
-    outb(AX_RESET, AX_CMD);    /* Reset the chip */
-    outb(AX_LOOP, AX_CMD);     /* Aux control, irq and loopback enabled */
-    {
-       int i;
-       for (i = 0; i < 6; i++) /* Set the station address. */
-           outb(dev->dev_addr[i], ioaddr + i);
-    }
+       if (el_debug> 2)
+               printk("3c501 reset...");
+       outb(AX_RESET, AX_CMD);         /* Reset the chip */
+       outb(AX_LOOP, AX_CMD);          /* Aux control, irq and loopback enabled */
+       {
+               int i;
+               for (i = 0; i < 6; i++) /* Set the station address. */
+                       outb(dev->dev_addr[i], ioaddr + i);
+       }
     
-    outw(0, RX_BUF_CLR);               /* Set rx packet area to 0. */
-    cli();                     /* Avoid glitch on writes to CMD regs */
-    outb(TX_NORM, TX_CMD);             /* tx irq on done, collision */
-    outb(RX_NORM, RX_CMD);     /* Set Rx commands. */
-    inb(RX_STATUS);            /* Clear status. */
-    inb(TX_STATUS);
-    dev->interrupt = 0;
-    dev->tbusy = 0;
-    sti();
+       outw(0, RX_BUF_CLR);            /* Set rx packet area to 0. */
+       cli();                          /* Avoid glitch on writes to CMD regs */
+       outb(TX_NORM, TX_CMD);          /* tx irq on done, collision */
+       outb(RX_NORM, RX_CMD);          /* Set Rx commands. */
+       inb(RX_STATUS);                 /* Clear status. */
+       inb(TX_STATUS);
+       dev->interrupt = 0;
+       dev->tbusy = 0;
+       sti();
 }
 
-static int
-el1_close(struct device *dev)
+static int el1_close(struct device *dev)
 {
-    int ioaddr = dev->base_addr;
+       int ioaddr = dev->base_addr;
 
-    if (el_debug > 2)
-       printk("%s: Shutting down ethercard at %#x.\n", dev->name, ioaddr);
+       if (el_debug > 2)
+               printk("%s: Shutting down ethercard at %#x.\n", dev->name, ioaddr);
 
-    dev->tbusy = 1;
-    dev->start = 0;
+       dev->tbusy = 1;
+       dev->start = 0;
 
-    /* Free and disable the IRQ. */
-    free_irq(dev->irq);
-    outb(AX_RESET, AX_CMD);    /* Reset the chip */
-    irq2dev_map[dev->irq] = 0;
+       /*
+        *      Free and disable the IRQ. 
+        */
 
-    MOD_DEC_USE_COUNT;
-    return 0;
+       free_irq(dev->irq);
+       outb(AX_RESET, AX_CMD);         /* Reset the chip */
+       irq2dev_map[dev->irq] = 0;
+
+       MOD_DEC_USE_COUNT;
+       return 0;
 }
 
-static struct enet_statistics *
-el1_get_stats(struct device *dev)
+static struct enet_statistics *el1_get_stats(struct device *dev)
 {
-    struct net_local *lp = (struct net_local *)dev->priv;
-    return &lp->stats;
+       struct net_local *lp = (struct net_local *)dev->priv;
+       return &lp->stats;
 }
 
 /*
@@ -674,18 +773,21 @@ static void set_multicast_list(struct device *dev)
 }
 
 #ifdef MODULE
+
 static char devicename[9] = { 0, };
-static struct device dev_3c501 = {
+
+static struct device dev_3c501 = 
+{
        devicename, /* device name is inserted by linux/drivers/net/net_init.c */
        0, 0, 0, 0,
        0x280, 5,
-       0, 0, 0, NULL, el1_probe };
+       0, 0, 0, NULL, el1_probe 
+};
 
 static int io=0x280;
 static int irq=5;
        
-int
-init_module(void)
+int init_module(void)
 {
        dev_3c501.irq=irq;
        dev_3c501.base_addr=io;
@@ -694,19 +796,27 @@ init_module(void)
        return 0;
 }
 
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
-       /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+       /*
+        *      No need to check MOD_IN_USE, as sys_delete_module() checks.
+        */
+        
        unregister_netdev(&dev_3c501);
 
-       /* Free up the private structure, or leak memory :-)  */
+       /*
+        *      Free up the private structure, or leak memory :-) 
+        */
+        
        kfree(dev_3c501.priv);
        dev_3c501.priv = NULL;  /* gets re-allocated by el1_probe1 */
 
-       /* If we don't do this, we can't re-insmod it later. */
+       /*
+        *      If we don't do this, we can't re-insmod it later. 
+        */
        release_region(dev_3c501.base_addr, EL1_IO_EXTENT);
 }
+
 #endif /* MODULE */
 \f
 /*
index 908e1907f1f3d0e9f57ab7be7d6ee5896b9f3e44..f5084b9f20f4b703eca57dadbad4fe7572f2d7d2 100644 (file)
          
        **********************
 
+       v2.21 ALPHA (95/11/29)
+         - "Unknown protocol ID" messages now also indicate the station
+           which sent the unrecognized packet, to aid in debugging network
+           confusion.  Also, if anyone knows why Novell servers send packets
+           with protocol ID 0xEC, be sure to tell me.  For now they're
+           ignored.
+         - Rearranged ARC_P_* handling a bit, so it makes slightly more
+           sense.
+         - We were clearing irq2dev_map too soon, and causing spurious
+           "irq %d for unknown device" messages.  Moved all the set/clear
+           irq2dev_map operations to more intelligent places.
+         - 1.2.x kernels really didn't work with 2.20 ALPHA.  Maybe this
+           will fix it.
+         - Fixed the setting of set_multicast_list.  Since we don't have
+           multicast support, there's no point in using this at all.
+
        v2.20 ALPHA (95/11/12)
          - Added a bit of protocol confusion to the arc0 code to allow
            trxnet-compatible IPX support - and the use of all that new
 */
 
 static const char *version =
- "arcnet.c: v2.20 ALPHA 95/11/12 Avery Pennarun <apenwarr@foxnet.net>\n";
+ "arcnet.c: v2.21 ALPHA 95/11/29 Avery Pennarun <apenwarr@foxnet.net>\n";
 
  
 
@@ -166,9 +182,20 @@ static const char *version =
 
 /* are we Linux 1.2.x? */
 #if LINUX_VERSION_CODE < 0x10300
-#define LINUX12
+# define LINUX12
 #endif
 
+/* for older kernels with older module.h */
+#ifdef LINUX12 
+# ifdef MODULE
+   char kernel_version[] = UTS_RELEASE;
+# else
+#  undef  MOD_INC_USE_COUNT
+#  define MOD_INC_USE_COUNT
+#  undef  MOD_DEC_USE_COUNT
+#  define MOD_DEC_USE_COUNT
+# endif
+#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -414,6 +441,7 @@ extern struct device *irq2dev_map[16];
 #define ARC_P_ARP      213             /* 0xD5 */
 #define ARC_P_RARP     214             /* 0xD6 */
 #define ARC_P_IPX      250             /* 0xFA */
+#define ARC_P_NOVELL_EC        236             /* 0xEC */
 
        /* Old RFC1051 Protocol ID's */
 #define ARC_P_IP_RFC1051 240           /* 0xF0 */
@@ -575,8 +603,9 @@ static void arcnetS_rx(struct device *dev,u_char *buf,
        int length,u_char saddr, u_char daddr);
 
 static struct enet_statistics *arcnet_get_stats(struct device *dev);
+/*
 static void set_multicast_list(struct device *dev);
-
+*/
        /* functions for header/arp/etc building */
 #ifdef LINUX12
 int arcnetA_header(unsigned char *buff,struct device *dev,
@@ -745,8 +774,10 @@ arcnet_probe(struct device *dev)
                        printk("%6s: unable to get IRQ %d (irqval=%d).\n",
                                dev->name,dev->irq, irqval);
                        return EAGAIN;
-                }
-        }
+               }
+               
+               irq2dev_map[dev->irq]=dev;
+       }
         
        /* Grab the region so we can find another board if autoIRQ fails. */
        request_region(dev->base_addr, ARCNET_TOTAL_SIZE,"arcnet");
@@ -765,9 +796,7 @@ arcnet_probe(struct device *dev)
        dev->stop=arcnet_close;
        dev->hard_start_xmit=arcnetA_send_packet;
        dev->get_stats=arcnet_get_stats;
-#ifdef HAVE_MULTICAST
-       dev->set_multicast_list = &set_multicast_list;
-#endif
+       /*dev->set_multicast_list = &set_multicast_list;*/
 
        /* Fill in the fields of the device structure with generic
         * values.
@@ -1097,7 +1126,7 @@ static int arcnetS_init(struct device *dev)
 #ifdef LINUX12
        dev->type_trans=arcnetS_type_trans;
 #endif
-       BUGMSG(D_EXTRA,"ARCnet RFC1051 (NetBsd, AmiTCP) protocol initialized.\n");
+       BUGMSG(D_EXTRA,"ARCnet RFC1051 (NetBSD, AmiTCP) protocol initialized.\n");
 
        return 0;
 }
@@ -1132,8 +1161,6 @@ arcnet_open(struct device *dev)
 
        BUGLVL(D_EXTRA) printk(version);
 
-       irq2dev_map[dev->irq] = dev;
-
        BUGMSG(D_EXTRA,"arcnet_open: resetting card.\n");
        
        /* try to reset - twice if it fails the first time */
@@ -1198,11 +1225,7 @@ arcnet_close(struct device *dev)
        
        TBUSY=1;
        START=0;
-       
-       /* very important! */
-       irq2dev_map[dev->irq] = NULL;
 
-       
        /* Flush TX and disable RX */
        outb(0,INTMASK);        /* no IRQ's (except RESET, of course) */
        outb(NOTXcmd,COMMAND);  /* stop transmit */
@@ -1883,6 +1906,8 @@ static void
 arcnet_interrupt(int irq,struct pt_regs *regs)
 {
        struct device *dev = (struct device *)(irq2dev_map[irq]);
+       
+       BUGMSG(D_DURING,"in arcnet_interrupt\n");
 
        if (dev==NULL)
        {
@@ -1915,8 +1940,8 @@ arcnet_inthandler(struct device *dev)
        outb(0,INTMASK);
        INTERRUPT = 1;
 
-       BUGMSG(D_DURING,"in net_interrupt (status=%Xh)\n",inb(STATUS));
-               
+       BUGMSG(D_DURING,"in arcnet_inthandler (status=%Xh)\n",inb(STATUS));
+
 #if 1 /* Whatever you do, don't set this to 0. */
        do
        {
@@ -2212,6 +2237,7 @@ arcnet_rx(struct device *dev,int recbuf)
        case ARC_P_ARP:
        case ARC_P_RARP:
        case ARC_P_IPX:
+       case ARC_P_NOVELL_EC:
                arcnetA_rx(lp->adev,arcsoft,length,saddr,daddr);
                break;
        case ARC_P_ETHER:
@@ -2221,9 +2247,10 @@ arcnet_rx(struct device *dev,int recbuf)
        case ARC_P_ARP_RFC1051:
                arcnetS_rx(lp->sdev,arcsoft,length,saddr,daddr);
                break;
+       case ARC_P_LANSOFT: /* don't understand.  fall through. */
        default:
-               printk("%6s: received unknown protocol %d (%Xh)\n",
-                       dev->name,arcsoft[0],arcsoft[0]);
+               printk("%6s: received unknown protocol %d (%Xh) from station %d.\n",
+                       dev->name,arcsoft[0],arcsoft[0],saddr);
                lp->stats.rx_errors++;
                lp->stats.rx_crc_errors++;
                break;
@@ -2683,6 +2710,7 @@ arcnet_get_stats(struct device *dev)
        return &lp->stats;
 }
 
+#if 0
 /* Set or clear the multicast filter for this adaptor.
  * num_addrs == -1     Promiscuous mode, receive all packets
  * num_addrs == 0      Normal mode, clear multicast list
@@ -2692,7 +2720,7 @@ arcnet_get_stats(struct device *dev)
 static void
 set_multicast_list(struct device *dev)
 {
-#if 0    /* no promiscuous mode at all on most (all?) ARCnet models */
+#if 0    /* no promiscuous mode at all on most ARCnet models */
        struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
 
        short ioaddr = dev->base_addr;
@@ -2702,6 +2730,7 @@ set_multicast_list(struct device *dev)
                outw(99, ioaddr);               /* Disable promiscuous mode, use normal mode */
 #endif
 }
+#endif
 
 /* Create the ARCnet ClientData header for an arbitrary protocol layer
  *
@@ -2962,9 +2991,10 @@ unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev)
        case ARC_P_IP:          return htons(ETH_P_IP);
        case ARC_P_ARP:         return htons(ETH_P_ARP);
        case ARC_P_RARP:        return htons(ETH_P_RARP);
-       case ARC_P_IPX:         return htons(ETH_P_802_3);
-       case ARC_P_ATALK:   return htons(ETH_P_ATALK); /* untested appletalk */
-       case ARC_P_LANSOFT: /* don't understand.  fall through. */
+
+       case ARC_P_IPX:
+       case ARC_P_NOVELL_EC:
+               return htons(ETH_P_802_3);
        default:
                BUGMSG(D_EXTRA,"received packet of unknown protocol id %d (%Xh)\n",
                                head->protocol_id,head->protocol_id);
@@ -3005,6 +3035,7 @@ unsigned short arcnetS_type_trans(struct sk_buff *skb,struct device *dev)
        {
        case ARC_P_IP_RFC1051:  return htons(ETH_P_IP);
        case ARC_P_ARP_RFC1051: return htons(ETH_P_ARP);
+       case ARC_P_ATALK:   return htons(ETH_P_ATALK); /* untested appletalk */
        default:
                BUGMSG(D_EXTRA,"received packet of unknown protocol id %d (%Xh)\n",
                                head->protocol_id,head->protocol_id);
@@ -3067,7 +3098,14 @@ void
 cleanup_module(void)
 {
        if (thiscard.start) arcnet_close(&thiscard);
-       if (thiscard.irq) free_irq(thiscard.irq);
+       
+       if (thiscard.irq)
+       {
+               free_irq(thiscard.irq);
+               /* very important! */
+               irq2dev_map[thiscard.irq] = NULL;
+       }
+       
        if (thiscard.base_addr) release_region(thiscard.base_addr,
                                                ARCNET_TOTAL_SIZE);
        unregister_netdev(&thiscard);
index c2b4dd69037980d6a5f37c9eb48376759aca5e73..07061ec0c71a8cbd44f3836d99a63a493ed06fcb 100644 (file)
@@ -435,7 +435,7 @@ static short   srom_data(u_int command, u_long address);
 /*static void    srom_busy(u_int command, u_long address);*/
 static void    sendto_srom(u_int command, u_long addr);
 static int     getfrom_srom(u_long addr);
-static void    SetMulticastFilter(struct device *dev, int num_addrs, char *addrs);
+static void    SetMulticastFilter(struct device *dev);
 static int     get_hw_addr(struct device *dev);
 
 static void    eisa_probe(struct device *dev, u_long iobase);
@@ -901,7 +901,7 @@ de4x5_init(struct device *dev)
   barrier();
 
   /* Build the setup frame depending on filtering mode */
-  SetMulticastFilter(dev, 0, NULL);
+  SetMulticastFilter(dev);
 
   if (lp->chipset != DC21140) {
     load_packet(dev, lp->setup_frame, HASH_F|TD_SET|SETUP_FRAME_LEN, NULL);
@@ -1386,40 +1386,33 @@ static void load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff
 static void
 set_multicast_list(struct device *dev)
 {
-       struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-       u_long iobase = dev->base_addr;
-
-       /* First, double check that the adapter is open */
-       if (irq2dev_map[dev->irq] != NULL) 
-       {
-               if (num_addrs >= 0) 
-               {
-                       SetMulticastFilter(dev);
-                       if (lp->setup_f == HASH_PERF) 
-                       {
-                               load_packet(dev, lp->setup_frame, TD_IC | HASH_F | TD_SET | 
+  struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+  u_long iobase = dev->base_addr;
+
+  /* First, double check that the adapter is open */
+  if (irq2dev_map[dev->irq] != NULL) {
+    if (dev->flags & IFF_PROMISC) {         /* set promiscuous mode */
+      u32 omr;
+      omr = inl(DE4X5_OMR);
+      omr |= OMR_PR;
+      outl(omr, DE4X5_OMR);
+    } else { 
+      SetMulticastFilter(dev);
+      if (lp->setup_f == HASH_PERF) {
+       load_packet(dev, lp->setup_frame, TD_IC | HASH_F | TD_SET | 
                                                        SETUP_FRAME_LEN, NULL);
-                       }
-                       else
-                       {
-                               load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | 
+      } else {
+       load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | 
                                                        SETUP_FRAME_LEN, NULL);
-                       }
+      }
+      
+      lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+      outl(POLL_DEMAND, DE4X5_TPD);                /* Start the TX */
+      dev->trans_start = jiffies;
+    }
+  }
 
-                       lp->tx_new = (++lp->tx_new) % lp->txRingSize;
-                       outl(POLL_DEMAND, DE4X5_TPD);                /* Start the TX */
-                       dev->trans_start = jiffies;
-               }
-               else
-               { 
-                       /* set promiscuous mode */
-                       u32 omr;
-                       omr = inl(DE4X5_OMR);
-                       omr |= OMR_PR;
-                       outl(omr, DE4X5_OMR);
-               }
-       }
-       return;
+  return;
 }
 
 /*
@@ -1439,76 +1432,49 @@ static void SetMulticastFilter(struct device *dev)
   unsigned char *addrs;
 
   omr = inl(DE4X5_OMR);
+  omr &= ~OMR_PR;
   pa = build_setup_frame(dev, ALL);          /* Build the basic frame */
 
-  if (lp->setup_f == HASH_PERF) 
-  {
-       if (num_addrs >= HASH_TABLE_LEN || (dev->flags&IFF_ALLMULTI)) 
-       {
-               /* Pass all multicasts */
-               omr |= OMR_PM;
-       }
-       else
-       {
-               omr &= ~OMR_PM;
+  if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 14)) {
+    omr |= OMR_PM;                           /* Pass all multicasts */
+  } else if (lp->setup_f == HASH_PERF) {
                                              /* Now update the MCA table */
-               for (i=0;i<num_addrs;i++) 
-               {     
-                       /* for each address in the list */
-                       addrs=dmi->dmi_addr;
-                       dmi=dmi->next;
-                       if ((*addrs & 0x01) == 1) 
-                       {       
-                               /* multicast address? */ 
-                               crc = 0xffffffff;                  /* init CRC for each address */
-                               for (byte=0;byte<ETH_ALEN;byte++) 
-                               {
-                                       /* for each address byte */
-                                       /* process each address bit */ 
-                                       for (bit = *addrs++,j=0;j<8;j++, bit>>=1) 
-                                       {
-                                               crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
-                                       }
-                               }
-                               hashcode = crc & HASH_BITS;        /* hashcode is 9 LSb of CRC */
-
-                               byte = hashcode >> 3;              /* bit[3-8] -> byte in filter */
-                               bit = 1 << (hashcode & 0x07);      /* bit[0-2] -> bit in byte */
-
-                               byte <<= 1;                        /* calc offset into setup frame */
-                               if (byte & 0x02) 
-                               {
-                                       byte -= 1;
-                               }
-                               lp->setup_frame[byte] |= bit;
-         
-                       }
-                       else
-                       {                             /* skip this address */
-                               addrs += ETH_ALEN;
-                       }
-               }
+    for (i=0;i<dev->mc_count;i++) {          /* for each address in the list */
+      addrs=dmi->dmi_addr;
+      dmi=dmi->next;
+      if ((*addrs & 0x01) == 1) {            /* multicast address? */ 
+       crc = 0xffffffff;                    /* init CRC for each address */
+       for (byte=0;byte<ETH_ALEN;byte++) {  /* for each address byte */
+                                            /* process each address bit */ 
+         for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
+           crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
+         }
        }
-       else
-       {                                   /* Perfect filtering */
-               omr &= ~OMR_PM;
-               for (j=0; j<dev->mc_count; j++) 
-               {
-                       addrs=dmi->dmi_addr;
-                       dmi=dmi->next;
-                       for (i=0; i<ETH_ALEN; i++) 
-                       { 
-                               *(pa + (i&1)) = *addrs++;
-                               if (i & 0x01) pa += 4;
-                       }
-               }
+       hashcode = crc & HASH_BITS;          /* hashcode is 9 LSb of CRC */
+       
+       byte = hashcode >> 3;                /* bit[3-8] -> byte in filter */
+       bit = 1 << (hashcode & 0x07);        /* bit[0-2] -> bit in byte */
+       
+       byte <<= 1;                          /* calc offset into setup frame */
+       if (byte & 0x02) {
+         byte -= 1;
        }
+       lp->setup_frame[byte] |= bit;
+      }
+    }
+  } else {                                   /* Perfect filtering */
+    for (j=0; j<dev->mc_count; j++) {
+      addrs=dmi->dmi_addr;
+      dmi=dmi->next;
+      for (i=0; i<ETH_ALEN; i++) { 
+       *(pa + (i&1)) = *addrs++;
+       if (i & 0x01) pa += 4;
+      }
+    }
+  }
+  outl(omr, DE4X5_OMR);
 
-       if (dev->mc_count == 0)
-               omr &= ~OMR_PR;
-       outl(omr, DE4X5_OMR);
-
-       return;
+  return;
 }
 
 /*
@@ -2616,10 +2582,10 @@ static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd)
       if (ioc->len != HASH_TABLE_LEN) {         /* MCA changes */
        if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN * ioc->len))) {
          memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
-         set_multicast_list(dev, ioc->len, tmp.addr);
+         set_multicast_list(dev);
        }
       } else {
-       set_multicast_list(dev, ioc->len, NULL);
+       set_multicast_list(dev);
       }
     } else {
       status = -EPERM;
@@ -2628,7 +2594,7 @@ static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd)
     break;
   case DE4X5_CLR_MCA:                /* Clear all multicast addresses */
     if (suser()) {
-      set_multicast_list(dev, 0, NULL);
+      set_multicast_list(dev);
     } else {
       status = -EPERM;
     }
index 7248f64829319db77ac7a685a7f1a60a848bf101..f55215b9d69f25645a38194fa5b8eee5ed06b8ae 100644 (file)
@@ -1846,8 +1846,8 @@ ppp_tty_read (struct tty_struct *tty, struct file *file, u_char * buf,
                                if (ppp->flags & SC_DEBUG)
                                        printk (KERN_DEBUG
                                                "ppp_tty_read: no data "
-                                               "(EWOULDBLOCK)\n");
-                               return -EWOULDBLOCK;
+                                               "(EAGAIN)\n");
+                               return -EAGAIN;
                        }
                        current->timeout = 0;
 
@@ -3359,7 +3359,7 @@ ppp_alloc (void)
 
                status = register_netdev (dev);
                if (status == 0) {
-                       printk ("registered device %s\n", dev->name);
+                       printk (KERN_INFO "registered device %s\n", dev->name);
                        return (ppp);
                }
 
index 1fdbbaa0eda4ad81f95fda79c679c0b714e1b7f2..d046fd870e49ff08f2fbec53de8ffd5ba3356280 100644 (file)
@@ -131,7 +131,7 @@ slhc_init(int rslots, int tslots)
                        kfree((unsigned char *)comp);
                        return NULL;
                }
-               memset(comp->tstate, 0, rslots * sizeof(struct cstate));
+               memset(comp->tstate, 0, tslots * sizeof(struct cstate));
                comp->tslot_limit = tslots - 1;
        }
 
index 23f082aed28fc845a3630584a73b32eda510baf1..3386a9b8340dc7717b94e1e0fc70adc4e0a92b95 100644 (file)
@@ -684,7 +684,7 @@ static void set_multicast_list(struct device *dev)
                /* Log any net taps. */
                printk("%s: Promiscuous mode enabled.\n", dev->name);
        }
-       else if (dev->mc_count > 15 || (dev->mc_flags&IFF_ALLMULTI)) 
+       else if (dev->mc_count > 15 || (dev->flags&IFF_ALLMULTI)) 
        {
                /* Too many to filter perfectly -- accept all multicasts. */
                outl(csr6 | 0x0080, ioaddr + CSR6);
@@ -727,11 +727,12 @@ static int
 set_mac_address(struct device *dev, void *addr)
 {
        int i;
+       struct sockaddr *sa=(struct sockaddr *)addr;
        if (dev->start)
                return -EBUSY;
        printk("%s: Setting MAC address to ", dev->name);
        for (i = 0; i < 6; i++)
-               printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]);
+               printk(" %2.2x", dev->dev_addr[i] = sa->sa_data[i]);
        printk(".\n");
        return 0;
 }
index 00238bdcb784048053014811aa84120f2ea52b86..b9482d7f93f9be2608b288ba0c551d3d9278c212 100644 (file)
@@ -71,6 +71,7 @@ struct pci_dev_info dev_info[] = {
        DEVICE( CIRRUS,         CIRRUS_5434_8,  "GD 5434"),
        DEVICE( CIRRUS,         CIRRUS_6729,    "CL 6729"),
        DEVICE( CIRRUS,         CIRRUS_7542,    "CL 7542"),
+       DEVICE( CIRRUS,         CIRRUS_7543,    "CL 7543"),
        DEVICE( AMD,            AMD_LANCE,      "79C970"),
        DEVICE( AMD,            AMD_SCSI,       "53C974"),
        DEVICE( TRIDENT,        TRIDENT_9420,   "TG 9420"),
@@ -141,7 +142,8 @@ struct pci_dev_info dev_info[] = {
        DEVICE( ALLIANCE,       ALLIANCE_PROVIDEO, "Provideo"),
        DEVICE( MUTECH,         MUTECH_MV1000,  "MV-1000"),
        DEVICE( ZEITNET,        ZEITNET_1221,   "1221"),
-       DEVICE( HAL,            HAL_RIO,        "RIO host"),
+       DEVICE( SPECIALIX,      SPECIALIX_XIO,  "XIO/SIO host"),
+       DEVICE( SPECIALIX,      SPECIALIX_RIO,  "RIO host"),
        DEVICE( CYCLADES,       CYCLADES_Y,     "Cyclome-Y"),
        DEVICE( SYMPHONY,       SYMPHONY_101,   "82C101"),
        DEVICE( TEKRAM,         TEKRAM_DC290,   "DC-290"),
@@ -422,7 +424,7 @@ const char *pci_strvendor(unsigned int vendor)
              case PCI_VENDOR_ID_ALLIANCE:      return "Alliance";
              case PCI_VENDOR_ID_MUTECH:        return "Mutech";
              case PCI_VENDOR_ID_ZEITNET:       return "ZeitNet";
-             case PCI_VENDOR_ID_HAL:           return "HAL";
+             case PCI_VENDOR_ID_SPECIALIX:     return "Specialix";
              case PCI_VENDOR_ID_CYCLADES:      return "Cyclades";
              case PCI_VENDOR_ID_SYMPHONY:      return "Symphony";
              case PCI_VENDOR_ID_TEKRAM:        return "Tekram";
@@ -724,7 +726,7 @@ static unsigned int scan_bus(struct pci_bus *bus, unsigned long *mem_startp)
                dev->device = (l >> 16) & 0xffff;
 
                /*
-                * Check to see if we now about this device and report
+                * Check to see if we know about this device and report
                 * a message at boot time.  This is the only way to
                 * learn about new hardware...
                 */
index 7b568fddda9f3a0b5366f56a46823c6dfa762cec..935f31c4c0c4101835252812f4fb5af05f44671c 100644 (file)
  * Thanks also to Greg Hosler who did a lot of testing and  *
  * found quite a number of bugs during the development.            *
  ************************************************************
- *  last change: 95/11/03                 OS: Linux 1.3.37  *
+ *  last change: 95/11/29                 OS: Linux 1.3.45  *
  ************************************************************/
 
 /* Look in eata_dma.h for configuration and revision information */
 
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
@@ -98,7 +97,7 @@ static unchar reg_IRQL[] =
 static struct eata_sp *status = 0;   /* Statuspacket array   */
 static void *dma_scratch = 0;
 
-static u32 fake_int_base;
+static struct eata_register *fake_int_base;
 static int fake_int_result;
 static int fake_int_happened;
 
@@ -117,9 +116,9 @@ void eata_scsi_done (Scsi_Cmnd * scmd)
 
 void eata_fake_int_handler(s32 irq, struct pt_regs * regs)
 {
-    fake_int_result = inb(fake_int_base + HA_RSTATUS);
+    fake_int_result = inb((ulong)fake_int_base + HA_RSTATUS);
     fake_int_happened = TRUE;
-    DBG(DBG_INTR3, printk("eata_fake_int_handler called irq%d base %#x"
+    DBG(DBG_INTR3, printk("eata_fake_int_handler called irq%d base %p"
                          " res %#x\n", irq, fake_int_base, fake_int_result));
     return;
 }
@@ -166,141 +165,141 @@ void eata_int_handler(int irq, struct pt_regs * regs)
     save_flags(flags);
     cli();
 
-    for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->prev) {
+    for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) {
        if (sh->irq != irq)
            continue;
-       if (!(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ))
-           continue;
-       
-       int_counter++;
-       
-       sp=&SD(sh)->sp;
-       
-       if((cp = sp->ccb) == NULL) {
-           eata_stat = inb(base + HA_RSTATUS);
-           printk("eata_dma: Board: %x Spurious IRQ %lx "
-                  "received. CCB pointer not set.\n", base, (long)cp);
-           restore_flags(flags);
-           return;
-       }
-       cmd = cp->cmd;
-       base = (uint) cmd->host->base;
-       
-       hba_stat = sp->hba_stat;
-       
-       scsi_stat = (sp->scsi_stat >> 1) & 0x1f; 
-       
-       if (sp->EOC == FALSE) {
-           eata_stat = inb(base + HA_RSTATUS);
-           printk("eata_dma: int_handler, board: %x cmd %lx returned "
-                  "unfinished.\nEATA: %x HBA: %x SCSI: %x spadr %lx spadrirq "
-                  "%lx, irq%d\n", base, (long)cp, eata_stat, hba_stat, 
-                  scsi_stat,(long)&status, (long)&status[irq], irq);
-           DBG(DBG_DELAY, DEL2(800));
-           restore_flags(flags);
-           return;
-       } 
-       
-       if (cp->status == LOCKED) {
-           cp->status = FREE;
-           eata_stat = inb(base + HA_RSTATUS);
-           printk("eata_dma: int_handler, freeing locked queueslot\n");
-           DBG(DBG_INTR && DBG_DELAY, DEL2(800));
-           restore_flags(flags);
-           return;
-       }
        
-       eata_stat = inb(base + HA_RSTATUS); 
-       DBG(DBG_INTR, printk("IRQ %d received, base %#.4x, pid %ld, target: "
-                            "%x, lun: %x, ea_s: %#.2x, hba_s: %#.2x \n", 
-                            irq, base, cmd->pid, cmd->target, cmd->lun, 
-                            eata_stat, hba_stat));
-
-       switch (hba_stat) {
-       case HA_NO_ERROR:       /* NO Error */
-           if (scsi_stat == CONDITION_GOOD
-               && cmd->device->type == TYPE_DISK
-               && (HD(cmd)->t_state[cp->cp_channel][cp->cp_id] == RESET))
-               result = DID_BUS_BUSY << 16;        
-           else if (scsi_stat == GOOD) {
-               HD(cmd)->t_state[cp->cp_channel][cp->cp_id] = OK;
-               if(HD(cmd)->do_latency == TRUE && cp->timestamp) {
-                   uint time;
-                   time = jiffies - cp->timestamp;
-                   if((cp->rw_latency) == TRUE) { /* was WRITE */
-                       if(HD(cmd)->writes_lat[cp->sizeindex][1] > time)
-                           HD(cmd)->writes_lat[cp->sizeindex][1] = time;
-                       if(HD(cmd)->writes_lat[cp->sizeindex][2] < time)
-                           HD(cmd)->writes_lat[cp->sizeindex][2] = time;
-                       HD(cmd)->writes_lat[cp->sizeindex][3] += time;
-                       HD(cmd)->writes_lat[cp->sizeindex][0]++;
-                   } else {
-                       if(HD(cmd)->reads_lat[cp->sizeindex][1] > time)
-                           HD(cmd)->reads_lat[cp->sizeindex][1] = time;
-                       if(HD(cmd)->reads_lat[cp->sizeindex][2] < time)
-                           HD(cmd)->reads_lat[cp->sizeindex][2] = time;
-                       HD(cmd)->reads_lat[cp->sizeindex][3] += time;
-                       HD(cmd)->reads_lat[cp->sizeindex][0]++;
+       while(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) {
+           
+           int_counter++;
+           
+           sp = &SD(sh)->sp;
+           cp = sp->ccb;
+           
+           if(cp == NULL) {
+               eata_stat = inb((uint)sh->base + HA_RSTATUS);
+               printk("eata_dma: int_handler, Spurious IRQ %d "
+                      "received. CCB pointer not set.\n", irq);
+               break;
+           }
+
+           cmd = cp->cmd;
+           base = (uint) cmd->host->base;
+                   hba_stat = sp->hba_stat;
+           
+           scsi_stat = (sp->scsi_stat >> 1) & 0x1f; 
+           
+           if (sp->EOC == FALSE) {
+               eata_stat = inb(base + HA_RSTATUS);
+               printk("eata_dma: int_handler, board: %x cmd %lx returned "
+                      "unfinished.\nEATA: %x HBA: %x SCSI: %x spadr %lx "
+                      "spadrirq %lx, irq%d\n", base, (long)cp, eata_stat, 
+                      hba_stat, scsi_stat,(long)&status, (long)&status[irq], 
+                      irq);
+               DBG(DBG_DELAY, DEL2(800));
+               break;
+           } 
+           
+           if (cp->status == LOCKED) {
+               cp->status = FREE;
+               eata_stat = inb(base + HA_RSTATUS);
+               printk("eata_dma: int_handler, freeing locked queueslot\n");
+               DBG(DBG_INTR && DBG_DELAY, DEL2(800));
+               break;
+           }
+           
+           eata_stat = inb(base + HA_RSTATUS); 
+           DBG(DBG_INTR, printk("IRQ %d received, base %#.4x, pid %ld, "
+                                "target: %x, lun: %x, ea_s: %#.2x, hba_s: "
+                                "%#.2x \n", irq, base, cmd->pid, cmd->target,
+                                cmd->lun, eata_stat, hba_stat));
+           
+           switch (hba_stat) {
+           case HA_NO_ERROR:   /* NO Error */
+               if (scsi_stat == CONDITION_GOOD
+                   && cmd->device->type == TYPE_DISK
+                   && (HD(cmd)->t_state[cp->cp_channel][cp->cp_id] == RESET))
+                   result = DID_BUS_BUSY << 16;            
+               else if (scsi_stat == GOOD) {
+                   HD(cmd)->t_state[cp->cp_channel][cp->cp_id] = OK;
+                   if(HD(cmd)->do_latency == TRUE && cp->timestamp) {
+                       uint time;
+                       time = jiffies - cp->timestamp;
+                       if((cp->rw_latency) == TRUE) { /* was WRITE */
+                           if(HD(cmd)->writes_lat[cp->sizeindex][1] > time)
+                               HD(cmd)->writes_lat[cp->sizeindex][1] = time;
+                           if(HD(cmd)->writes_lat[cp->sizeindex][2] < time)
+                               HD(cmd)->writes_lat[cp->sizeindex][2] = time;
+                           HD(cmd)->writes_lat[cp->sizeindex][3] += time;
+                           HD(cmd)->writes_lat[cp->sizeindex][0]++;
+                       } else {
+                           if(HD(cmd)->reads_lat[cp->sizeindex][1] > time)
+                               HD(cmd)->reads_lat[cp->sizeindex][1] = time;
+                           if(HD(cmd)->reads_lat[cp->sizeindex][2] < time)
+                               HD(cmd)->reads_lat[cp->sizeindex][2] = time;
+                           HD(cmd)->reads_lat[cp->sizeindex][3] += time;
+                           HD(cmd)->reads_lat[cp->sizeindex][0]++;
+                       }
                    }
                }
-           }
-           else if (scsi_stat == CHECK_CONDITION
-                    && cmd->device->type == TYPE_DISK
-                    && (cmd->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
-               result = DID_BUS_BUSY << 16;
-           else
-               result = DID_OK << 16;
-           HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id] = OK;
-           break;
-       case HA_ERR_SEL_TO:     /* Selection Timeout */
-           result = DID_BAD_TARGET << 16;  
-           break;
-       case HA_ERR_CMD_TO:     /* Command Timeout   */
-           if (HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id] > 1)
+               else if (scsi_stat == CHECK_CONDITION
+                        && cmd->device->type == TYPE_DISK
+                        && (cmd->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+                   result = DID_BUS_BUSY << 16;
+               else
+                   result = DID_OK << 16;
+               HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id] = OK;
+               break;
+           case HA_ERR_SEL_TO:         /* Selection Timeout */
+               result = DID_BAD_TARGET << 16;  
+               break;
+           case HA_ERR_CMD_TO:         /* Command Timeout   */
+               if (HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id] > 1)
+                   result = DID_ERROR << 16;
+               else {
+                   result = DID_TIME_OUT << 16;
+                   HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id]++;
+               }
+               break;
+           case HA_ERR_RESET:          /* SCSI Bus Reset Received */
+           case HA_INIT_POWERUP:       /* Initial Controller Power-up */
+               if (cmd->device->type != TYPE_TAPE)
+                   result = DID_BUS_BUSY << 16;
+               else
+                   result = DID_ERROR << 16;
+               
+               for (i = 0; i < MAXTARGET; i++)
+                   HD(cmd)->t_state[cp->cp_channel][i] = RESET;
+               break;
+           case HA_UNX_BUSPHASE:       /* Unexpected Bus Phase */
+           case HA_UNX_BUS_FREE:       /* Unexpected Bus Free */
+           case HA_BUS_PARITY:         /* Bus Parity Error */
+           case HA_SCSI_HUNG:          /* SCSI Hung */
+           case HA_UNX_MSGRJCT:        /* Unexpected Message Reject */
+           case HA_RESET_STUCK:        /* SCSI Bus Reset Stuck */
+           case HA_RSENSE_FAIL:        /* Auto Request-Sense Failed */
+           case HA_PARITY_ERR:         /* Controller Ram Parity */
+           default:
                result = DID_ERROR << 16;
-           else {
-               result = DID_TIME_OUT << 16;
-               HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id]++;
+               break;
            }
-           break;
-       case HA_ERR_RESET:              /* SCSI Bus Reset Received */
-       case HA_INIT_POWERUP:           /* Initial Controller Power-up */
-           if (cmd->device->type != TYPE_TAPE)
-               result = DID_BUS_BUSY << 16;
-           else
-               result = DID_ERROR << 16;
+           cmd->result = result | (scsi_stat << 1); 
            
-           for (i = 0; i < MAXTARGET; i++)
-               HD(cmd)->t_state[cp->cp_channel][i] = RESET;
-           break;
-       case HA_UNX_BUSPHASE:       /* Unexpected Bus Phase */
-       case HA_UNX_BUS_FREE:       /* Unexpected Bus Free */
-       case HA_BUS_PARITY:         /* Bus Parity Error */
-       case HA_SCSI_HUNG:          /* SCSI Hung */
-       case HA_UNX_MSGRJCT:        /* Unexpected Message Reject */
-       case HA_RESET_STUCK:        /* SCSI Bus Reset Stuck */
-       case HA_RSENSE_FAIL:        /* Auto Request-Sense Failed */
-       case HA_PARITY_ERR:         /* Controller Ram Parity */
-       default:
-           result = DID_ERROR << 16;
-           break;
-       }
-       cmd->result = result | (scsi_stat << 1); 
-       
 #if DBG_INTR2
-       if (scsi_stat || result || hba_stat || eata_stat != 0x50 
-           || cmd->scsi_done == NULL || cmd->device->id == 7) 
-           printk("HBA: %d, channel %d, id: %d, lun %d, pid %ld:\n" 
-                  "eata_stat %#x, hba_stat %#.2x, scsi_stat %#.2x, "
-                  "sense_key: %#x, result: %#.8x\n"
-                  x, cmd->device->channel, cmd->device->id, cmd->device->lun,
-                  cmd->pid, eata_stat, hba_stat, scsi_stat, 
-                  cmd->sense_buffer[2] & 0xf, cmd->result); 
-       DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
+           if (scsi_stat || result || hba_stat || eata_stat != 0x50 
+               || cmd->scsi_done == NULL || cmd->device->id == 7) 
+               printk("HBA: %d, channel %d, id: %d, lun %d, pid %ld:\n" 
+                      "eata_stat %#x, hba_stat %#.2x, scsi_stat %#.2x, "
+                      "sense_key: %#x, result: %#.8x\n", x
+                      cmd->device->channel, cmd->device->id, cmd->device->lun,
+                      cmd->pid, eata_stat, hba_stat, scsi_stat, 
+                      cmd->sense_buffer[2] & 0xf, cmd->result); 
+           DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
 #endif
-       
-       cp->status = FREE;          /* now we can release the slot  */
-       cmd->scsi_done(cmd);
+           
+           cp->status = FREE;      /* now we can release the slot  */
+           cmd->scsi_done(cmd);
+       }
     }
     restore_flags(flags);
     
@@ -471,7 +470,7 @@ int eata_queue(Scsi_Cmnd * cmd, void (* done) (Scsi_Cmnd *))
     if (cmd->use_sg) {
        cp->scatter = TRUE;     /* SG mode     */
        if (cp->sg_list == NULL) {
-           cp->sg_list = kmalloc(SG_SIZE_BIG*sizeof(struct eata_sg_list),
+           cp->sg_list = kmalloc(sh->sg_tablesize * sizeof(struct eata_sg_list),
                                  GFP_ATOMIC | GFP_DMA);
        }
        if (cp->sg_list == NULL)
@@ -529,8 +528,8 @@ int eata_queue(Scsi_Cmnd * cmd, void (* done) (Scsi_Cmnd *))
 
 int eata_abort(Scsi_Cmnd * cmd)
 {
-    ulong flags;
     ulong loop = R_LIMIT;
+    ulong flags;
 
     save_flags(flags);
     cli();
@@ -539,24 +538,14 @@ int eata_abort(Scsi_Cmnd * cmd)
                           " reason %x\n", cmd->pid, cmd->target, cmd->lun, 
                           cmd->abort_reason));
     DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
-    
-    
-    while (inb((u32)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY)
+
+    while (inb((u32)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY) {
        if (--loop == 0) {
            printk("eata_dma: abort, timeout error.\n");
            restore_flags(flags);
            DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
            return (SCSI_ABORT_ERROR);
        }
-    if (CD(cmd)->status == USED) {
-       DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_BUSY\n"));
-       restore_flags(flags);
-       return (SCSI_ABORT_BUSY);  /* SNOOZE */ 
-    }
-    if (CD(cmd)->status == FREE) {
-       DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_NOT_RUNNING\n")); 
-       restore_flags(flags);
-       return (SCSI_ABORT_NOT_RUNNING);
     }
     if (CD(cmd)->status == RESET) {
        restore_flags(flags);
@@ -570,6 +559,16 @@ int eata_abort(Scsi_Cmnd * cmd)
        DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
        return (SCSI_ABORT_NOT_RUNNING);
     }
+    if (CD(cmd)->status == USED) {
+       DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_BUSY\n"));
+       restore_flags(flags);
+       return (SCSI_ABORT_BUSY);  /* SNOOZE */ 
+    }
+    if (CD(cmd)->status == FREE) {
+       DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_NOT_RUNNING\n")); 
+       restore_flags(flags);
+       return (SCSI_ABORT_NOT_RUNNING);
+    }
     restore_flags(flags);
     panic("eata_dma: abort: invalid slot status\n");
 }
@@ -728,7 +727,7 @@ char * get_board_data(u32 base, u32 irq, u32 id)
     cp->cp_cdb[4] = 56;
     cp->cp_cdb[5] = 0;
 
-    fake_int_base = base;
+    fake_int_base = (struct eata_register *) base;
     fake_int_result = FALSE;
     fake_int_happened = FALSE;
 
@@ -828,7 +827,6 @@ int get_conf_PIO(u32 base, struct get_conf *buf)
 
 void print_config(struct get_conf *gc)
 {
-    printk("Please check values: (read config data)\n");
     printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d DMAS:%d\n",
           (u32) ntohl(gc->len), gc->version,
           gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support,
@@ -1075,7 +1073,7 @@ short register_HBA(u32 base, struct get_conf *gc, Scsi_Host_Template * tpnt,
                       "This might be a PM2012 with a defective Firmware\n");
        }
     }
-    
+
     if (gc->SECOND)
        hd->primary = FALSE;
     else
@@ -1193,7 +1191,7 @@ void find_PCI(struct get_conf *buf, Scsi_Host_Template * tpnt)
                                    pci_index, &pci_bus, &pci_device_fn))
                break;
            DBG(DBG_PROBE && DBG_PCI, 
-               printk("eata_dma: HBA at bus %d, device %d,"
+               printk("eata_dma: find_PCI, HBA at bus %d, device %d,"
                       " function %d, index %d\n", (s32)pci_bus, 
                       (s32)((pci_device_fn & 0xf8) >> 3),
                       (s32)(pci_device_fn & 7), pci_index));
@@ -1206,17 +1204,17 @@ void find_PCI(struct get_conf *buf, Scsi_Host_Template * tpnt)
                                               (u16 *) & com_adr))) {
                        if (!((com_adr & PCI_COMMAND_IO) && 
                              (com_adr & PCI_COMMAND_MASTER))) {
-                           printk("eata_dma: HBA has IO or BUSMASTER mode disabled\n");
+                           printk("eata_dma: find_PCI, HBA has IO or BUSMASTER mode disabled\n");
                            continue;
                        }
                    } else
-                       printk("eata_dma: error %x while reading "
+                       printk("eata_dma: find_PCI, error %x while reading "
                               "PCI_COMMAND\n", error);
                } else
-                   printk("eata_dma: DEVICECLASSID %x didn't match\n", 
+                   printk("eata_dma: find_PCI, DEVICECLASSID %x didn't match\n", 
                           rev_device);
            } else {
-               printk("eata_dma: error %x while reading PCI_CLASS_BASE\n", 
+               printk("eata_dma: find_PCI, error %x while reading PCI_CLASS_BASE\n", 
                       error);
                continue;
            }
index dc15cfdcee5a2527d4e9e95a318a1b21e66890b9..41504673a5dd86e45a829d0ed9b86e985dac9d39 100644 (file)
@@ -16,7 +16,7 @@
 
 #define VER_MAJOR 2
 #define VER_MINOR 5
-#define VER_SUB   "8"
+#define VER_SUB   "8a"
 
 
 /************************************************************************
index 2e8376f034d0457fdd0e686befa3de5f290144c4..4d9fc497496a1825155e73c5175aff1f73c73500 100644 (file)
@@ -3,7 +3,7 @@
 * Linux EATA SCSI drivers                              *
 * (c) 1993,94,95 Michael Neuffer                       *
 *********************************************************
-* last change: 95/06/20                                        *
+* last change: 95/11/07                                        *
 ********************************************************/
 
 
@@ -64,7 +64,7 @@
 #define MAX_PCI_BUS    16             /* Maximum # Of Busses Allowed    */
 
 #define SG_SIZE                64 
-#define SG_SIZE_BIG    509            /* max. 509 */
+#define SG_SIZE_BIG    509            /* max. 509 elements, one 4k page */
 
 #define C_P_L_DIV      2 /* 1 <= C_P_L_DIV <= 8            
                           * You can use this parameter to fine-tune
index ee6d8e9b71076a491fdd49609bed3f8e05f96d18..cf3f63e21f1d8751a9fa9ed1cc776cecb6b8d1e2 100644 (file)
@@ -158,6 +158,10 @@ Scsi_Host_Template * scsi_hosts = NULL;
 
 static Scsi_Host_Template builtin_scsi_hosts[] =
 {
+/* BusLogic must come before aha1542.c */
+#ifdef CONFIG_SCSI_BUSLOGIC
+    BUSLOGIC,
+#endif
 #ifdef CONFIG_SCSI_U14_34F
     ULTRASTOR_14_34F,
 #endif
@@ -167,10 +171,6 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
 #ifdef CONFIG_SCSI_AHA152X
     AHA152X,
 #endif
-/* BusLogic must come before aha1542.c */
-#ifdef CONFIG_SCSI_BUSLOGIC
-    BUSLOGIC,
-#endif
 #ifdef CONFIG_SCSI_AHA1542
     AHA1542,
 #endif
index 4cddce55103f1b6bf6872288611e5f96a9197168..de48287ab964925d7ae833c2d7fe1b5c99ce0f19 100644 (file)
@@ -62,6 +62,10 @@ static void scsi_done (Scsi_Cmnd *SCpnt);
 static int update_timeout (Scsi_Cmnd *, int);
 static void print_inquiry(unsigned char *data);
 static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid);
+static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev ,
+                 Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt,
+                 struct Scsi_Host *shpnt, char * scsi_result);
+
 
 static unsigned char * dma_malloc_freelist = NULL;
 static int scsi_need_isa_bounce_buffers;
@@ -371,392 +375,365 @@ void scsi_luns_setup(char *str, int *ints) {
  *  lun address of all sequential devices to the tape driver, all random
  *  devices to the disk driver.
  */
-static
-void scan_scsis (struct Scsi_Host * shpnt, unchar hardcoded, 
-                unchar hchannel, unchar hid, unchar hlun)
+static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
+                 unchar hchannel, unchar hid, unchar hlun)
 {
-    int dev, lun, type, channel;
-    unsigned char scsi_cmd [12];
-    unsigned char scsi_result0 [256];
-    unsigned char * scsi_result;
-    Scsi_Device * SDpnt, *SDtail;
-    struct Scsi_Device_Template * sdtpnt;
-    int                 bflags;
-    int                 max_dev_lun = 0;
-    Scsi_Cmnd  *SCpnt;
-    
-    ++in_scan_scsis;
-    lun = 0;
-    type = -1;
-    SCpnt = (Scsi_Cmnd *) scsi_init_malloc(sizeof(Scsi_Cmnd), GFP_ATOMIC|GFP_DMA);
-    SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof (Scsi_Device), GFP_ATOMIC);
-    SDtail = scsi_devices;
-    
-    if(scsi_devices) while(SDtail->next) SDtail = SDtail->next;
-    
-    /* Make sure we have something that is valid for DMA purposes */
-    scsi_result = ((!dma_malloc_freelist  || !shpnt->unchecked_isa_dma)
-                  ?  &scsi_result0[0] : scsi_malloc(512));
-    
-    if(scsi_result == NULL) {
-       printk("Unable to obtain scsi_result buffer\n");
-       goto leave;
-    }
-    
-    shpnt->host_queue = SCpnt; /* We need this so that commands can time out */
+  int dev, lun, channel;
+  unsigned char scsi_result0[256];
+  unsigned char *scsi_result;
+  Scsi_Device *SDpnt;
+  int max_dev_lun;
+  Scsi_Cmnd *SCpnt;
 
-    if(hardcoded == 1) {
-       channel = hchannel;
-       dev = hid;
-       lun = hlun;
-       goto crude; /* Anyone remember good ol' BASIC ?  :-) */
-    }
+  in_scan_scsis++;
+  SCpnt = (Scsi_Cmnd *) scsi_init_malloc (sizeof (Scsi_Cmnd), GFP_ATOMIC | GFP_DMA);
+  SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
+
+  /* Make sure we have something that is valid for DMA purposes */
+  scsi_result = ((!dma_malloc_freelist || !shpnt->unchecked_isa_dma)
+                 ? &scsi_result0[0] : scsi_malloc (512));
+
+  if (scsi_result == NULL) {
+    printk ("Unable to obtain scsi_result buffer\n");
+    goto leave;
+  }
+
+  shpnt->host_queue = SCpnt;    /* We need this so that commands can time out */
+
+  if (hardcoded == 1) {
+    channel = hchannel;
+    if(channel > shpnt->max_channel) goto leave;
+    dev = hid;
+    if(dev >= shpnt->max_id) goto leave;
+    lun = hlun;
+    if(lun >= shpnt->max_lun) goto leave;
+    scan_scsis_single (channel, dev, lun, &max_dev_lun,
+                   &SDpnt, SCpnt, shpnt, scsi_result);
+  }
+  else {
+    for (channel = 0; channel <= shpnt->max_channel; channel++) {
+      for (dev = 0; dev < shpnt->max_id; ++dev) {
+        if (shpnt->this_id != dev) {
+          /*
+           * We need the for so our continue, etc. work fine. We put this in
+           * a variable so that we can override it during the scan if we
+           * detect a device *KNOWN* to have multiple logical units.
+           */
+          max_dev_lun = (max_scsi_luns < shpnt->max_lun ?
+                         max_scsi_luns : shpnt->max_lun);
+
+          for (lun = 0; lun < max_dev_lun; ++lun) {
+
+            if (!scan_scsis_single (channel, dev, lun, &max_dev_lun,
+                                    &SDpnt, SCpnt, shpnt, scsi_result))
+              break; /* break means don't probe for luns!=0 */
+          }                     /* for lun ends */
+        }                       /* if this_id != id ends */
+      }                         /* for dev ends */
+    }                           /* for channel ends */
+
+leave:
+    shpnt->host_queue = NULL;   /* No longer needed here */
+
+     /* Last device block does not exist.  Free memory. */
+    if (SDpnt != NULL)
+      scsi_init_free ((char *) SDpnt, sizeof (Scsi_Device));
+
+    if (SCpnt != NULL)
+      scsi_init_free ((char *) SCpnt, sizeof (Scsi_Cmnd));
 
-    for (channel = 0; channel <= shpnt->max_channel; channel++)
-    {
-       for (dev = 0; dev < shpnt->max_id; ++dev) {
-           if (shpnt->this_id != dev) {
-               
-               /*
-                * We need the for so our continue, etc. work fine.
-                * We put this in a variable so that we can override
-                * it during the scan if we detect a device *KNOWN*
-                * to have multiple logical units.
-                */
-               max_dev_lun = (max_scsi_luns < shpnt->max_lun ? 
-                              max_scsi_luns : shpnt->max_lun);
+    /* If we allocated a buffer so we could do DMA, free it now */
+    if (scsi_result != &scsi_result0[0] && scsi_result != NULL)
+      scsi_free (scsi_result, 512);
 
-               for (lun = 0; lun < max_dev_lun; ++lun)
-               {
-               crude:
-                   memset(SDpnt, 0, sizeof(Scsi_Device));
-                   SDpnt->host = shpnt;
-                   SDpnt->id = dev;
-                   SDpnt->lun = lun;
-                   SDpnt->channel = channel;
-
-                   /* Some low level driver could use device->type (DB) */
-                   SDpnt->type = -1;
-                   /*
-                    * Assume that the device will have handshaking problems, 
-                    * and then fix this field later if it turns out it doesn't
-                    */
-                   SDpnt->borken = 1;
-                    SDpnt->was_reset = 0;
-                    SDpnt->expecting_cc_ua = 0;
-                   
-                   scsi_cmd[0] = TEST_UNIT_READY;
-                   scsi_cmd[1] = lun << 5;
-                   scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
-                   
-                   memset(SCpnt, 0,  sizeof(Scsi_Cmnd));
-                   SCpnt->host = SDpnt->host;
-                   SCpnt->device = SDpnt;
-                   SCpnt->target = SDpnt->id;
-                   SCpnt->lun = SDpnt->lun;
-                   SCpnt->channel = SDpnt->channel;
+    in_scan_scsis--;
+  }
+}
 
-                   {
-                       /*
-                        * Do the actual command, and wait for it to finish
-                        */
-                       struct semaphore sem = MUTEX_LOCKED;
-                       SCpnt->request.sem = &sem;
-                       SCpnt->request.rq_status = RQ_SCSI_BUSY;
-                       scsi_do_cmd (SCpnt, (void *)  scsi_cmd, 
-                                    (void *) scsi_result,
-                                    256,  scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
-                       down(&sem);
-                   }
+/*
+ * The worker for scan_scsis.
+ * Returning 0 means Please don't ask further for lun!=0, 1 means OK go on.
+ * Global variables used : scsi_devices(linked list), the_result
+ */
+int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
+    Scsi_Device **SDpnt2, Scsi_Cmnd * SCpnt, struct Scsi_Host * shpnt, 
+    char *scsi_result)
+{
+  unsigned char scsi_cmd[12];
+  struct Scsi_Device_Template *sdtpnt;
+  Scsi_Device * SDtail, *SDpnt=*SDpnt2;
+  int bflags, type=-1;
+
+  SDtail = scsi_devices;
+  if (scsi_devices)
+    while (SDtail->next)
+      SDtail = SDtail->next;
+
+  memset (SDpnt, 0, sizeof (Scsi_Device));
+  SDpnt->host = shpnt;
+  SDpnt->id = dev;
+  SDpnt->lun = lun;
+  SDpnt->channel = channel;
+
+  /* Some low level driver could use device->type (DB) */
+  SDpnt->type = -1;
+
+  /*
+   * Assume that the device will have handshaking problems, and then fix this
+   * field later if it turns out it doesn't
+   */
+  SDpnt->borken = 1;
+  SDpnt->was_reset = 0;
+  SDpnt->expecting_cc_ua = 0;
+
+  scsi_cmd[0] = TEST_UNIT_READY;
+  scsi_cmd[1] = lun << 5;
+  scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
+
+  memset (SCpnt, 0, sizeof (Scsi_Cmnd));
+  SCpnt->host = SDpnt->host;
+  SCpnt->device = SDpnt;
+  SCpnt->target = SDpnt->id;
+  SCpnt->lun = SDpnt->lun;
+  SCpnt->channel = SDpnt->channel;
+  {
+    struct semaphore sem = MUTEX_LOCKED;
+    SCpnt->request.sem = &sem;
+    SCpnt->request.rq_status = RQ_SCSI_BUSY;
+    scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+                 (void *) scsi_result,
+                 256, scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
+    down (&sem);
+  }
 
 #if defined(DEBUG) || defined(DEBUG_INIT)
-                   printk("scsi: scan SCSIS id %d lun %d\n", dev, lun);
-                   printk("scsi: return code %08x\n", SCpnt->result);
+  printk ("scsi: scan_scsis_single id %d lun %d. Return code %d\n",
+          dev, lun, SCpnt->result);
 #endif
 
-                   if(SCpnt->result) {
-                       if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||
-                            (status_byte(SCpnt->result) & CHECK_CONDITION)) &&
-                           ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
-                           if(((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
-                              ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION))
-                               continue;
-                       }
-                       else
-                           break;
-                   }
-                   
+  if (SCpnt->result) {
+    if (((driver_byte (SCpnt->result) & DRIVER_SENSE) ||
+         (status_byte (SCpnt->result) & CHECK_CONDITION)) &&
+        ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
+      if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
+          ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION))
+        return 1;
+    }
+    else
+      return 0;
+  }
+
 #if defined (DEBUG) || defined(DEBUG_INIT)
-                   printk("scsi: performing INQUIRY\n");
+  printk ("scsi: performing INQUIRY\n");
 #endif
-
-                   /*
-                    * Build an INQUIRY command block.
-                    */
-                   scsi_cmd[0] = INQUIRY;
-                   scsi_cmd[1] = (lun << 5) & 0xe0;
-                   scsi_cmd[2] = 0;
-                   scsi_cmd[3] = 0;
-                   scsi_cmd[4] = 255;
-                   scsi_cmd[5] = 0;
-                   
-                   SCpnt->cmd_len = 0;
-
-                   {
-                       struct semaphore sem = MUTEX_LOCKED;
-                       SCpnt->request.sem = &sem;
-                       SCpnt->request.rq_status = RQ_SCSI_BUSY;
-                       scsi_do_cmd (SCpnt, (void *)  scsi_cmd, 
-                                (void *) scsi_result,
-                                256,  scan_scsis_done, SCSI_TIMEOUT, 3);
-                       down(&sem);
-                   }
-                   
-                   the_result = SCpnt->result;
-                   
+  /*
+   * Build an INQUIRY command block.
+   */
+  scsi_cmd[0] = INQUIRY;
+  scsi_cmd[1] = (lun << 5) & 0xe0;
+  scsi_cmd[2] = 0;
+  scsi_cmd[3] = 0;
+  scsi_cmd[4] = 255;
+  scsi_cmd[5] = 0;
+  SCpnt->cmd_len = 0;
+  {
+    struct semaphore sem = MUTEX_LOCKED;
+    SCpnt->request.sem = &sem;
+    SCpnt->request.rq_status = RQ_SCSI_BUSY;
+    scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+                 (void *) scsi_result,
+                 256, scan_scsis_done, SCSI_TIMEOUT, 3);
+    down (&sem);
+  }
+
+  the_result = SCpnt->result;
 #if defined(DEBUG) || defined(DEBUG_INIT)
-                   if (!the_result)
-                       printk("scsi: INQUIRY successful\n");
-                   else
-                       printk("scsi: INQUIRY failed with code %08x\n", the_result);
+  printk ("scsi: INQUIRY %s with code 0x%x\n",
+          the_result ? "failed" : "successful", the->result);
 #endif
-                   
-                   if(the_result) break;
-                   
-                   /* skip other luns on this device */
-                   
-                   if (!the_result)
-                   {
-                       /* It would seem some TOSHIBA CDROM 
-                        * gets things wrong 
-                        */
-                       if (!strncmp(scsi_result+8,"TOSHIBA",7) &&
-                           !strncmp(scsi_result+16,"CD-ROM",6) &&
-                           scsi_result[0] == TYPE_DISK) {
-                           scsi_result[0] = TYPE_ROM;
-                           scsi_result[1] |= 0x80;  /* removable */
-                       }
-                       
-                       if (!strncmp(scsi_result+8,"NEC",3)) {
-                           if (!strncmp(scsi_result+16,"CD-ROM DRIVE:84 ",16) || 
-                               !strncmp(scsi_result+16,"CD-ROM DRIVE:25",15))
-                               SDpnt->manufacturer = SCSI_MAN_NEC_OLDCDR;
-                           else
-                               SDpnt->manufacturer = SCSI_MAN_NEC;
-                       } else if (!strncmp(scsi_result+8,"TOSHIBA",7))
-                           SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
-                       else if (!strncmp(scsi_result+8,"SONY",4))
-                           SDpnt->manufacturer = SCSI_MAN_SONY;
-                        else if (!strncmp(scsi_result+8, "PIONEER", 7))
-                            SDpnt->manufacturer = SCSI_MAN_PIONEER;
-                       else
-                           SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
-                       
-                       memcpy(SDpnt->vendor, scsi_result+8, 8);
-                       memcpy(SDpnt->model, scsi_result+16, 16);
-                       memcpy(SDpnt->rev, scsi_result+32, 4);
-                       SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
-                       SDpnt->lockable = SDpnt->removable;
-                       SDpnt->changed = 0;
-                       SDpnt->access_count = 0;
-                       SDpnt->busy = 0;
-                       SDpnt->has_cmdblocks = 0;
-                       /*
-                        * Currently, all sequential devices are assumed to be
-                        * tapes, all random devices disk, with the appropriate
-                        * read only flags set for ROM / WORM treated as RO.
-                        */
-                       
-                       switch (type = (scsi_result[0] & 0x1f))
-                       {
-                       case TYPE_TAPE :
-                       case TYPE_DISK :
-                       case TYPE_MOD :
-                       case TYPE_PROCESSOR :
-                       case TYPE_SCANNER :
-                           SDpnt->writeable = 1;
-                           break;
-                       case TYPE_WORM :
-                       case TYPE_ROM :
-                           SDpnt->writeable = 0;
-                           break;
-                       default :
-#if 0
-#ifdef DEBUG
-                           printk("scsi: unknown type %d\n", type);
-                           print_inquiry(scsi_result);
-#endif
-                           type = -1;
-#endif
-                       }
-                       
-                       SDpnt->single_lun = 0;
-                       SDpnt->soft_reset =
-                           (scsi_result[7] & 1) && ((scsi_result[3] &7) == 2);
-                       SDpnt->random = (type == TYPE_TAPE) ? 0 : 1;
-                       SDpnt->type = (type & 0x1f);
-                       
-                       if (type != -1)
-                       {
-                           print_inquiry(scsi_result);
-                           
-                           for(sdtpnt = scsi_devicelist; sdtpnt; 
-                               sdtpnt = sdtpnt->next)
-                               if(sdtpnt->detect) SDpnt->attached +=
-                                   (*sdtpnt->detect)(SDpnt);
-                           
-                           SDpnt->scsi_level = scsi_result[2] & 0x07;
-                           if (SDpnt->scsi_level >= 2 ||
-                               (SDpnt->scsi_level == 1 &&
-                                (scsi_result[3] & 0x0f) == 1))
-                               SDpnt->scsi_level++;
-                           /*
-                            * Set the tagged_queue flag for SCSI-II devices 
-                            * that purport to support
-                            * tagged queuing in the INQUIRY data.
-                            */
-                           
-                           SDpnt->tagged_queue = 0;
-                           
-                           if ((SDpnt->scsi_level >= SCSI_2) &&
-                               (scsi_result[7] & 2)) {
-                               SDpnt->tagged_supported = 1;
-                               SDpnt->current_tag = 0;
-                           }
-                           
-                           /*
-                            * Accommodate drivers that want to sleep when 
-                            * they should be in a polling loop.
-                            */
-
-                           SDpnt->disconnect = 0;
-
-                           /*
-                            * Get any flags for this device.
-                            */
-                           bflags = get_device_flags(scsi_result);
-
-                           
-                           /*
-                            * Some revisions of the Texel CD ROM drives have 
-                            * handshaking problems when used with the Seagate
-                            * controllers.  Before we know what type of device
-                            * we're talking to, we assume it's borken and then
-                            * change it here if it turns out that it isn't
-                            * a TEXEL drive.
-                            */
-                           if( (bflags & BLIST_BORKEN) == 0 )
-                           {
-                               SDpnt->borken = 0;
-                           }
-                           
-                           
-                           /* These devices need this "key" to unlock the
-                            * devices so we can use it 
-                            */
-                           if( (bflags & BLIST_KEY) != 0 ) {
-                               printk("Unlocked floptical drive.\n");
-                               SDpnt->lockable = 0;
-                               scsi_cmd[0] = MODE_SENSE;
-                               scsi_cmd[1] = (lun << 5) & 0xe0;
-                               scsi_cmd[2] = 0x2e;
-                               scsi_cmd[3] = 0;
-                               scsi_cmd[4] = 0x2a;
-                               scsi_cmd[5] = 0;
-                               
-                               SCpnt->cmd_len = 0;
-                               {
-                                   struct semaphore sem = MUTEX_LOCKED;
-                                   SCpnt->request.rq_status = RQ_SCSI_BUSY;
-                                   SCpnt->request.sem = &sem;
-                                   scsi_do_cmd (SCpnt, (void *)  scsi_cmd,
-                                            (void *) scsi_result, 0x2a,  
-                                            scan_scsis_done, SCSI_TIMEOUT, 3);
-                                   down(&sem);
-                               }
-                           }
-                           /* Add this device to the linked list at the end */
-                           if(SDtail)
-                               SDtail->next = SDpnt;
-                           else
-                               scsi_devices = SDpnt;
-                           SDtail = SDpnt;
-                           
-                           SDpnt = (Scsi_Device *) scsi_init_malloc(sizeof (Scsi_Device), GFP_ATOMIC);
-                           /* Some scsi devices cannot be polled for lun != 0
-                            * due to firmware bugs 
-                            */
-                           if(bflags & BLIST_NOLUN) break;
-
-                           /*
-                            * If we want to only allow I/O to one of the luns
-                            * attached to this device at a time, then we set 
-                             * this flag.
-                            */
-                           if(bflags & BLIST_SINGLELUN)
-                           {
-                               SDpnt->single_lun = 1;
-                           }
-
-                           /*
-                            * If this device is known to support multiple 
-                             * units, override the other settings, and scan 
-                             * all of them.
-                            */
-                           if(bflags & BLIST_FORCELUN)
-                           {
-                               /*
-                                * We probably want to make this a variable, 
-                                 * but this will do for now.
-                                */
-                               max_dev_lun = 8;
-                           }
-
-                           /* Old drives like the MAXTOR XT-3280 say vers=0 */
-                           if ((scsi_result[2] & 0x07) == 0)
-                               break;
-                           /* Some scsi-1 peripherals do not handle lun != 0.
-                            * I am assuming that scsi-2 peripherals do better 
-                            */
-                           if((scsi_result[2] & 0x07) == 1 &&
-                              (scsi_result[3] & 0x0f) == 0) break;
-                       }
-                   }       /* if result == DID_OK ends */
 
-                   /*
-                    * This might screw us up with multi-lun devices, but the 
-                     * user can scan for them too.
-                    */
-                   if(hardcoded == 1)
-                       goto leave;
-               } /* for lun ends */
-           } /* if this_id != id ends */
-       } /* for dev ends */
-    } /* for channel ends */
-    
- leave:
-    shpnt->host_queue = NULL;  /* No longer needed here */
-    
-    /* Last device block does not exist.  Free memory. */
-    if(SDpnt != NULL)
-       scsi_init_free((char *) SDpnt, sizeof(Scsi_Device));
-    
-    if(SCpnt != NULL)
-       scsi_init_free((char *) SCpnt, sizeof(Scsi_Cmnd));
-    
-    /* If we allocated a buffer so we could do DMA, free it now */
-    if (scsi_result != &scsi_result0[0] && scsi_result != NULL) 
-       scsi_free(scsi_result, 512);
-    
-    in_scan_scsis = 0;
-}       /* scan_scsis  ends */
+  if (the_result)
+    return 0;     /* assume no peripheral if any sort of error */
+
+  /*
+   * It would seem some TOSHIBA CDROM gets things wrong
+   */
+  if (!strncmp (scsi_result + 8, "TOSHIBA", 7) &&
+      !strncmp (scsi_result + 16, "CD-ROM", 6) &&
+      scsi_result[0] == TYPE_DISK) {
+    scsi_result[0] = TYPE_ROM;
+    scsi_result[1] |= 0x80;     /* removable */
+  }
+
+  if (!strncmp (scsi_result + 8, "NEC", 3)) {
+    if (!strncmp (scsi_result + 16, "CD-ROM DRIVE:84 ", 16) ||
+        !strncmp (scsi_result + 16, "CD-ROM DRIVE:25", 15))
+      SDpnt->manufacturer = SCSI_MAN_NEC_OLDCDR;
+    else
+      SDpnt->manufacturer = SCSI_MAN_NEC;
+  }
+  else if (!strncmp (scsi_result + 8, "TOSHIBA", 7))
+    SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
+  else if (!strncmp (scsi_result + 8, "SONY", 4))
+    SDpnt->manufacturer = SCSI_MAN_SONY;
+  else if (!strncmp (scsi_result + 8, "PIONEER", 7))
+    SDpnt->manufacturer = SCSI_MAN_PIONEER;
+  else
+    SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
+
+  memcpy (SDpnt->vendor, scsi_result + 8, 8);
+  memcpy (SDpnt->model, scsi_result + 16, 16);
+  memcpy (SDpnt->rev, scsi_result + 32, 4);
+
+  SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
+  SDpnt->lockable = SDpnt->removable;
+  SDpnt->changed = 0;
+  SDpnt->access_count = 0;
+  SDpnt->busy = 0;
+  SDpnt->has_cmdblocks = 0;
+  /*
+   * Currently, all sequential devices are assumed to be tapes, all random
+   * devices disk, with the appropriate read only flags set for ROM / WORM
+   * treated as RO.
+   */
+  switch (type = (scsi_result[0] & 0x1f)) {
+  case TYPE_TAPE:
+  case TYPE_DISK:
+  case TYPE_MOD:
+  case TYPE_PROCESSOR:
+  case TYPE_SCANNER:
+    SDpnt->writeable = 1;
+    break;
+  case TYPE_WORM:
+  case TYPE_ROM:
+    SDpnt->writeable = 0;
+    break;
+  default:
+    printk ("scsi: unknown type %d\n", type);
+  }
+
+  SDpnt->single_lun = 0;
+  SDpnt->soft_reset =
+    (scsi_result[7] & 1) && ((scsi_result[3] & 7) == 2);
+  SDpnt->random = (type == TYPE_TAPE) ? 0 : 1;
+  SDpnt->type = (type & 0x1f);
+
+  print_inquiry (scsi_result);
+
+  for (sdtpnt = scsi_devicelist; sdtpnt;
+       sdtpnt = sdtpnt->next)
+    if (sdtpnt->detect)
+      SDpnt->attached +=
+        (*sdtpnt->detect) (SDpnt);
+
+  SDpnt->scsi_level = scsi_result[2] & 0x07;
+  if (SDpnt->scsi_level >= 2 ||
+      (SDpnt->scsi_level == 1 &&
+       (scsi_result[3] & 0x0f) == 1))
+    SDpnt->scsi_level++;
+
+  /*
+   * Set the tagged_queue flag for SCSI-II devices that purport to support
+   * tagged queuing in the INQUIRY data.
+   */
+  SDpnt->tagged_queue = 0;
+  if ((SDpnt->scsi_level >= SCSI_2) &&
+      (scsi_result[7] & 2)) {
+    SDpnt->tagged_supported = 1;
+    SDpnt->current_tag = 0;
+  }
+
+  /*
+   * Accommodate drivers that want to sleep when they should be in a polling
+   * loop.
+   */
+  SDpnt->disconnect = 0;
+
+  /*
+   * Get any flags for this device.
+   */
+  bflags = get_device_flags (scsi_result);
+
+  /*
+   * Some revisions of the Texel CD ROM drives have handshaking problems when
+   * used with the Seagate controllers.  Before we know what type of device
+   * we're talking to, we assume it's borken and then change it here if it
+   * turns out that it isn't a TEXEL drive.
+   */
+  if ((bflags & BLIST_BORKEN) == 0)
+    SDpnt->borken = 0;
+
+  /*
+   * These devices need this "key" to unlock the devices so we can use it
+   */
+  if ((bflags & BLIST_KEY) != 0) {
+    printk ("Unlocked floptical drive.\n");
+    SDpnt->lockable = 0;
+    scsi_cmd[0] = MODE_SENSE;
+    scsi_cmd[1] = (lun << 5) & 0xe0;
+    scsi_cmd[2] = 0x2e;
+    scsi_cmd[3] = 0;
+    scsi_cmd[4] = 0x2a;
+    scsi_cmd[5] = 0;
+    SCpnt->cmd_len = 0;
+    {
+      struct semaphore sem = MUTEX_LOCKED;
+      SCpnt->request.rq_status = RQ_SCSI_BUSY;
+      SCpnt->request.sem = &sem;
+      scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+                   (void *) scsi_result, 0x2a,
+                   scan_scsis_done, SCSI_TIMEOUT, 3);
+      down (&sem);
+    }
+  }
+  /* Add this device to the linked list at the end */
+  if (SDtail)
+    SDtail->next = SDpnt;
+  else
+    scsi_devices = SDpnt;
+  SDtail = SDpnt;
+
+  SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
+  *SDpnt2=SDpnt;
+  if (!SDpnt)
+    printk ("scsi: scan_scsis_single: No memory\n");
+
+
+  /*
+   * Some scsi devices cannot be polled for lun != 0 due to firmware bugs
+   */
+  if (bflags & BLIST_NOLUN)
+    return 0;                   /* break; */
+
+  /*
+   * If we want to only allow I/O to one of the luns attached to this device
+   * at a time, then we set this flag.
+   */
+  if (bflags & BLIST_SINGLELUN)
+    SDpnt->single_lun = 1;
+
+  /*
+   * If this device is known to support multiple units, override the other
+   * settings, and scan all of them.
+   */
+  if (bflags & BLIST_FORCELUN)
+    *max_dev_lun = 8;
+  /*
+   * We assume the device can't handle lun!=0 if: - it reports scsi-0 (ANSI
+   * SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it reports scsi-1
+   * (ANSI SCSI Revision 1) and Response Data Format 0
+   */
+  if (((scsi_result[2] & 0x07) == 0)
+      ||
+      ((scsi_result[2] & 0x07) == 1 &&
+       (scsi_result[3] & 0x0f) == 0))
+    return 0;
+  return 1;
+}
 
 /*
  *  Flag bits for the internal_timeout array
  */
-
 #define NORMAL_TIMEOUT 0
 #define IN_ABORT 1
 #define IN_RESET 2
@@ -1153,8 +1130,8 @@ inline void internal_cmnd (Scsi_Cmnd * SCpnt)
 #ifdef DEBUG_DELAY
        clock = jiffies + 4 * HZ;
        while (jiffies < clock);
-       printk("done(host = %d, result = %04x) : routine at %08x\n", 
-              host->host_no, temp);
+       printk("done(host = %d, result = %04x) : routine at %p\n", 
+              host->host_no, temp, host->hostt->command);
 #endif
        scsi_done(SCpnt);
     }
@@ -2413,7 +2390,7 @@ int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
     struct Scsi_Host *HBA_ptr;
     int  parameter[4];
     char *p;
-    int          size, len = 0;
+    int          i,size, len = 0;
     off_t begin = 0;
     off_t pos = 0;
 
@@ -2458,21 +2435,29 @@ int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
        return (len);     
     }
 
+    /*
+     * Usage: echo "scsi singledevice 0 1 2 3" >/proc/scsi/scsi
+     * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
+     * Consider this feature ALPHA, as you can easily hang your
+     * scsi system (depending on your low level driver).
+     */
     if(!buffer || length < 25 || strncmp("scsi", buffer, 4))
        return(-EINVAL);
 
     if(!strncmp("singledevice", buffer + 5, 12)) {
        p = buffer + 17;
 
-       parameter[0] = simple_strtoul(p , &p, 0);
-       parameter[1] = simple_strtoul(p , &p, 0);
-       parameter[2] = simple_strtoul(p , &p, 0);
-       parameter[3] = simple_strtoul(p , &p, 0);
+       for(i=0; i<4; i++)      {
+           p++;
+           parameter[i] = simple_strtoul(p, &p, 0);
+       }
+       printk("scsi singledevice %d %d %d %d\n", parameter[0], parameter[1],
+                       parameter[2], parameter[3]);
 
-       while(scd && scd->host->host_no != parameter[0] 
-             && scd->channel != parameter[1] 
-             && scd->id != parameter[2] 
-             && scd->lun != parameter[3]) {
+       while(scd && (scd->host->host_no != parameter[0] 
+             || scd->channel != parameter[1] 
+             || scd->id != parameter[2] 
+             || scd->lun != parameter[3])) {
            scd = scd->next;
        }
        if(scd)
@@ -2484,7 +2469,7 @@ int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
            return(-ENXIO);
 
        scan_scsis (HBA_ptr, 1, parameter[1], parameter[2], parameter[3]);
-       return(0);
+       return(length);
     }
     return(-EINVAL);
 }
@@ -3035,7 +3020,7 @@ scsi_dump_status(void)
        for(SCpnt=shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
        {
            /*  (0) 0:0:0:0 (802 123434 8 8 0) (3 3 2) (%d %d %d) %d %x      */
-           printk("(%d) %d:%d:%d:%d (%s %ld %ld %ld %ld) (%d %d %x) (%d %d %d) %x %x %x\n",
+           printk("(%d) %d:%d:%d:%d (%s %ld %ld %ld %d) (%d %d %x) (%d %d %d) %x %x %x\n",
                   i++, SCpnt->host->host_no,
                   SCpnt->channel,
                   SCpnt->target,
index db0a0b808d63f05ce5768eb6b7415cc4c69e8054..30045df8c135fa2e966996217cd96a3f438c61db 100644 (file)
@@ -59,6 +59,7 @@ struct symbol_table scsi_symbol_table = {
     X(kernel_scsi_ioctl),
     X(need_isa_buffer),
     X(request_queueable),
+    X(in_scan_scsis),
 #if defined(CONFIG_PROC_FS)
     X(proc_print_scsidevice),
 #endif
index 17939b7ce259fd716e0ca3876fecaf0ec55d1da8..e4e4e7647e835a45c28a8e1a35d1bd319a822e1a 100644 (file)
@@ -86,8 +86,8 @@ static int partsize(struct buffer_head *bh, unsigned long capacity,
     unsigned int  *cyls, unsigned int *hds, unsigned int *secs) {
     struct partition *p, *largest = NULL;
     int i, largest_cyl;
-    int cyl, end_head, end_cyl, end_sector;
-    unsigned int logical_end, physical_end;
+    int cyl, ext_cyl, end_head, end_cyl, end_sector;
+    unsigned int logical_end, physical_end, ext_physical_end;
     
 
     if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
@@ -111,6 +111,7 @@ static int partsize(struct buffer_head *bh, unsigned long capacity,
        end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
        end_head = largest->end_head;
        end_sector = largest->end_sector & 0x3f;
+
 #ifdef DEBUG
        printk ("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
            end_head, end_cyl, end_sector);
@@ -122,12 +123,25 @@ static int partsize(struct buffer_head *bh, unsigned long capacity,
        /* This is the actual _sector_ number at the end */
        logical_end = largest->start_sect + largest->nr_sects;
 
-       if (logical_end == physical_end) {
+       /* This is for >1023 cylinders */
+        ext_cyl= (logical_end-(end_head * end_sector + end_sector))
+                                        /(end_head + 1) / end_sector;
+       ext_physical_end = ext_cyl * (end_head + 1) * end_sector +
+            end_head * end_sector + end_sector;
+
+#ifdef DEBUG
+       printk("scsicam_bios_param : logical_end=%d physical_end=%d ext_physical_end=%d ext_cyl=%d\n"
+                       ,logical_end,physical_end,ext_physical_end,ext_cyl);
+#endif
+
+       if ((logical_end == physical_end) ||
+           (end_cyl==1023 && ext_physical_end==logical_end)) {
            *secs = end_sector;
            *hds = end_head + 1;
            *cyls = capacity / ((end_head + 1) * end_sector);
            return 0;
        }
+       
 #ifdef DEBUG
        printk ("scsicam_bios_param : logical (%u) != physical (%u)\n",
            logical_end, physical_end);
index af250158164eb45b93c5f8444794c5ddfa9779e8..7bb72d0cdb58fd058305d4a933e286b91e70cf73 100644 (file)
@@ -215,7 +215,7 @@ static int sg_read(struct inode *inode,struct file *filp,char *buf,int count)
        if (filp->f_flags & O_NONBLOCK)
        {
            restore_flags(flags);
-           return -EWOULDBLOCK;
+           return -EAGAIN;
        }
        interruptible_sleep_on(&device->read_wait);
        if (current->signal & ~current->blocked)
@@ -323,7 +323,7 @@ static int sg_write(struct inode *inode,struct file *filp,const char *buf,int co
     while(device->pending)
     {
        if (filp->f_flags & O_NONBLOCK)
-           return -EWOULDBLOCK;
+           return -EAGAIN;
 #ifdef DEBUG
        printk("sg_write: sleeping on pending request\n");
 #endif     
@@ -392,7 +392,7 @@ static int sg_write(struct inode *inode,struct file *filp,const char *buf,int co
        wake_up(&device->write_wait);
        sg_free(device->buff,device->buff_len);
        device->buff = NULL;
-       return -EWOULDBLOCK;
+       return -EAGAIN;
     } 
 #ifdef DEBUG
     printk("device allocated\n");
index 8c34df319d87e023282e1c5de47099a3e8dde9a4..d64c65fa3d2aff7e08732bda534eb8c03a5028fa 100644 (file)
@@ -13,7 +13,7 @@ O_TARGET := fs.o
 O_OBJS    = open.o read_write.o inode.o devices.o file_table.o buffer.o \
                super.o  block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
                ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \
-               dcache.o $(BINFMTS) 
+               dcache.o dquot.o $(BINFMTS) 
 
 MOD_LIST_NAME := FS_MODULES
 ALL_SUB_DIRS = minix ext ext2 msdos proc isofs nfs xiafs umsdos hpfs sysv smbfs
index e9a8bdca98cd7d114930f34123e6882d0c1e9df2..f88a87764bda9ce44c8242ecaf90a9a5bfff5256 100644 (file)
@@ -222,6 +222,7 @@ void sync_dev(kdev_t dev)
        sync_supers(dev);
        sync_inodes(dev);
        sync_buffers(dev, 0);
+       sync_dquots(dev, -1);
 }
 
 int fsync_dev(kdev_t dev)
@@ -229,6 +230,7 @@ int fsync_dev(kdev_t dev)
        sync_buffers(dev, 0);
        sync_supers(dev);
        sync_inodes(dev);
+       sync_dquots(dev, -1);
        return sync_buffers(dev, 1);
 }
 
diff --git a/fs/dquot.c b/fs/dquot.c
new file mode 100644 (file)
index 0000000..008e9df
--- /dev/null
@@ -0,0 +1,1060 @@
+/*
+ * Implementation of the diskquota system for the LINUX operating
+ * system. QUOTA is implemented using the BSD systemcall interface as
+ * the means of communication with the user level. Currently only the
+ * ext2-filesystem has support for diskquotas. Other filesystems may
+ * be added in future time. This file contains the generic routines
+ * called by the different filesystems on allocation of an inode or
+ * block. These routines take care of the administration needed to
+ * have a consistent diskquota tracking system. The ideas of both
+ * user and group quotas are based on the Melbourne quota system as
+ * used on BSD derivated systems. The internal implementation is 
+ * based on the LINUX inode-subsystem with added complexity of the
+ * diskquota system. This implementation is not based on any BSD
+ * kernel sourcecode.
+ * 
+ * Version: $Id: dquot.c,v 5.6 1995/11/15 20:30:27 mvw Exp mvw $
+ * 
+ * Author:  Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net>
+ *
+ * (C) Copyright 1994, 1995 Marco van Wieringen 
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/mount.h>
+
+#include <asm/segment.h>
+#include <sys/sysmacros.h>
+
+#define __DQUOT_VERSION__      "dquot_5.6.0"
+
+static char quotamessage[MAX_QUOTA_MESSAGE];
+static char *quotatypes[] = INITQFNAMES;
+
+static int nr_dquots = 0, nr_free_dquots = 0;
+static struct dquot *hash_table[NR_DQHASH];
+static struct dquot *first_dquot;
+static struct dqstats dqstats;
+
+static struct wait_queue *dquot_wait = (struct wait_queue *)NULL;
+
+extern void add_dquot_ref(kdev_t dev, short type);
+extern void reset_dquot_ptrs(kdev_t dev, short type);
+
+#ifndef min
+#define min(a,b) ((a) < (b)) ? (a) : (b)
+#endif
+
+/*
+ * Functions for management of the hashlist.
+ */
+static inline int const hashfn(kdev_t dev, unsigned int id, short type)
+{
+       return((HASHDEV(dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;
+}
+
+static inline struct dquot **const hash(kdev_t dev, unsigned int id, short type)
+{
+       return(hash_table + hashfn(dev, id, type));
+}
+
+static inline int has_quota_enabled(kdev_t dev, short type)
+{
+       struct vfsmount *vfsmnt;
+
+       return((vfsmnt = lookup_vfsmnt(dev)) != (struct vfsmount *)NULL &&
+              (vfsmnt->mnt_quotas[type] != (struct file *)NULL));
+}
+
+static void insert_dquot_free(struct dquot *dquot)
+{
+       dquot->dq_next = first_dquot;
+       dquot->dq_prev = first_dquot->dq_prev;
+       dquot->dq_next->dq_prev = dquot;
+       dquot->dq_prev->dq_next = dquot;
+       first_dquot = dquot;
+}
+
+static void remove_dquot_free(struct dquot *dquot)
+{
+       if (first_dquot == dquot)
+               first_dquot = first_dquot->dq_next;
+       if (dquot->dq_next)
+               dquot->dq_next->dq_prev = dquot->dq_prev;
+       if (dquot->dq_prev)
+               dquot->dq_prev->dq_next = dquot->dq_next;
+       dquot->dq_next = dquot->dq_prev = NODQUOT;
+}
+
+static void insert_dquot_hash(struct dquot *dquot)
+{
+       struct dquot **hash_ent;
+
+       hash_ent = hash(dquot->dq_dev, dquot->dq_id, dquot->dq_type);
+       dquot->dq_hash_next = *hash_ent;
+       dquot->dq_hash_prev = NODQUOT;
+       if (dquot->dq_hash_next)
+               dquot->dq_hash_next->dq_hash_prev = dquot;
+       *hash_ent = dquot;
+}
+
+static void remove_dquot_hash(struct dquot *dquot)
+{
+       struct dquot **hash_ent;
+
+       hash_ent = hash(dquot->dq_dev, dquot->dq_id, dquot->dq_type);
+       if (*hash_ent == dquot)
+               *hash_ent = dquot->dq_hash_next;
+       if (dquot->dq_hash_next)
+               dquot->dq_hash_next->dq_hash_prev = dquot->dq_hash_prev;
+       if (dquot->dq_hash_prev)
+               dquot->dq_hash_prev->dq_hash_next = dquot->dq_hash_next;
+       dquot->dq_hash_prev = dquot->dq_hash_next = NODQUOT;
+}
+
+static void put_last_free(struct dquot *dquot)
+{
+       remove_dquot_free(dquot);
+       dquot->dq_prev = first_dquot->dq_prev;
+       dquot->dq_prev->dq_next = dquot;
+       dquot->dq_next = first_dquot;
+       dquot->dq_next->dq_prev = dquot;
+}
+
+static void grow_dquots(void)
+{
+       struct dquot *dquot;
+       int cnt;
+
+       if (!(dquot = (struct dquot*) get_free_page(GFP_KERNEL)))
+               return;
+       dqstats.pages_allocated++;
+       cnt = PAGE_SIZE / sizeof(struct dquot);
+       nr_dquots += cnt;
+       nr_free_dquots += cnt;
+       if (!first_dquot) {
+               dquot->dq_next = dquot->dq_prev = first_dquot = dquot++;
+               cnt--;
+       }
+       for (; cnt; cnt--)
+               insert_dquot_free(dquot++);
+}
+
+/*
+ * Functions for locking and waiting on dquots.
+ */
+static void __wait_on_dquot(struct dquot *dquot)
+{
+       struct wait_queue wait = {current, NULL};
+
+       add_wait_queue(&dquot->dq_wait, &wait);
+repeat:
+       current->state = TASK_UNINTERRUPTIBLE;
+       if (dquot->dq_flags & DQ_LOCKED) {
+               dquot->dq_flags |= DQ_WANT;
+               schedule();
+               goto repeat;
+       }
+       remove_wait_queue(&dquot->dq_wait, &wait);
+       current->state = TASK_RUNNING;
+}
+
+static inline void wait_on_dquot(struct dquot *dquot)
+{
+       if (dquot->dq_flags & DQ_LOCKED)
+               __wait_on_dquot(dquot);
+}
+
+static inline void lock_dquot(struct dquot *dquot)
+{
+       wait_on_dquot(dquot);
+       dquot->dq_flags |= DQ_LOCKED;
+}
+
+static inline void unlock_dquot(struct dquot *dquot)
+{
+       dquot->dq_flags &= ~DQ_LOCKED;
+       if (dquot->dq_flags & DQ_WANT) {
+               dquot->dq_flags &= ~DQ_WANT;
+               wake_up(&dquot->dq_wait);
+       }
+}
+/*
+ * Note that we don't want to disturb any wait-queues when we discard
+ * an dquot.
+ *
+ * FIXME: As soon as we have a nice solution for the inode problem we
+ *               can also fix this one. I.e. the volatile part.
+ */
+static void clear_dquot(struct dquot * dquot)
+{
+       struct wait_queue *wait;
+
+       wait_on_dquot(dquot);
+       remove_dquot_hash(dquot);
+       remove_dquot_free(dquot);
+       wait = ((volatile struct dquot *) dquot)->dq_wait;
+       if (dquot->dq_count)
+               nr_free_dquots++;
+       memset(dquot, 0, sizeof(*dquot));
+       ((volatile struct dquot *) dquot)->dq_wait = wait;
+       insert_dquot_free(dquot);
+}
+
+static void write_dquot(struct dquot *dquot)
+{
+       short type = dquot->dq_type;
+       struct file *filp = dquot->dq_mnt->mnt_quotas[type];
+       unsigned short fs;
+
+       if (!(dquot->dq_flags & DQ_MOD) || (filp == (struct file *)NULL))
+               return;
+       lock_dquot(dquot);
+       down(&dquot->dq_mnt->mnt_sem);
+       if (filp->f_op->lseek) {
+               if (filp->f_op->lseek(filp->f_inode, filp,
+                   dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) {
+                       up(&dquot->dq_mnt->mnt_sem);
+                       unlock_dquot(dquot);
+                       return;
+               }
+       } else
+               filp->f_pos = dqoff(dquot->dq_id);
+       fs = get_fs();
+       set_fs(KERNEL_DS);
+       if (filp->f_op->write(filp->f_inode, filp,
+          (char *)&dquot->dq_dqb, sizeof(struct dqblk)) == sizeof(struct dqblk))
+               dquot->dq_flags &= ~DQ_MOD;
+       up(&dquot->dq_mnt->mnt_sem);
+       set_fs(fs);
+       unlock_dquot(dquot);
+       dqstats.writes++;
+}
+
+static void read_dquot(struct dquot *dquot)
+{
+       short type = dquot->dq_type;
+       struct file *filp = dquot->dq_mnt->mnt_quotas[type];
+       unsigned short fs;
+
+       if (filp == (struct file *)NULL)
+               return;
+       lock_dquot(dquot);
+       down(&dquot->dq_mnt->mnt_sem);
+       if (filp->f_op->lseek) {
+               if (filp->f_op->lseek(filp->f_inode, filp,
+                   dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) {
+                       up(&dquot->dq_mnt->mnt_sem);
+                       unlock_dquot(dquot);
+                       return;
+               }
+       } else
+               filp->f_pos = dqoff(dquot->dq_id);
+       fs = get_fs();
+       set_fs(KERNEL_DS);
+       filp->f_op->read(filp->f_inode, filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk));
+       up(&dquot->dq_mnt->mnt_sem);
+       set_fs(fs);
+       if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 &&
+           dquot->dq_ihardlimit == 0 && dquot->dq_isoftlimit == 0)
+               dquot->dq_flags |= DQ_FAKE;
+       unlock_dquot(dquot);
+       dqstats.reads++;
+}
+
+int sync_dquots(kdev_t dev, short type)
+{
+       struct dquot *dquot = first_dquot;
+       int i;
+
+       dqstats.syncs++;
+       for (i = 0; i < nr_dquots * 2; i++, dquot = dquot->dq_next) {
+               if (dev == NODEV || dquot->dq_count == 0 || dquot->dq_dev != dev)
+                       continue;
+               if (type != -1 && dquot->dq_type != type)
+                       continue;
+               wait_on_dquot(dquot);
+               if (dquot->dq_flags & DQ_MOD)
+                       write_dquot(dquot);
+       }
+       return(0);
+}
+
+/*
+ * Trash the cache for a certain type on a device.
+ */
+void invalidate_dquots(kdev_t dev, short type)
+{
+       struct dquot *dquot, *next;
+       int cnt;
+
+       next = first_dquot;
+       for (cnt = nr_dquots ; cnt > 0 ; cnt--) {
+               dquot = next;
+               next = dquot->dq_next;
+               if (dquot->dq_dev != dev || dquot->dq_type != type)
+                       continue;
+               if (dquot->dq_flags & DQ_LOCKED) {
+                       printk("VFS: dquot busy on removed device %s\n", kdevname(dev));
+                       continue;
+               }
+               if (dquot->dq_flags & DQ_MOD)
+                       write_dquot(dquot);
+               dqstats.drops++;
+               clear_dquot(dquot);
+       }
+}
+
+static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number)
+{
+       lock_dquot(dquot);
+       dquot->dq_curinodes += number;
+       dquot->dq_flags |= DQ_MOD;
+       unlock_dquot(dquot);
+}
+
+static inline void dquot_incr_blocks(struct dquot *dquot, unsigned long number)
+{
+       lock_dquot(dquot);
+       dquot->dq_curblocks += number;
+       dquot->dq_flags |= DQ_MOD;
+       unlock_dquot(dquot);
+}
+
+static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number)
+{
+       lock_dquot(dquot);
+       if (dquot->dq_curinodes > number)
+               dquot->dq_curinodes -= number;
+       else
+               dquot->dq_curinodes = 0;
+       if (dquot->dq_curinodes < dquot->dq_isoftlimit)
+               dquot->dq_itime = (time_t) 0;
+       dquot->dq_flags &= ~DQ_INODES;
+       dquot->dq_flags |= DQ_MOD;
+       unlock_dquot(dquot);
+}
+
+static inline void dquot_decr_blocks(struct dquot *dquot, unsigned long number)
+{
+       lock_dquot(dquot);
+       if (dquot->dq_curblocks > number)
+               dquot->dq_curblocks -= number;
+       else
+               dquot->dq_curblocks = 0;
+       if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
+               dquot->dq_btime = (time_t) 0;
+       dquot->dq_flags &= ~DQ_BLKS;
+       dquot->dq_flags |= DQ_MOD;
+       unlock_dquot(dquot);
+}
+
+static inline int need_print_warning(short type, struct dquot *dquot)
+{
+       switch (type) {
+               case USRQUOTA:
+                       return(current->fsuid == dquot->dq_id);
+               case GRPQUOTA:
+                       return(current->fsgid == dquot->dq_id);
+       }
+       return(0);
+}
+
+static int check_idq(struct dquot *dquot, short type, u_long short inodes)
+{
+       if (inodes <= 0 || dquot->dq_flags & DQ_FAKE)
+               return(QUOTA_OK);
+       if (dquot->dq_ihardlimit &&
+          (dquot->dq_curinodes + inodes) > dquot->dq_ihardlimit && !fsuser()) {
+               if ((dquot->dq_flags & DQ_INODES) == 0 &&
+                     need_print_warning(type, dquot)) {
+                       sprintf(quotamessage, "%s: write failed, %s file limit reached\r\n",
+                               dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+                       tty_write_message(current->tty, quotamessage);
+                       dquot->dq_flags |= DQ_INODES;
+               }
+               return(NO_QUOTA);
+       }
+       if (dquot->dq_isoftlimit &&
+          (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
+           dquot->dq_itime && CURRENT_TIME >= dquot->dq_itime && !fsuser()) {
+                if (need_print_warning(type, dquot)) {
+                       sprintf(quotamessage, "%s: warning, %s file quota exceeded to long.\r\n",
+                               dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+                       tty_write_message(current->tty, quotamessage);
+               }
+               return(NO_QUOTA);
+       }
+       if (dquot->dq_isoftlimit &&
+          (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit &&
+           dquot->dq_itime == 0 && !fsuser()) {
+                if (need_print_warning(type, dquot)) {
+                       sprintf(quotamessage, "%s: warning, %s file quota exceeded\r\n",
+                               dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+                       tty_write_message(current->tty, quotamessage);
+               }
+               dquot->dq_itime = CURRENT_TIME + dquot->dq_mnt->mnt_iexp[type];
+       }
+       return(QUOTA_OK);
+}
+
+static int check_bdq(struct dquot *dquot, short type, u_long blocks)
+{
+       if (blocks <= 0 || dquot->dq_flags & DQ_FAKE)
+               return(QUOTA_OK);
+       if (dquot->dq_bhardlimit &&
+          (dquot->dq_curblocks + blocks) > dquot->dq_bhardlimit && !fsuser()) {
+               if ((dquot->dq_flags & DQ_BLKS) == 0 &&
+                     need_print_warning(type, dquot)) {
+                       sprintf(quotamessage, "%s: write failed, %s disk limit reached.\r\n",
+                               dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+                       tty_write_message(current->tty, quotamessage);
+                       dquot->dq_flags |= DQ_BLKS;
+               }
+               return(NO_QUOTA);
+       }
+       if (dquot->dq_bsoftlimit &&
+          (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
+           dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime && !fsuser()) {
+                if (need_print_warning(type, dquot)) {
+                       sprintf(quotamessage, "%s: write failed, %s disk quota exceeded to long.\r\n",
+                               dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+                       tty_write_message(current->tty, quotamessage);
+               }
+               return(NO_QUOTA);
+       }
+       if (dquot->dq_bsoftlimit &&
+          (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit &&
+           dquot->dq_btime == 0 && !fsuser()) {
+                if (need_print_warning(type, dquot)) {
+                       sprintf(quotamessage, "%s: warning, %s disk quota exceeded\r\n",
+                               dquot->dq_mnt->mnt_dirname, quotatypes[type]);
+                       tty_write_message(current->tty, quotamessage);
+               }
+               dquot->dq_btime = CURRENT_TIME + dquot->dq_mnt->mnt_bexp[type];
+       }
+       return(QUOTA_OK);
+}
+
+static void dqput(struct dquot *dquot)
+{
+       if (!dquot)
+               return;
+       /*
+        * If the dq_mnt pointer isn't initialized this entry needs no
+        * checking and doesn't need to be written. It just an empty
+        * dquot that is put back into the freelist.
+        */
+       if (dquot->dq_mnt != (struct vfsmount *)NULL) {
+               dqstats.drops++;
+               wait_on_dquot(dquot);
+               if (!dquot->dq_count) {
+                       printk("VFS: dqput: trying to free free dquot\n");
+                       printk("VFS: device %s, dquot of %s %d\n", kdevname(dquot->dq_dev),
+                              quotatypes[dquot->dq_type], dquot->dq_id);
+                       return;
+               }
+repeat:
+               if (dquot->dq_count > 1) {
+                       dquot->dq_count--;
+                       return;
+               }
+               wake_up(&dquot_wait);
+               if (dquot->dq_flags & DQ_MOD) {
+                       write_dquot(dquot);     /* we can sleep - so do again */
+                       wait_on_dquot(dquot);
+                       goto repeat;
+               }
+       }
+       if (dquot->dq_count) {
+               dquot->dq_count--;
+               nr_free_dquots++;
+       }
+       return;
+}
+
+static struct dquot *get_empty_dquot(void)
+{
+       struct dquot *dquot, *best;
+       int cnt;
+
+       if (nr_dquots < NR_DQUOTS && nr_free_dquots < (nr_dquots >> 2))
+               grow_dquots();
+
+repeat:
+       dquot = first_dquot;
+       best = NODQUOT;
+       for (cnt = 0; cnt < nr_dquots; dquot = dquot->dq_next, cnt++) {
+               if (!dquot->dq_count) {
+                       if (!best)
+                               best = dquot;
+                       if (!(dquot->dq_flags & DQ_MOD) && !(dquot->dq_flags & DQ_LOCKED)) {
+                               best = dquot;
+                               break;
+                       }
+               }
+       }
+       if (!best || best->dq_flags & DQ_MOD || best->dq_flags & DQ_LOCKED)
+               if (nr_dquots < NR_DQUOTS) {
+                       grow_dquots();
+                       goto repeat;
+               }
+       dquot = best;
+       if (!dquot) {
+               printk("VFS: No free dquots - contact mvw@mcs.ow.org\n");
+               sleep_on(&dquot_wait);
+               goto repeat;
+       }
+       if (dquot->dq_flags & DQ_LOCKED) {
+               wait_on_dquot(dquot);
+               goto repeat;
+       }
+       if (dquot->dq_flags & DQ_MOD) {
+               write_dquot(dquot);
+               goto repeat;
+       }
+       if (dquot->dq_count)
+               goto repeat;
+       clear_dquot(dquot);
+       dquot->dq_count = 1;
+       nr_free_dquots--;
+       if (nr_free_dquots < 0) {
+               printk ("VFS: get_empty_dquot: bad free dquot count.\n");
+               nr_free_dquots = 0;
+       }
+       return(dquot);
+}
+
+static struct dquot *dqget(kdev_t dev, unsigned int id, short type)
+{
+       struct dquot *dquot, *empty;
+       struct vfsmount *vfsmnt;
+
+       if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL ||
+           (vfsmnt->mnt_quotas[type] == (struct file *)0))
+               return(NODQUOT);
+       dqstats.lookups++;
+       empty = get_empty_dquot();
+repeat:
+       dquot = *(hash(dev, id, type));
+       while (dquot) {
+               if (dquot->dq_dev != dev || dquot->dq_id != id) {
+                       dquot = dquot->dq_hash_next;
+                       continue;
+               }
+               wait_on_dquot(dquot);
+               if (dquot->dq_dev != dev || dquot->dq_id != id)
+                       goto repeat;
+               if (!dquot->dq_count)
+                       nr_free_dquots--;
+               dquot->dq_count++;
+               if (empty)
+                       dqput(empty);
+               dqstats.cache_hits++;
+               return(dquot);
+       }
+       if (!empty)
+               return(NODQUOT);
+       dquot = empty;
+       dquot->dq_id = id;
+       dquot->dq_type = type;
+       dquot->dq_dev = dev;
+       dquot->dq_mnt = vfsmnt;
+       put_last_free(dquot);
+       insert_dquot_hash(dquot);
+       read_dquot(dquot);
+       return(dquot);
+}
+
+/*
+ * Initialize a dquot-struct with new quota info. This is used by the
+ * systemcall interface functions.
+ */ 
+static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dqblk)
+{
+       struct dquot *dquot;
+       struct dqblk dq_dqblk;
+       int error;
+
+       if (dqblk == (struct dqblk *)NULL)
+               return(-EFAULT);
+
+       if (flags & QUOTA_SYSCALL) {
+               if ((error = verify_area(VERIFY_READ, dqblk, sizeof(struct dqblk))) != 0)
+                       return(error);
+               memcpy_fromfs(&dq_dqblk, dqblk, sizeof(struct dqblk));
+       } else {
+               memcpy(&dq_dqblk, dqblk, sizeof(struct dqblk));
+       }
+       if ((dquot = dqget(dev, id, type)) != NODQUOT) {
+               lock_dquot(dquot);
+               if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) {
+                       dquot->dq_bhardlimit = dq_dqblk.dqb_bhardlimit;
+                       dquot->dq_bsoftlimit = dq_dqblk.dqb_bsoftlimit;
+                       dquot->dq_ihardlimit = dq_dqblk.dqb_ihardlimit;
+                       dquot->dq_isoftlimit = dq_dqblk.dqb_isoftlimit;
+               }
+               if ((flags & SET_QUOTA) || (flags & SET_USE)) {
+                       if (dquot->dq_isoftlimit &&
+                           dquot->dq_curinodes < dquot->dq_isoftlimit &&
+                           dq_dqblk.dqb_curinodes >= dquot->dq_isoftlimit)
+                               dquot->dq_itime = CURRENT_TIME + dquot->dq_mnt->mnt_iexp[type];
+                       dquot->dq_curinodes = dq_dqblk.dqb_curinodes;
+                       if (dquot->dq_curinodes < dquot->dq_isoftlimit)
+                               dquot->dq_flags &= ~DQ_INODES;
+                       if (dquot->dq_bsoftlimit &&
+                           dquot->dq_curblocks < dquot->dq_bsoftlimit &&
+                           dq_dqblk.dqb_curblocks >= dquot->dq_bsoftlimit)
+                               dquot->dq_btime = CURRENT_TIME + dquot->dq_mnt->mnt_bexp[type];
+                       dquot->dq_curblocks = dq_dqblk.dqb_curblocks;
+                       if (dquot->dq_curblocks < dquot->dq_bsoftlimit)
+                               dquot->dq_flags &= ~DQ_BLKS;
+               }
+               if (id == 0) {
+                       /* 
+                        * Change in expiretimes, change them in dq_mnt.
+                        */
+                       dquot->dq_mnt->mnt_bexp[type] = dquot->dq_btime = dq_dqblk.dqb_btime;
+                       dquot->dq_mnt->mnt_iexp[type] = dquot->dq_itime = dq_dqblk.dqb_itime;
+               }
+               if (dq_dqblk.dqb_bhardlimit == 0 && dq_dqblk.dqb_bsoftlimit == 0 &&
+                   dq_dqblk.dqb_ihardlimit == 0 && dq_dqblk.dqb_isoftlimit == 0)
+                       dquot->dq_flags |= DQ_FAKE;
+               else
+                       dquot->dq_flags &= ~DQ_FAKE;
+               dquot->dq_flags |= DQ_MOD;
+               unlock_dquot(dquot);
+               dqput(dquot);
+       }
+       return(0);
+}
+
+static int get_quota(kdev_t dev, int id, short type, struct dqblk *dqblk)
+{
+       struct dquot *dquot;
+       int error;
+
+       if (has_quota_enabled(dev, type)) {
+               if (dqblk == (struct dqblk *)NULL)
+                       return(-EFAULT);
+
+               if ((error = verify_area(VERIFY_WRITE, dqblk, sizeof(struct dqblk))) != 0)
+                       return(error);
+
+               if ((dquot = dqget(dev, id, type)) != NODQUOT) {
+                       memcpy_tofs(dqblk, (char *)&dquot->dq_dqb, sizeof(struct dqblk));
+                       dqput(dquot);
+                       return(0);
+               }
+       }
+       return(-ESRCH);
+}
+
+static int get_stats(caddr_t addr)
+{
+       int error;
+
+       if ((error = verify_area(VERIFY_WRITE, addr, sizeof(struct dqstats))) != 0)
+               return(error);
+
+       dqstats.allocated_dquots = nr_dquots;
+       dqstats.free_dquots = nr_free_dquots;
+       memcpy_tofs(addr, (caddr_t)&dqstats, sizeof(struct dqstats));
+       return(0);
+}
+
+/*
+ * Initialize pointer in a inode to the right dquots.
+ */
+void dquot_initialize(struct inode *inode, short type)
+{
+       unsigned int id = 0;
+       short cnt;
+
+       if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) {
+               for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+                       if (type != -1 && cnt != type)
+                               continue;
+                       if (!has_quota_enabled(inode->i_dev, cnt))
+                               continue;
+                       if (inode->i_dquot[cnt] == NODQUOT) {
+                               switch (cnt) {
+                                       case USRQUOTA:
+                                               id = inode->i_uid;
+                                               break;
+                                       case GRPQUOTA:
+                                               id = inode->i_gid;
+                                               break;
+                               }
+                               inode->i_dquot[cnt] = dqget(inode->i_dev, id, cnt);
+                               inode->i_flags |= S_WRITE;
+                       }
+               }
+       }
+}
+
+void dquot_drop(struct inode *inode)
+{
+       short cnt;
+
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (inode->i_dquot[cnt] == NODQUOT)
+                       continue;
+               dqput(inode->i_dquot[cnt]);
+               inode->i_dquot[cnt] = NODQUOT;
+       }
+       inode->i_flags &= ~S_WRITE;
+}
+
+/*
+ * This is a simple algorithm that calculates the size of a file in blocks.
+ * This is only used on filesystems that do not have a i_blocks count.
+ */
+static u_long isize_to_blocks(size_t isize, size_t blksize)
+{
+       u_long blocks;
+       u_long indirect;
+
+       if (!blksize)
+               blksize = BLOCK_SIZE;
+       blocks = (isize / blksize) + ((isize % blksize) ? 1 : 0);
+       if (blocks > 10) {
+               indirect = ((blocks - 11) >> 8) + 1; /* single indirect blocks */
+               if (blocks > (10 + 256)) {
+                       indirect += ((blocks - 267) >> 16) + 1; /* double indirect blocks */
+                       if (blocks > (10 + 256 + (256 << 8)))
+                               indirect++; /* triple indirect blocks */
+               }
+               blocks += indirect;
+       }
+       return(blocks);
+}
+
+/*
+ * Externaly referenced funtions trough dq_operations.
+ */
+int dquot_alloc_block(const struct inode *inode, unsigned long number)
+{
+       unsigned short cnt;
+
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (inode->i_dquot[cnt] == NODQUOT)
+                       continue;
+               if (check_bdq(inode->i_dquot[cnt], cnt, number))
+                       return(NO_QUOTA);
+       }
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (inode->i_dquot[cnt] == NODQUOT)
+                       continue;
+               dquot_incr_blocks(inode->i_dquot[cnt], number);
+       }
+       return(QUOTA_OK);
+}
+
+int dquot_alloc_inode(const struct inode *inode, unsigned long number)
+{
+       unsigned short cnt;
+
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (inode->i_dquot[cnt] == NODQUOT)
+                       continue;
+               if (check_idq(inode->i_dquot[cnt], cnt, number))
+                       return(NO_QUOTA);
+       }
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (inode->i_dquot[cnt] == NODQUOT)
+                       continue;
+               dquot_incr_inodes(inode->i_dquot[cnt], number);
+       }
+       return(QUOTA_OK);
+}
+
+void dquot_free_block(const struct inode *inode, unsigned long number)
+{
+       unsigned short cnt;
+
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (inode->i_dquot[cnt] == NODQUOT)
+                       continue;
+               dquot_decr_blocks(inode->i_dquot[cnt], number);
+       }
+}
+
+void dquot_free_inode(const struct inode *inode, unsigned long number)
+{
+       unsigned short cnt;
+
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (inode->i_dquot[cnt] == NODQUOT)
+                       continue;
+               dquot_decr_inodes(inode->i_dquot[cnt], number);
+       }
+}
+
+/*
+ * Transfer the number of inode and blocks from one diskquota to an other.
+ */
+int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction)
+{
+       unsigned long blocks;
+       struct dquot *transfer_from[MAXQUOTAS];
+       struct dquot *transfer_to[MAXQUOTAS];
+       short cnt, disc;
+
+       /*
+        * Find out if this filesystems uses i_blocks.
+        */
+       if (inode->i_blksize == 0)
+               blocks = isize_to_blocks(inode->i_size, BLOCK_SIZE);
+       else
+               blocks = (inode->i_blocks / 2);
+
+       /*
+        * Build the transfer_from and transfer_to lists and check quotas to see
+        * if operation is permitted.
+        */
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               transfer_from[cnt] = NODQUOT;
+               transfer_to[cnt] = NODQUOT;
+
+               if (!has_quota_enabled(inode->i_dev, cnt))
+                       continue;
+
+               switch (cnt) {
+                       case USRQUOTA:
+                               if (inode->i_uid == iattr->ia_uid)
+                                       continue;
+                               transfer_from[cnt] = dqget(inode->i_dev, (direction) ? iattr->ia_uid : inode->i_uid, cnt);
+                               transfer_to[cnt] = dqget(inode->i_dev, (direction) ? inode->i_uid : iattr->ia_uid, cnt);
+                               break;
+                       case GRPQUOTA:
+                               if (inode->i_gid == iattr->ia_gid)
+                                       continue;
+                               transfer_from[cnt] = dqget(inode->i_dev, (direction) ? iattr->ia_gid : inode->i_gid, cnt);
+                               transfer_to[cnt] = dqget(inode->i_dev, (direction) ? inode->i_gid : iattr->ia_gid, cnt);
+                               break;
+               }
+
+               if (check_idq(transfer_to[cnt], cnt, 1) == NO_QUOTA ||
+                   check_bdq(transfer_to[cnt], cnt, blocks) == NO_QUOTA) {
+                       for (disc = 0; disc <= cnt; disc++) {
+                               dqput(transfer_from[disc]);
+                               dqput(transfer_to[disc]);
+                       }
+                       return(NO_QUOTA);
+               }
+       }
+
+       /*
+        * Finaly perform the needed transfer from transfer_from to transfer_to.
+        * And release any pointer to dquots not needed anymore.
+        */
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               /*
+                * Skip changes for same uid or gid or for non-existing quota-type.
+                */
+               if (transfer_from[cnt] == NODQUOT && transfer_to[cnt] == NODQUOT)
+                       continue;
+
+               if (transfer_from[cnt] != NODQUOT) {
+                       dquot_decr_inodes(transfer_from[cnt], 1);
+                       dquot_decr_blocks(transfer_from[cnt], blocks);
+               }
+               if (transfer_to[cnt] != NODQUOT) {
+                       dquot_incr_inodes(transfer_to[cnt], 1);
+                       dquot_incr_blocks(transfer_to[cnt], blocks);
+               }
+               if (inode->i_dquot[cnt] != NODQUOT) {
+                       dqput(transfer_from[cnt]);
+                       dqput(inode->i_dquot[cnt]);
+                       inode->i_dquot[cnt] = transfer_to[cnt];
+               } else {
+                       dqput(transfer_from[cnt]);
+                       dqput(transfer_to[cnt]);
+               }
+       }
+       return(QUOTA_OK);
+}
+
+void dquot_init(void)
+{
+       printk("VFS: Diskquotas version %s initialized\r\n", __DQUOT_VERSION__);
+       memset(hash_table, 0, sizeof(hash_table));
+       memset((caddr_t)&dqstats, 0, sizeof(dqstats));
+       first_dquot = NODQUOT;
+}
+
+/*
+ * Definitions of diskquota operations.
+ */
+struct dquot_operations dquot_operations = {
+       dquot_initialize,
+       dquot_drop,
+       dquot_alloc_block,
+       dquot_alloc_inode,
+       dquot_free_block,
+       dquot_free_inode,
+       dquot_transfer
+};
+
+/*
+ * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
+ */
+int quota_off(kdev_t dev, short type)
+{
+       struct vfsmount *vfsmnt;
+       short cnt;
+
+       for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+               if (type != -1 && cnt != type)
+                       continue;
+               if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL ||
+                    vfsmnt->mnt_quotas[cnt] == (struct file *)NULL)
+                       continue;
+               vfsmnt->mnt_sb->dq_op = (struct dquot_operations *)NULL;
+               reset_dquot_ptrs(dev, cnt);
+               invalidate_dquots(dev, cnt);
+               close_fp(vfsmnt->mnt_quotas[cnt]);
+               vfsmnt->mnt_quotas[cnt] = (struct file *)NULL;
+               vfsmnt->mnt_iexp[cnt] = vfsmnt->mnt_bexp[cnt] = (time_t)NULL;
+       }
+       return(0);
+}
+
+int quota_on(kdev_t dev, short type, char *path)
+{
+       struct file *filp = (struct file *)NULL;
+       struct vfsmount *vfsmnt;
+       struct inode *inode;
+       struct dquot *dquot;
+       char *tmp;
+       int error;
+
+       if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL)
+               return(-ENODEV);
+       if (vfsmnt->mnt_quotas[type] != (struct file *)NULL)
+               return(-EBUSY);
+       if ((error = getname(path, &tmp)) != 0)
+               return(error);
+       error = open_namei(tmp, O_RDWR, 0600, &inode, 0);
+       putname(tmp);
+       if (error)
+               return(error);
+       if (!S_ISREG(inode->i_mode)) {
+               iput(inode);
+               return(-EACCES);
+       }
+       if ((filp = get_empty_filp()) != (struct file *)NULL) {
+               filp->f_mode = (O_RDWR + 1) & O_ACCMODE;
+               filp->f_flags = O_RDWR;
+               filp->f_inode = inode;
+               filp->f_pos = 0;
+               filp->f_reada = 0;
+               filp->f_op = inode->i_op->default_file_ops;
+               if (filp->f_op->read || filp->f_op->write) {
+                       if ((error = get_write_access(inode)) == 0) {
+                               if (filp->f_op && filp->f_op->open)
+                                       error = filp->f_op->open(inode, filp);
+                               if (error == 0) {
+                                       vfsmnt->mnt_quotas[type] = filp;
+                                       dquot = dqget(dev, 0, type);
+                                       vfsmnt->mnt_iexp[type] = (dquot) ? dquot->dq_itime : MAX_IQ_TIME;
+                                       vfsmnt->mnt_bexp[type] = (dquot) ? dquot->dq_btime : MAX_DQ_TIME;
+                                       dqput(dquot);
+                                       vfsmnt->mnt_sb->dq_op = &dquot_operations;
+                                       add_dquot_ref(dev, type);
+                                       return(0);
+                               }
+                               put_write_access(inode);
+                       }
+               } else
+                       error = -EIO;
+         filp->f_count--;
+       } else
+               error = -EMFILE;
+       iput(inode);
+       return(error);
+}
+
+/*
+ * Ok this is the systemcall interface, this communicates with
+ * the userlevel programs. Currently this only supports diskquota
+ * calls. Maybe we need to add the process quotas etc in the future.
+ * But we probably better use rlimits for that.
+ */
+asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr)
+{
+       int cmds = 0, type = 0, flags = 0;
+       struct inode *ino;
+       kdev_t dev;
+
+       cmds = cmd >> SUBCMDSHIFT;
+       type = cmd & SUBCMDMASK;
+
+       if ((u_int) type >= MAXQUOTAS)
+               return(-EINVAL);
+       switch (cmds) {
+               case Q_SYNC:
+               case Q_GETSTATS:
+                       break;
+               case Q_GETQUOTA:
+                       if (((type == USRQUOTA && current->uid != id) ||
+                            (type == GRPQUOTA && current->gid != id)) && !fsuser())
+                               return(-EPERM);
+                       break;
+               default:
+                       if (!fsuser())
+                               return(-EPERM);
+       }
+
+       if (special == (char *)NULL && (cmds == Q_SYNC || cmds == Q_GETSTATS))
+               dev = 0;
+       else {
+               if (namei(special, &ino))
+                       return(-EINVAL);
+               dev = ino->i_rdev;
+               if (!S_ISBLK(ino->i_mode)) {
+                       iput(ino);
+                       return(-ENOTBLK);
+               }
+               iput(ino);
+       }
+
+       switch (cmds) {
+               case Q_QUOTAON:
+                       return(quota_on(dev, type, (char *) addr));
+               case Q_QUOTAOFF:
+                       return(quota_off(dev, type));
+               case Q_GETQUOTA:
+                       return(get_quota(dev, id, type, (struct dqblk *) addr));
+               case Q_SETQUOTA:
+                       flags |= SET_QUOTA;
+                       break;
+               case Q_SETUSE:
+                       flags |= SET_USE;
+                       break;
+               case Q_SETQLIM:
+                       flags |= SET_QLIMIT;
+                       break;
+               case Q_SYNC:
+                       return(sync_dquots(dev, type));
+               case Q_GETSTATS:
+                       return(get_stats(addr));
+               default:
+                       return(-EINVAL);
+       }
+
+       flags |= QUOTA_SYSCALL;
+       if (has_quota_enabled(dev, type))
+               return(set_dqblk(dev, id, type, flags, (struct dqblk *) addr));
+       return(-ESRCH);
+}
index 57b0cf27fff45be5093d64d48d14f260d565612e..cc465bbe5a11062627826a612a43410075b357d5 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -666,7 +666,7 @@ restart_interp:
                goto exec_error2;
        }
        /* better not execute files which are being written to */
-       if (bprm.inode->i_wcount > 0) {
+       if (bprm.inode->i_writecount > 0) {
                retval = -ETXTBSY;
                goto exec_error2;
        }
index 5053c8a8f241bf22f63f906b003152843ade22af..9a136fbb5f2e6f2f8eecfb73f53262e1cbbcf5c4 100644 (file)
@@ -165,7 +165,7 @@ static inline int load_block_bitmap (struct super_block * sb,
        return load__block_bitmap (sb, block_group);
 }
 
-void ext2_free_blocks (struct super_block * sb, unsigned long block,
+void ext2_free_blocks (const struct inode * inode, unsigned long block,
                       unsigned long count)
 {
        struct buffer_head * bh;
@@ -174,9 +174,11 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block,
        unsigned long bit;
        unsigned long i;
        int bitmap_nr;
+       struct super_block * sb;
        struct ext2_group_desc * gdp;
        struct ext2_super_block * es;
 
+       sb = inode->i_sb;
        if (!sb) {
                printk ("ext2_free_blocks: nonexistent device");
                return;
@@ -224,6 +226,8 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block,
                                      "bit already cleared for block %lu", 
                                      block);
                else {
+                       if (sb->dq_op)
+                               sb->dq_op->free_block(inode, fs_to_dq_blocks(1, inode->i_blksize));
                        gdp->bg_free_blocks_count++;
                        es->s_free_blocks_count++;
                }
@@ -249,21 +253,23 @@ void ext2_free_blocks (struct super_block * sb, unsigned long block,
  * each block group the search first looks for an entire free byte in the block
  * bitmap, and then for any free bit if that fails.
  */
-int ext2_new_block (struct super_block * sb, unsigned long goal,
-                   u32 * prealloc_count,
-                   u32 * prealloc_block)
+int ext2_new_block (const struct inode * inode, unsigned long goal,
+                   u32 * prealloc_count, u32 * prealloc_block, int * err)
 {
        struct buffer_head * bh;
        struct buffer_head * bh2;
        char * p, * r;
        int i, j, k, tmp;
        int bitmap_nr;
+       struct super_block * sb;
        struct ext2_group_desc * gdp;
        struct ext2_super_block * es;
 
+       *err = -ENOSPC;
 #ifdef EXT2FS_DEBUG
        static int goal_hits = 0, goal_attempts = 0;
 #endif
+       sb = inode->i_sb;
        if (!sb) {
                printk ("ext2_new_block: nonexistent device");
                return 0;
@@ -394,6 +400,16 @@ got_block:
 
        ext2_debug ("using block group %d(%d)\n", i, gdp->bg_free_blocks_count);
 
+       /*
+        * Check quota for allocation of this block.
+        */
+       if (sb->dq_op)
+               if (sb->dq_op->alloc_block (inode, fs_to_dq_blocks(1, inode->i_blksize))) {
+                       unlock_super (sb);
+                       *err = -EDQUOT;
+                       return 0;
+               }
+
        tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + es->s_first_data_block;
 
        if (test_opt (sb, CHECK_STRICT) &&
@@ -407,6 +423,8 @@ got_block:
        if (set_bit (j, bh->b_data)) {
                ext2_warning (sb, "ext2_new_block",
                              "bit already set for block %d", j);
+               if (sb->dq_op)
+                       sb->dq_op->free_block(inode, fs_to_dq_blocks(1, inode->i_blksize));
                goto repeat;
        }
 
@@ -421,8 +439,14 @@ got_block:
                *prealloc_block = tmp + 1;
                for (k = 1;
                     k < 8 && (j + k) < EXT2_BLOCKS_PER_GROUP(sb); k++) {
-                       if (set_bit (j + k, bh->b_data))
+                       if (sb->dq_op)
+                               if (sb->dq_op->alloc_block(inode, fs_to_dq_blocks(1, inode->i_blksize)))
+                                       break;
+                       if (set_bit (j + k, bh->b_data)) {
+                               if (sb->dq_op)
+                                       sb->dq_op->free_block(inode, fs_to_dq_blocks(1, inode->i_blksize));
                                break;
+                       }
                        (*prealloc_count)++;
                }       
                gdp->bg_free_blocks_count -= *prealloc_count;
@@ -466,6 +490,7 @@ got_block:
        mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
        sb->s_dirt = 1;
        unlock_super (sb);
+       *err = 0;
        return j;
 }
 
index 9102d02b2d96283c5e574143d87c6c2811fc4dba..f102deff453ba0194550790bb21396ae5376dea3 100644 (file)
@@ -252,7 +252,8 @@ void ext2_free_inode (struct inode * inode)
                ll_rw_block (WRITE, 1, &bh);
                wait_on_buffer (bh);
        }
-
+       if (sb->dq_op)
+               sb->dq_op->free_inode (inode, 1);
        sb->s_dirt = 1;
        clear_inode (inode);
        unlock_super (sb);
@@ -303,7 +304,7 @@ static void inc_inode_version (struct inode * inode,
  * For other inodes, search forward from the parent directory\'s block
  * group to find a free inode.
  */
-struct inode * ext2_new_inode (const struct inode * dir, int mode)
+struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err)
 {
        struct super_block * sb;
        struct buffer_head * bh;
@@ -325,6 +326,7 @@ struct inode * ext2_new_inode (const struct inode * dir, int mode)
 repeat:
        gdp = NULL; i=0;
        
+       *err = -ENOSPC;
        if (S_ISDIR(mode)) {
                avefreei = es->s_free_inodes_count /
                        sb->u.ext2_sb.s_groups_count;
@@ -481,9 +483,21 @@ repeat:
        insert_inode_hash(inode);
        inc_inode_version (inode, gdp, mode);
 
+       unlock_super (sb);
+       if (sb->dq_op) {
+               sb->dq_op->initialize (inode, -1);
+               if (sb->dq_op->alloc_inode (inode, 1)) {
+                       sb->dq_op->drop (inode);
+                       inode->i_nlink = 0;
+                       iput (inode);
+                       *err = -EDQUOT;
+                       return NULL;
+               }
+               inode->i_flags |= S_WRITE;
+       }
        ext2_debug ("allocating inode %lu\n", inode->i_ino);
 
-       unlock_super (sb);
+       *err = 0;
        return inode;
 }
 
index 70e1f8b8c428e7ba890b637a04fc0ce4952ce70b..52fa4710723f67da386c2b85f64f7a5039b7ca57 100644 (file)
@@ -62,17 +62,17 @@ static int block_bmap (struct buffer_head * bh, int nr)
 void ext2_discard_prealloc (struct inode * inode)
 {
 #ifdef EXT2_PREALLOCATE
+       unsigned short total;
+
        if (inode->u.ext2_i.i_prealloc_count) {
-               int i = inode->u.ext2_i.i_prealloc_count;
+               total = inode->u.ext2_i.i_prealloc_count;
                inode->u.ext2_i.i_prealloc_count = 0;
-               ext2_free_blocks (inode->i_sb,
-                                 inode->u.ext2_i.i_prealloc_block,
-                                 i);
+               ext2_free_blocks (inode, inode->u.ext2_i.i_prealloc_block, total);
        }
 #endif
 }
 
-static int ext2_alloc_block (struct inode * inode, unsigned long goal)
+static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err)
 {
 #ifdef EXT2FS_DEBUG
        static unsigned long alloc_hits = 0, alloc_attempts = 0;
@@ -110,15 +110,14 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal)
                ext2_debug ("preallocation miss (%lu/%lu).\n",
                            alloc_hits, ++alloc_attempts);
                if (S_ISREG(inode->i_mode))
-                       result = ext2_new_block
-                               (inode->i_sb, goal,
+                       result = ext2_new_block (inode, goal,
                                 &inode->u.ext2_i.i_prealloc_count,
-                                &inode->u.ext2_i.i_prealloc_block);
+                                &inode->u.ext2_i.i_prealloc_block, err);
                else
-                       result = ext2_new_block (inode->i_sb, goal, 0, 0);
+                       result = ext2_new_block (inode, goal, 0, 0, err);
        }
 #else
-       result = ext2_new_block (inode->i_sb, goal, 0, 0);
+       result = ext2_new_block (inode, goal, 0, 0, err);
 #endif
 
        return result;
@@ -225,12 +224,12 @@ repeat:
 
        ext2_debug ("goal = %d.\n", goal);
 
-       tmp = ext2_alloc_block (inode, goal);
+       tmp = ext2_alloc_block (inode, goal, err);
        if (!tmp)
                return NULL;
        result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
        if (*p) {
-               ext2_free_blocks (inode->i_sb, tmp, 1);
+               ext2_free_blocks (inode, tmp, 1);
                brelse (result);
                goto repeat;
        }
@@ -297,14 +296,14 @@ repeat:
                if (!goal)
                        goal = bh->b_blocknr;
        }
-       tmp = ext2_alloc_block (inode, goal);
+       tmp = ext2_alloc_block (inode, goal, err);
        if (!tmp) {
                brelse (bh);
                return NULL;
        }
        result = getblk (bh->b_dev, tmp, blocksize);
        if (*p) {
-               ext2_free_blocks (inode->i_sb, tmp, 1);
+               ext2_free_blocks (inode, tmp, 1);
                brelse (result);
                goto repeat;
        }
index b3410dea8f43fe4f3f3857d3a21930f9b727ec90..46d1e23754486b47445f23d7da9dda6e4e4cb99d 100644 (file)
@@ -375,10 +375,10 @@ int ext2_create (struct inode * dir,const char * name, int len, int mode,
        *result = NULL;
        if (!dir)
                return -ENOENT;
-       inode = ext2_new_inode (dir, mode);
+       inode = ext2_new_inode (dir, mode, &err);
        if (!inode) {
                iput (dir);
-               return -ENOSPC;
+               return err;
        }
        inode->i_op = &ext2_file_inode_operations;
        inode->i_mode = mode;
@@ -421,10 +421,10 @@ int ext2_mknod (struct inode * dir, const char * name, int len, int mode,
                iput (dir);
                return -EEXIST;
        }
-       inode = ext2_new_inode (dir, mode);
+       inode = ext2_new_inode (dir, mode, &err);
        if (!inode) {
                iput (dir);
-               return -ENOSPC;
+               return err;
        }
        inode->i_uid = current->fsuid;
        inode->i_mode = mode;
@@ -488,10 +488,10 @@ int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
                iput (dir);
                return -EMLINK;
        }
-       inode = ext2_new_inode (dir, S_IFDIR);
+       inode = ext2_new_inode (dir, S_IFDIR, &err);
        if (!inode) {
                iput (dir);
-               return -ENOSPC;
+               return err;
        }
        inode->i_op = &ext2_dir_inode_operations;
        inode->i_size = inode->i_sb->s_blocksize;
@@ -622,6 +622,8 @@ repeat:
        retval = -EPERM;
        if (!(inode = iget (dir->i_sb, de->inode)))
                goto end_rmdir;
+       if (inode->i_sb->dq_op)
+               inode->i_sb->dq_op->initialize (inode, -1);
        if (inode->i_dev != dir->i_dev)
                goto end_rmdir;
        if (de->inode != inode->i_ino) {
@@ -702,6 +704,8 @@ repeat:
                goto end_unlink;
        if (!(inode = iget (dir->i_sb, de->inode)))
                goto end_unlink;
+       if (inode->i_sb->dq_op)
+               inode->i_sb->dq_op->initialize (inode, -1);
        retval = -EPERM;
        if (S_ISDIR(inode->i_mode))
                goto end_unlink;
@@ -757,9 +761,9 @@ int ext2_symlink (struct inode * dir, const char * name, int len,
        int l;
        char c;
 
-       if (!(inode = ext2_new_inode (dir, S_IFLNK))) {
+       if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
                iput (dir);
-               return -ENOSPC;
+               return err;
        }
        inode->i_mode = S_IFLNK | S_IRWXUGO;
        inode->i_op = &ext2_symlink_inode_operations;
@@ -795,6 +799,7 @@ int ext2_symlink (struct inode * dir, const char * name, int len,
        }
        inode->i_size = i;
        inode->i_dirt = 1;
+
        bh = ext2_find_entry (dir, name, len, &de);
        if (bh) {
                inode->i_nlink--;
@@ -967,6 +972,9 @@ start_up:
                if (!new_inode) {
                        brelse (new_bh);
                        new_bh = NULL;
+               } else {
+                       if (new_inode->i_sb->dq_op)
+                               new_inode->i_sb->dq_op->initialize (new_inode, -1);
                }
        }
        if (new_inode == old_inode) {
index 302849677f0d598bfc669af49b94395fc8bb5231..c069e49bff197169a1e44c7a5aa5fdd4f9b18953 100644 (file)
@@ -249,6 +249,12 @@ static int parse_options (char * options, unsigned long * sb_block,
                                return 0;
                        }
                }
+               /* Silently ignore the quota options */
+               else if (!strcmp (this_char, "grpquota")
+                        || !strcmp (this_char, "noquota")
+                        || !strcmp (this_char, "quota")
+                        || !strcmp (this_char, "usrquota"))
+                       /* Don't do anything ;-) */ ;
                else {
                        printk ("EXT2-fs: Unrecognized mount option %s\n", this_char);
                        return 0;
index 7cf0a5dbcd30edee464515820bb57f3dbce6fbd8..500b7dca43700578248e6e31bb44f2585f5fb168 100644 (file)
@@ -96,14 +96,14 @@ repeat:
                } else if (free_count > 0 && block_to_free == tmp - free_count)
                        free_count++;
                else {
-                       ext2_free_blocks (inode->i_sb, block_to_free, free_count);
+                       ext2_free_blocks (inode, block_to_free, free_count);
                        block_to_free = tmp;
                        free_count = 1;
                }
-/*             ext2_free_blocks (inode->i_sb, tmp, 1); */
+/*             ext2_free_blocks (inode, tmp, 1); */
        }
        if (free_count > 0)
-               ext2_free_blocks (inode->i_sb, block_to_free, free_count);
+               ext2_free_blocks (inode, block_to_free, free_count);
        return retry;
 }
 
@@ -163,16 +163,16 @@ repeat:
                } else if (free_count > 0 && block_to_free == tmp - free_count)
                        free_count++;
                else {
-                       ext2_free_blocks (inode->i_sb, block_to_free, free_count);
+                       ext2_free_blocks (inode, block_to_free, free_count);
                        block_to_free = tmp;
                        free_count = 1;
                }
-/*             ext2_free_blocks (inode->i_sb, tmp, 1); */
+/*             ext2_free_blocks (inode, tmp, 1); */
                inode->i_blocks -= blocks;
                inode->i_dirt = 1;
        }
        if (free_count > 0)
-               ext2_free_blocks (inode->i_sb, block_to_free, free_count);
+               ext2_free_blocks (inode, block_to_free, free_count);
        ind = (u32 *) ind_bh->b_data;
        for (i = 0; i < addr_per_block; i++)
                if (*(ind++))
@@ -185,7 +185,7 @@ repeat:
                        *p = 0;
                        inode->i_blocks -= blocks;
                        inode->i_dirt = 1;
-                       ext2_free_blocks (inode->i_sb, tmp, 1);
+                       ext2_free_blocks (inode, tmp, 1);
                }
        if (IS_SYNC(inode) && buffer_dirty(ind_bh)) {
                ll_rw_block (WRITE, 1, &ind_bh);
@@ -245,7 +245,7 @@ repeat:
                        *p = 0;
                        inode->i_blocks -= blocks;
                        inode->i_dirt = 1;
-                       ext2_free_blocks (inode->i_sb, tmp, 1);
+                       ext2_free_blocks (inode, tmp, 1);
                }
        if (IS_SYNC(inode) && buffer_dirty(dind_bh)) {
                ll_rw_block (WRITE, 1, &dind_bh);
@@ -304,7 +304,7 @@ repeat:
                        *p = 0;
                        inode->i_blocks -= blocks;
                        inode->i_dirt = 1;
-                       ext2_free_blocks (inode->i_sb, tmp, 1);
+                       ext2_free_blocks (inode, tmp, 1);
                }
        if (IS_SYNC(inode) && buffer_dirty(tind_bh)) {
                ll_rw_block (WRITE, 1, &tind_bh);
index a088e48ec73cb5ab6555e29d8771f0d8b35ed616..30802aec618ba49cfe8a19efb1a11cdda3c4eff9 100644 (file)
@@ -119,3 +119,33 @@ struct file * get_empty_filp(void)
 
        return NULL;
 }
+
+void add_dquot_ref(dev_t dev, short type)
+{
+       struct file *filp;
+       int cnt;
+
+       for (filp = first_file, cnt = 0; cnt < nr_files; cnt++, filp = filp->f_next) {
+               if (!filp->f_count || !filp->f_inode || filp->f_inode->i_dev != dev)
+                       continue;
+               if (filp->f_mode & FMODE_WRITE && filp->f_inode->i_sb->dq_op) {
+                       filp->f_inode->i_sb->dq_op->initialize(filp->f_inode, type);
+                       filp->f_inode->i_flags |= S_WRITE;
+               }
+       }
+}
+
+void reset_dquot_ptrs(dev_t dev, short type)
+{
+       struct file *filp;
+       int cnt;
+
+       for (filp = first_file, cnt = 0; cnt < nr_files; cnt++, filp = filp->f_next) {
+               if (!filp->f_count || !filp->f_inode || filp->f_inode->i_dev != dev)
+                       continue;
+               if (IS_WRITABLE(filp->f_inode)) {
+                       filp->f_inode->i_dquot[type] = NODQUOT;
+                       filp->f_inode->i_flags &= ~S_WRITE;
+               }
+       }
+}
index 5edae650f26c82e13a85882a916abaab9dd83a08..9001901d1d30ee9196dd6f27c1247c867159fdc7 100644 (file)
@@ -600,7 +600,7 @@ static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
                                return 0;
                }
                else
-                       return 0;
+                       return 1;
        }
 
        return 1;
index 14e9770b1f7564ba88ef5f1456535509f7181e27..9fbfd4725807f991193b6848fc41aa8fa9b9352a 100644 (file)
@@ -149,6 +149,10 @@ void clear_inode(struct inode * inode)
        struct wait_queue * wait;
 
        wait_on_inode(inode);
+       if (IS_WRITABLE(inode)) {
+               if (inode->i_sb->dq_op)
+                       inode->i_sb->dq_op->drop(inode);
+       }
        remove_inode_hash(inode);
        remove_inode_free(inode);
        wait = ((volatile struct inode *) inode)->i_wait;
@@ -389,28 +393,38 @@ repeat:
                inode->i_count--;
                return;
        }
+
        wake_up(&inode_wait);
        if (inode->i_pipe) {
                unsigned long page = (unsigned long) PIPE_BASE(*inode);
                PIPE_BASE(*inode) = NULL;
                free_page(page);
        }
+
        if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->put_inode) {
                inode->i_sb->s_op->put_inode(inode);
                if (!inode->i_nlink)
                        return;
        }
+
        if (inode->i_dirt) {
                write_inode(inode);     /* we can sleep - so do again */
                wait_on_inode(inode);
                goto repeat;
        }
+
        inode->i_count--;
+       if (IS_WRITABLE(inode)) {
+               if (inode->i_sb->dq_op)
+                       inode->i_sb->dq_op->drop(inode);
+       }
+
        if (inode->i_mmap) {
                printk("iput: inode %lu on device %s still has mappings.\n",
                        inode->i_ino, kdevname(inode->i_dev));
                inode->i_mmap = NULL;
        }
+
        nr_free_inodes++;
        return;
 }
@@ -499,7 +513,7 @@ struct inode * get_pipe_inode(void)
        return inode;
 }
 
-struct inode * __iget(struct super_block * sb, int nr, int crossmntp)
+struct inode *__iget(struct super_block * sb, int nr, int crossmntp)
 {
        static struct wait_queue * update_wait = NULL;
        struct inode_hash_entry * h;
@@ -526,7 +540,6 @@ repeat:
        inode->i_sb = sb;
        inode->i_dev = sb->s_dev;
        inode->i_ino = nr;
-       inode->i_flags = sb->s_flags;
        put_last_free(inode);
        insert_inode_hash(inode);
        read_inode(inode);
index ef0ec41951a8aa02ba064ebc1d75c3aea58ea5b0..0399ba1f40142480c120cea0867a181e42357586 100644 (file)
@@ -142,7 +142,7 @@ static int parse_options(char *options, struct iso9660_options * popt)
                    break;
                  }
                }
-               else return 0;
+               else return 1;
        }
        return 1;
 }
index 4ede33144887a9e0798ce576ca7aaeb771942bab..3f4342e4848204c1a0f1079791febd799d9a3d0f 100644 (file)
@@ -155,7 +155,7 @@ static int parse_options(char *options,char *check,char *conversion,uid_t *uid,
                                printk ("MSDOS FS: Invalid blocksize (512 or 1024)\n");
                        }
                }
-               else return 0;
+               else return 1;
        }
        return 1;
 }
index de9d8551062583ba723e54e994965b3bcb61ae29..e2f9c0e4d83ee78e0afea75dceae507f0c3fcd76 100644 (file)
@@ -136,13 +136,13 @@ int get_write_access(struct inode * inode)
                                        return -ETXTBSY;
                        }
                }
-       inode->i_wcount++;
+       inode->i_writecount++;
        return 0;
 }
 
 void put_write_access(struct inode * inode)
 {
-       inode->i_wcount--;
+       inode->i_writecount--;
 }
 
 /*
@@ -151,7 +151,7 @@ void put_write_access(struct inode * inode)
  * fathers (pseudo-roots, mount-points)
  */
 int lookup(struct inode * dir,const char * name, int len,
-       struct inode ** result)
+           struct inode ** result)
 {
        struct super_block * sb;
        int perm;
@@ -185,7 +185,7 @@ int lookup(struct inode * dir,const char * name, int len,
                *result = dir;
                return 0;
        }
-       return dir->i_op->lookup(dir,name,len,result);
+       return dir->i_op->lookup(dir, name, len, result);
 }
 
 int follow_link(struct inode * dir, struct inode * inode,
@@ -211,8 +211,8 @@ int follow_link(struct inode * dir, struct inode * inode,
  * dir_namei() returns the inode of the directory of the
  * specified name, and the name within that directory.
  */
-static int dir_namei(const char * pathname, int * namelen, const char ** name,
-       struct inode * base, struct inode ** res_inode)
+static int dir_namei(const char *pathname, int *namelen, const char **name,
+                     struct inode * base, struct inode **res_inode)
 {
        char c;
        const char * thisname;
@@ -237,7 +237,7 @@ static int dir_namei(const char * pathname, int * namelen, const char ** name,
                if (!c)
                        break;
                base->i_count++;
-               error = lookup(base,thisname,len,&inode);
+               error = lookup(base, thisname, len, &inode);
                if (error) {
                        iput(base);
                        return error;
@@ -257,24 +257,24 @@ static int dir_namei(const char * pathname, int * namelen, const char ** name,
 }
 
 static int _namei(const char * pathname, struct inode * base,
-       int follow_links, struct inode ** res_inode)
+                  int follow_links, struct inode ** res_inode)
 {
-       const char * basename;
+       const char *basename;
        int namelen,error;
        struct inode * inode;
 
        *res_inode = NULL;
-       error = dir_namei(pathname,&namelen,&basename,base,&base);
+       error = dir_namei(pathname, &namelen, &basename, base, &base);
        if (error)
                return error;
        base->i_count++;        /* lookup uses up base */
-       error = lookup(base,basename,namelen,&inode);
+       error = lookup(base, basename, namelen, &inode);
        if (error) {
                iput(base);
                return error;
        }
        if (follow_links) {
-               error = follow_link(base,inode,0,0,&inode);
+               error = follow_link(base, inode, 0, 0, &inode);
                if (error)
                        return error;
        } else
@@ -283,14 +283,14 @@ static int _namei(const char * pathname, struct inode * base,
        return 0;
 }
 
-int lnamei(const char * pathname, struct inode ** res_inode)
+int lnamei(const char *pathname, struct inode **res_inode)
 {
        int error;
        char * tmp;
 
-       error = getname(pathname,&tmp);
+       error = getname(pathname, &tmp);
        if (!error) {
-               error = _namei(tmp,NULL,0,res_inode);
+               error = _namei(tmp, NULL, 0, res_inode);
                putname(tmp);
        }
        return error;
@@ -303,14 +303,14 @@ int lnamei(const char * pathname, struct inode ** res_inode)
  * Open, link etc use their own routines, but this is enough for things
  * like 'chmod' etc.
  */
-int namei(const char * pathname, struct inode ** res_inode)
+int namei(const char *pathname, struct inode **res_inode)
 {
        int error;
        char * tmp;
 
-       error = getname(pathname,&tmp);
+       error = getname(pathname, &tmp);
        if (!error) {
-               error = _namei(tmp,NULL,1,res_inode);
+               error = _namei(tmp, NULL, 1, res_inode);
                putname(tmp);
        }
        return error;
@@ -330,7 +330,7 @@ int namei(const char * pathname, struct inode ** res_inode)
  * for symlinks (where the permissions are checked later).
  */
 int open_namei(const char * pathname, int flag, int mode,
-       struct inode ** res_inode, struct inode * base)
+               struct inode ** res_inode, struct inode * base)
 {
        const char * basename;
        int namelen,error;
@@ -338,7 +338,7 @@ int open_namei(const char * pathname, int flag, int mode,
 
        mode &= S_IALLUGO & ~current->fs->umask;
        mode |= S_IFREG;
-       error = dir_namei(pathname,&namelen,&basename,base,&dir);
+       error = dir_namei(pathname, &namelen, &basename, base, &dir);
        if (error)
                return error;
        if (!namelen) {                 /* special case: '/usr/' etc */
@@ -357,7 +357,7 @@ int open_namei(const char * pathname, int flag, int mode,
        dir->i_count++;         /* lookup eats the dir */
        if (flag & O_CREAT) {
                down(&dir->i_sem);
-               error = lookup(dir,basename,namelen,&inode);
+               error = lookup(dir, basename, namelen, &inode);
                if (!error) {
                        if (flag & O_EXCL) {
                                iput(inode);
@@ -371,14 +371,16 @@ int open_namei(const char * pathname, int flag, int mode,
                        error = -EROFS;
                else {
                        dir->i_count++;         /* create eats the dir */
-                       error = dir->i_op->create(dir,basename,namelen,mode,res_inode);
+                       if (dir->i_sb->dq_op)
+                               dir->i_sb->dq_op->initialize(dir, -1);
+                       error = dir->i_op->create(dir, basename, namelen, mode, res_inode);
                        up(&dir->i_sem);
                        iput(dir);
                        return error;
                }
                up(&dir->i_sem);
        } else
-               error = lookup(dir,basename,namelen,&inode);
+               error = lookup(dir, basename, namelen, &inode);
        if (error) {
                iput(dir);
                return error;
@@ -409,7 +411,7 @@ int open_namei(const char * pathname, int flag, int mode,
        /*
         * An append-only file must be opened in append mode for writing
         */
-       if (IS_APPEND(inode) && ((flag & 2) && !(flag & O_APPEND))) {
+       if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) {
                iput(inode);
                return -EPERM;
        }
@@ -420,6 +422,8 @@ int open_namei(const char * pathname, int flag, int mode,
                        iput(inode);
                        return error;
                }
+               if (inode->i_sb->dq_op)
+                       inode->i_sb->dq_op->initialize(inode, -1);
                newattrs.ia_size = 0;
                newattrs.ia_valid = ATTR_SIZE;
                if ((error = notify_change(inode, &newattrs))) {
@@ -434,7 +438,10 @@ int open_namei(const char * pathname, int flag, int mode,
                up(&inode->i_sem);
                inode->i_dirt = 1;
                put_write_access(inode);
-       }
+       } else
+               if (flag & FMODE_WRITE)
+                       if (inode->i_sb->dq_op)
+                               inode->i_sb->dq_op->initialize(inode, -1);
        *res_inode = inode;
        return 0;
 }
@@ -446,7 +453,7 @@ int do_mknod(const char * filename, int mode, dev_t dev)
        struct inode * dir;
 
        mode &= ~current->fs->umask;
-       error = dir_namei(filename,&namelen,&basename, NULL, &dir);
+       error = dir_namei(filename, &namelen, &basename, NULL, &dir);
        if (error)
                return error;
        if (!namelen) {
@@ -466,6 +473,8 @@ int do_mknod(const char * filename, int mode, dev_t dev)
                return -EPERM;
        }
        dir->i_count++;
+       if (dir->i_sb->dq_op)
+               dir->i_sb->dq_op->initialize(dir, -1);
        down(&dir->i_sem);
        error = dir->i_op->mknod(dir,basename,namelen,mode,dev);
        up(&dir->i_sem);
@@ -503,7 +512,7 @@ static int do_mkdir(const char * pathname, int mode)
        int namelen, error;
        struct inode * dir;
 
-       error = dir_namei(pathname,&namelen,&basename,NULL,&dir);
+       error = dir_namei(pathname, &namelen, &basename, NULL, &dir);
        if (error)
                return error;
        if (!namelen) {
@@ -523,6 +532,8 @@ static int do_mkdir(const char * pathname, int mode)
                return -EPERM;
        }
        dir->i_count++;
+       if (dir->i_sb->dq_op)
+               dir->i_sb->dq_op->initialize(dir, -1);
        down(&dir->i_sem);
        error = dir->i_op->mkdir(dir, basename, namelen, mode & 0777 & ~current->fs->umask);
        up(&dir->i_sem);
@@ -549,7 +560,7 @@ static int do_rmdir(const char * name)
        int namelen, error;
        struct inode * dir;
 
-       error = dir_namei(name,&namelen,&basename,NULL,&dir);
+       error = dir_namei(name, &namelen, &basename, NULL, &dir);
        if (error)
                return error;
        if (!namelen) {
@@ -575,6 +586,8 @@ static int do_rmdir(const char * name)
                iput(dir);
                return -EPERM;
        }
+       if (dir->i_sb->dq_op)
+               dir->i_sb->dq_op->initialize(dir, -1);
        return dir->i_op->rmdir(dir,basename,namelen);
 }
 
@@ -597,7 +610,7 @@ static int do_unlink(const char * name)
        int namelen, error;
        struct inode * dir;
 
-       error = dir_namei(name,&namelen,&basename,NULL,&dir);
+       error = dir_namei(name, &namelen, &basename, NULL, &dir);
        if (error)
                return error;
        if (!namelen) {
@@ -623,6 +636,8 @@ static int do_unlink(const char * name)
                iput(dir);
                return -EPERM;
        }
+       if (dir->i_sb->dq_op)
+               dir->i_sb->dq_op->initialize(dir, -1);
        return dir->i_op->unlink(dir,basename,namelen);
 }
 
@@ -645,7 +660,7 @@ static int do_symlink(const char * oldname, const char * newname)
        const char * basename;
        int namelen, error;
 
-       error = dir_namei(newname,&namelen,&basename,NULL,&dir);
+       error = dir_namei(newname, &namelen, &basename, NULL, &dir);
        if (error)
                return error;
        if (!namelen) {
@@ -665,6 +680,8 @@ static int do_symlink(const char * oldname, const char * newname)
                return -EPERM;
        }
        dir->i_count++;
+       if (dir->i_sb->dq_op)
+               dir->i_sb->dq_op->initialize(dir, -1);
        down(&dir->i_sem);
        error = dir->i_op->symlink(dir,basename,namelen,oldname);
        up(&dir->i_sem);
@@ -695,7 +712,7 @@ static int do_link(struct inode * oldinode, const char * newname)
        const char * basename;
        int namelen, error;
 
-       error = dir_namei(newname,&namelen,&basename,NULL,&dir);
+       error = dir_namei(newname, &namelen, &basename, NULL, &dir);
        if (error) {
                iput(oldinode);
                return error;
@@ -734,6 +751,8 @@ static int do_link(struct inode * oldinode, const char * newname)
                return -EPERM;
        }
        dir->i_count++;
+       if (dir->i_sb->dq_op)
+               dir->i_sb->dq_op->initialize(dir, -1);
        down(&dir->i_sem);
        error = dir->i_op->link(oldinode, dir, basename, namelen);
        up(&dir->i_sem);
@@ -766,7 +785,7 @@ static int do_rename(const char * oldname, const char * newname)
        const char * old_base, * new_base;
        int old_len, new_len, error;
 
-       error = dir_namei(oldname,&old_len,&old_base,NULL,&old_dir);
+       error = dir_namei(oldname, &old_len, &old_base, NULL, &old_dir);
        if (error)
                return error;
        if ((error = permission(old_dir,MAY_WRITE | MAY_EXEC)) != 0) {
@@ -779,7 +798,7 @@ static int do_rename(const char * oldname, const char * newname)
                iput(old_dir);
                return -EPERM;
        }
-       error = dir_namei(newname,&new_len,&new_base,NULL,&new_dir);
+       error = dir_namei(newname, &new_len, &new_base, NULL, &new_dir);
        if (error) {
                iput(old_dir);
                return error;
@@ -820,6 +839,8 @@ static int do_rename(const char * oldname, const char * newname)
                return -EPERM;
        }
        new_dir->i_count++;
+       if (new_dir->i_sb->dq_op)
+               new_dir->i_sb->dq_op->initialize(new_dir, -1);
        down(&new_dir->i_sem);
        error = old_dir->i_op->rename(old_dir, old_base, old_len, 
                new_dir, new_base, new_len);
index 30613a95cef67794a4746da59329f2ffcf8c22a6..25aed5e958f3b93f6d9352b639650b879bc85a7e 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -122,7 +122,7 @@ asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length)
                return -EBADF;
        if (!(inode = file->f_inode))
                return -ENOENT;
-       if (S_ISDIR(inode->i_mode) || !(file->f_mode & 2))
+       if (S_ISDIR(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
                return -EACCES;
        if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
                return -EPERM;
@@ -481,7 +481,7 @@ int do_open(const char * filename,int flags,int mode)
        error = open_namei(filename,flag,mode,&inode,NULL);
        if (error)
                goto cleanup_file;
-       if (f->f_mode & 2) {
+       if (f->f_mode & FMODE_WRITE) {
                error = get_write_access(inode);
                if (error)
                        goto cleanup_inode;
@@ -516,7 +516,7 @@ int do_open(const char * filename,int flags,int mode)
        if (f->f_op && f->f_op->release)
                f->f_op->release(inode,f);
 cleanup_all:
-       if (f->f_mode & 2)
+       if (f->f_mode & FMODE_WRITE)
                put_write_access(inode);
 cleanup_inode:
        iput(inode);
@@ -562,7 +562,8 @@ int close_fp(struct file *filp)
                filp->f_op->release(inode,filp);
        filp->f_count--;
        filp->f_inode = NULL;
-       if (filp->f_mode & 2) put_write_access(inode);
+       if (filp->f_mode & FMODE_WRITE)
+               put_write_access(inode);
        iput(inode);
        return 0;
 }
index bb8b4bcefc8be93e21ce9b2c7f7bf9ef175334b5..2542f089214788f59013b7bd96ea1d727d13f718 100644 (file)
@@ -41,6 +41,9 @@
 #include <linux/ioport.h>
 #include <linux/config.h>
 #include <linux/mm.h>
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+#endif
 
 #include <asm/segment.h>
 #include <asm/pgtable.h>
@@ -457,7 +460,7 @@ static int get_stat(int pid, char * buffer)
        if (tsk->state < 0 || tsk->state > 5)
                state = '.';
        else
-               state = "RSDZTD"[tsk->state];
+               state = "RSDZTW"[tsk->state];
        vsize = eip = esp = 0;
        if (tsk->mm) {
                struct vm_area_struct *vma = tsk->mm->mmap;
@@ -820,6 +823,10 @@ static int get_root_array(char * page, int type, char **start, off_t offset, int
 
                case PROC_IOPORTS:
                        return get_ioport_list(page);
+#ifdef CONFIG_APM
+               case PROC_APM:
+                       return apm_proc(page);
+#endif
        }
        return -EBADF;
 }
index 47b80a414022c502ced503c58c7d55f5d8fa72b5..f10768a8829c278e6cd2120617afd13aec4469e8 100644 (file)
@@ -66,7 +66,7 @@ static int parse_options(char *options,uid_t *uid,gid_t *gid)
                        if (*value)
                                return 0;
                }
-               else return 0;
+               else return 1;
        }
        return 1;
 }
index 9fa4cbaa812aed375706de51f49dbc8e6d63046c..a7f23d2121c1810c4bf13a9037f45ee06450981d 100644 (file)
@@ -13,6 +13,9 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <linux/config.h>
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+#endif
 
 /*
  * Offset of the first process in the /proc root directory..
@@ -293,7 +296,13 @@ void proc_root_init(void)
                PROC_IOPORTS, 7, "ioports",
                S_IFREG | S_IRUGO, 1, 0, 0,
        });
-
+#ifdef CONFIG_APM
+       proc_register(&proc_root, &(struct proc_dir_entry) {
+               PROC_APM, 3, "apm",
+               S_IFREG | S_IRUGO, 1, 0, 0,
+       });
+#endif
+                  
        if (prof_shift) {
                proc_register(&proc_root, &(struct proc_dir_entry) {
                        PROC_PROFILE, 7, "profile",
index 0023f88a143d057b318b5efdfe3796b96a6d0a03..0fe338eb5e18db92a9429ff74007f88437d80e8a 100644 (file)
@@ -874,7 +874,6 @@ smb_unlink(struct inode *dir, const char *name, int len)
                 if ((error = smb_proc_unlink(SMB_SERVER(dir), path, len)) == 0)
                         smb_invalid_dir_cache(dir->i_ino);
         }
-
        iput(dir);
        return error;
 }
index 8fe79c7d5c45a492d8033a43dad5e3c41cfb6497..12b09510ce72ad84fdb5f04b4d688f53feba572d 100644 (file)
@@ -264,7 +264,7 @@ smb_verify(byte *packet, int command, int wct, int bcc)
 {
        return (SMB_CMD(packet) == command &&
                SMB_WCT(packet) >= wct &&
-               (bcc == -1 || SMB_BCC(packet) == bcc)) ? 0 : -EIO;
+               (bcc == -1 || SMB_BCC(packet) >= bcc)) ? 0 : -EIO;
 }
 
 static int
@@ -1139,7 +1139,8 @@ int
 smb_proc_readdir_long(struct smb_server *server, struct inode *dir, int fpos,
                       int cache_size, struct smb_dirent *entry)
 {
-        int max_matches = 512;
+        int max_matches = 64; /* this should actually be based on the 
+                                maxxmit */
   
         /* NT uses 260, OS/2 uses 2. Both accept 1. */
         int info_level = 1;
@@ -1599,6 +1600,10 @@ smb_proc_reconnect(struct smb_server *server)
 #endif
 #ifdef LANMAN2
           { PROTOCOL_LANMAN2,"LM1.2X002"},
+#endif
+#ifdef NT1
+         { PROTOCOL_NT1,"NT LM 0.12"},
+         { PROTOCOL_NT1,"NT LANMAN 1.0"},
 #endif
           {-1, NULL} };
        char dev[] = "A:";
@@ -1712,29 +1717,70 @@ smb_proc_reconnect(struct smb_server *server)
                 DPRINTK("smb_proc_connect: blkmode = %d\n",
                         WVAL(server->packet, smb_vwv5));
 
-                server->maxxmt = WVAL(server->packet, smb_vwv2);
-                server->maxmux = WVAL(server->packet, smb_vwv3);
-                server->maxvcs = WVAL(server->packet, smb_vwv4);
-                server->blkmode= WVAL(server->packet, smb_vwv5);
-                server->sesskey= DVAL(server->packet, smb_vwv6);
-
-                smb_setup_header(server, SMBsesssetupX, 10,
-                                 2 + userlen + passlen);
-
-                WSET(server->packet, smb_vwv0, 0x00ff);
-                WSET(server->packet, smb_vwv1, 0);
-                WSET(server->packet, smb_vwv2, given_max_xmit);
-                WSET(server->packet, smb_vwv3, 2);
-                WSET(server->packet, smb_vwv4, server->pid);
-                DSET(server->packet, smb_vwv5, server->sesskey);
-                WSET(server->packet, smb_vwv7, passlen + 1);
-                WSET(server->packet, smb_vwv8, 0);
-                WSET(server->packet, smb_vwv9, 0);
-
-                p = SMB_BUF(server->packet);
-                strcpy(p, server->m.password);
-                p += passlen + 1;
-                strcpy(p, server->m.username);
+               if (server->protocol >= PROTOCOL_NT1) {
+                       server->maxxmt = DVAL(server->packet,smb_vwv3+1);
+                       server->maxmux = WVAL(server->packet, smb_vwv1+1);
+                       server->maxvcs = WVAL(server->packet, smb_vwv2+1);
+                       server->blkmode= DVAL(server->packet, smb_vwv9+1);
+                       server->sesskey= DVAL(server->packet, smb_vwv7+1);
+               } else {
+                       server->maxxmt = WVAL(server->packet, smb_vwv2);
+                       server->maxmux = WVAL(server->packet, smb_vwv3);
+                       server->maxvcs = WVAL(server->packet, smb_vwv4);
+                       server->blkmode= WVAL(server->packet, smb_vwv5);
+                       server->sesskey= DVAL(server->packet, smb_vwv6);
+               }
+
+
+               if (server->protocol >= PROTOCOL_NT1) {
+                       char *workgroup = "WORKGROUP";
+                       char *OS_id = "Unix";
+                       char *client_id = "ksmbfs";
+
+                       smb_setup_header(server, SMBsesssetupX, 13,
+                                        5 + userlen + passlen +
+                                        strlen(workgroup) + strlen(OS_id) +
+                                        strlen(client_id));
+                       
+                       WSET(server->packet, smb_vwv0, 0x00ff);
+                       WSET(server->packet, smb_vwv1, 0);
+                       WSET(server->packet, smb_vwv2, given_max_xmit);
+                       WSET(server->packet, smb_vwv3, 2);
+                       WSET(server->packet, smb_vwv4, server->pid);
+                       DSET(server->packet, smb_vwv5, server->sesskey);
+                       WSET(server->packet, smb_vwv7, passlen + 1);
+                       WSET(server->packet, smb_vwv8, 0);
+                       WSET(server->packet, smb_vwv9, 0);
+
+                       p = SMB_BUF(server->packet);
+                       strcpy(p, server->m.password);
+                       p += passlen + 1;
+                       strcpy(p, server->m.username);
+                       p += userlen + 1;
+                       strcpy(p, workgroup);
+                       p += strlen(p) + 1;
+                       strcpy(p, OS_id);
+                       p += strlen(p) + 1;
+                       strcpy(p, client_id);
+               } else {
+                       smb_setup_header(server, SMBsesssetupX, 10,
+                                        2 + userlen + passlen);
+
+                       WSET(server->packet, smb_vwv0, 0x00ff);
+                       WSET(server->packet, smb_vwv1, 0);
+                       WSET(server->packet, smb_vwv2, given_max_xmit);
+                       WSET(server->packet, smb_vwv3, 2);
+                       WSET(server->packet, smb_vwv4, server->pid);
+                       DSET(server->packet, smb_vwv5, server->sesskey);
+                       WSET(server->packet, smb_vwv7, passlen + 1);
+                       WSET(server->packet, smb_vwv8, 0);
+                       WSET(server->packet, smb_vwv9, 0);
+
+                       p = SMB_BUF(server->packet);
+                       strcpy(p, server->m.password);
+                       p += passlen + 1;
+                       strcpy(p, server->m.username);
+               }
 
                 if ((result = smb_request_ok(server,SMBsesssetupX,3,0)) < 0) {
                         DPRINTK("smb_proc_connect: SMBsessetupX failed\n");
index 245a15cda3271a5cb361123e421495446015be6b..9893a76b3d8ae845fc56ba4e12cb95e34243bcd0 100644 (file)
@@ -2,18 +2,22 @@
  *  linux/fs/super.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-/*
- * super.c contains code to handle the super-block tables.
+ *
+ *  super.c contains code to handle: - mount structures
+ *                                   - super-block tables.
+ *                                   - mount systemcall
+ *                                   - umount systemcall
  *
  * GK 2/5/95  -  Changed to support mounting the root fs via NFS
  */
+
 #include <stdarg.h>
 
 #include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
+#include <linux/mount.h>
+#include <linux/malloc.h>
 #include <linux/major.h>
 #include <linux/stat.h>
 #include <linux/errno.h>
@@ -32,8 +36,6 @@ extern void wait_for_keypress(void);
 
 extern int root_mountflags;
 
-struct super_block super_blocks[NR_SUPER];
-
 static int do_remount_sb(struct super_block *sb, int flags, char * data);
 
 #ifdef CONFIG_ROOT_NFS
@@ -43,24 +45,118 @@ extern int nfs_root_mount(struct super_block *sb);
 /* this is initialized in init/main.c */
 kdev_t ROOT_DEV;
 
-static struct file_system_type * file_systems = NULL;
+struct super_block super_blocks[NR_SUPER];
+static struct file_system_type *file_systems = (struct file_system_type *) NULL;
+static struct vfsmount *vfsmntlist = (struct vfsmount *) NULL,
+                       *vfsmnttail = (struct vfsmount *) NULL,
+                       *mru_vfsmnt = (struct vfsmount *) NULL;
 
-int register_filesystem(struct file_system_type * fs)
+/* 
+ * This part handles the management of the list of mounted filesystems.
+ */
+struct vfsmount *lookup_vfsmnt(kdev_t dev)
 {
-       struct file_system_type ** tmp;
+       struct vfsmount *lptr;
 
-       if (!fs)
-               return -EINVAL;
-       if (fs->next)
-               return -EBUSY;
-       tmp = &file_systems;
-       while (*tmp) {
-               if (strcmp((*tmp)->name, fs->name) == 0)
-                       return -EBUSY;
-               tmp = &(*tmp)->next;
+       if (vfsmntlist == (struct vfsmount *)NULL)
+               return ((struct vfsmount *)NULL);
+
+       if (mru_vfsmnt != (struct vfsmount *)NULL &&
+           mru_vfsmnt->mnt_dev == dev)
+               return (mru_vfsmnt);
+
+       for (lptr = vfsmntlist;
+            lptr != (struct vfsmount *)NULL;
+            lptr = lptr->mnt_next)
+               if (lptr->mnt_dev == dev) {
+                       mru_vfsmnt = lptr;
+                       return (lptr);
+               }
+
+       return ((struct vfsmount *)NULL);
+       /* NOTREACHED */
+}
+
+struct vfsmount *add_vfsmnt(kdev_t dev, const char *dev_name, const char *dir_name)
+{
+       struct vfsmount *lptr;
+       char *tmp;
+
+       if ((lptr = (struct vfsmount *)
+            kmalloc(sizeof(struct vfsmount), GFP_KERNEL)) == (struct vfsmount *)NULL)
+               return ((struct vfsmount *)NULL);
+       memset(lptr, 0, sizeof(struct vfsmount));
+
+       lptr->mnt_dev = dev;
+       lptr->mnt_sem.count = 1;
+       if (dev_name && !getname(dev_name, &tmp)) {
+               if ((lptr->mnt_devname =
+                   (char *) kmalloc(strlen(tmp), GFP_KERNEL)) != (char *)NULL)
+                       strcpy(lptr->mnt_devname, tmp);
+               putname(tmp);
+       }
+       if (dir_name && !getname(dir_name, &tmp)) {
+               if ((lptr->mnt_dirname =
+                   (char *) kmalloc(strlen(tmp), GFP_KERNEL)) != (char *)NULL)
+                       strcpy(lptr->mnt_dirname, tmp);
+               putname(tmp);
+       }
+
+       if (vfsmntlist == (struct vfsmount *)NULL) {
+               vfsmntlist = vfsmnttail = lptr;
+       } else {
+               vfsmnttail->mnt_next = lptr;
+               vfsmnttail = lptr;
        }
-       *tmp = fs;
-       return 0;
+       return (lptr);
+}
+
+void remove_vfsmnt(kdev_t dev)
+{
+       struct vfsmount *lptr, *tofree;
+
+       if (vfsmntlist == (struct vfsmount *)NULL)
+               return;
+       lptr = vfsmntlist;
+       if (lptr->mnt_dev == dev) {
+               tofree = lptr;
+               vfsmntlist = lptr->mnt_next;
+               if (vfsmnttail->mnt_dev == dev)
+                       vfsmnttail = vfsmntlist;
+       } else {
+               while (lptr->mnt_next != (struct vfsmount *)NULL) {
+                       if (lptr->mnt_next->mnt_dev == dev)
+                               break;
+                       lptr = lptr->mnt_next;
+               }
+               tofree = lptr->mnt_next;
+               if (tofree == (struct vfsmount *)NULL)
+                       return;
+               lptr->mnt_next = lptr->mnt_next->mnt_next;
+               if (vfsmnttail->mnt_dev == dev)
+                       vfsmnttail = lptr;
+       }
+       kfree(tofree->mnt_devname);
+       kfree(tofree->mnt_dirname);
+       kfree_s(tofree, sizeof(struct vfsmount));
+}
+
+int register_filesystem(struct file_system_type * fs)
+{
+        struct file_system_type ** tmp;
+
+        if (!fs)
+                return -EINVAL;
+        if (fs->next)
+                return -EBUSY;
+        tmp = &file_systems;
+        while (*tmp) {
+                if (strcmp((*tmp)->name, fs->name) == 0)
+                        return -EBUSY;
+                tmp = &(*tmp)->next;
+        }
+        *tmp = fs;
+        return 0;
 }
 
 int unregister_filesystem(struct file_system_type * fs)
@@ -364,11 +460,20 @@ static int do_umount(kdev_t dev)
        int retval;
        
        if (dev==ROOT_DEV) {
-               /* Special case for "unmounting" root.  We just try to remount
-                  it readonly, and sync() the device. */
+               /*
+                * Special case for "unmounting" root. We just try to remount
+                * it readonly, and sync() the device.
+                */
                if (!(sb=get_super(dev)))
                        return -ENOENT;
                if (!(sb->s_flags & MS_RDONLY)) {
+                       /*
+                        * Make sure all quotas are turned off on this device we need to mount
+                        * it readonly so no more writes by the quotasystem.
+                        * If later on the remount fails to bad there are no quotas running
+                        * anymore. Turn them on again by hand.
+                        */
+                       quota_off(dev, -1);
                        fsync_dev(dev);
                        retval = do_remount_sb(sb, MS_RDONLY, 0);
                        if (retval)
@@ -381,6 +486,12 @@ static int do_umount(kdev_t dev)
        if (!sb->s_covered->i_mount)
                printk("VFS: umount(%s): mounted inode has i_mount=NULL\n",
                       kdevname(dev));
+       /*
+        * Before checking if the filesystem is still busy make sure the kernel
+        * doesn't hold any quotafiles open on that device. If the umount fails
+        * to bad there are no quotas running anymore. Turn them on again by hand.
+        */
+       quota_off(dev, -1);
        if (!fs_may_umount(dev, sb->s_mounted))
                return -EBUSY;
        sb->s_covered->i_mount = NULL;
@@ -391,6 +502,7 @@ static int do_umount(kdev_t dev)
        if (sb->s_op && sb->s_op->write_super && sb->s_dirt)
                sb->s_op->write_super(sb);
        put_super(dev);
+       remove_vfsmnt(dev);
        return 0;
 }
 
@@ -415,9 +527,9 @@ asmlinkage int sys_umount(char * name)
 
        if (!suser())
                return -EPERM;
-       retval = namei(name,&inode);
+       retval = namei(name, &inode);
        if (retval) {
-               retval = lnamei(name,&inode);
+               retval = lnamei(name, &inode);
                if (retval)
                        return retval;
        }
@@ -466,13 +578,15 @@ asmlinkage int sys_umount(char * name)
  * We also have to flush all inode-data for this device, as the new mount
  * might need new info.
  */
-int do_mount(kdev_t dev, const char * dir, const char * type, int flags, void * data)
+
+int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data)
 {
        struct inode * dir_i;
        struct super_block * sb;
+       struct vfsmount *vfsmnt;
        int error;
 
-       error = namei(dir,&dir_i);
+       error = namei(dir_name, &dir_i);
        if (error)
                return error;
        if (dir_i->i_count != 1 || dir_i->i_mount) {
@@ -496,6 +610,8 @@ int do_mount(kdev_t dev, const char * dir, const char * type, int flags, void *
                iput(dir_i);
                return -EBUSY;
        }
+       vfsmnt = add_vfsmnt(dev, dev_name, dir_name);
+       vfsmnt->mnt_sb = sb;
        sb->s_covered = dir_i;
        dir_i->i_mount = sb->s_mounted;
        return 0;               /* we don't iput(dir_i) - see umount */
@@ -534,7 +650,7 @@ static int do_remount(const char *dir,int flags,char *data)
        struct inode *dir_i;
        int retval;
 
-       retval = namei(dir,&dir_i);
+       retval = namei(dir, &dir_i);
        if (retval)
                return retval;
        if (dir_i != dir_i->i_sb->s_mounted) {
@@ -619,7 +735,7 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
        t = fstype->name;
        fops = NULL;
        if (fstype->requires_dev) {
-               retval = namei(dev_name,&inode);
+               retval = namei(dev_name, &inode);
                if (retval)
                        return retval;
                if (!S_ISBLK(inode->i_mode)) {
@@ -666,7 +782,7 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
                        return retval;
                }
        }
-       retval = do_mount(dev,dir_name,t,flags,(void *) page);
+       retval = do_mount(dev,dev_name,dir_name,t,flags,(void *) page);
        free_page(page);
        if (retval && fops && fops->release)
                fops->release(inode, NULL);
@@ -678,10 +794,11 @@ void mount_root(void)
 {
        struct file_system_type * fs_type;
        struct super_block * sb;
+       struct vfsmount *vfsmnt;
        struct inode * inode, d_inode;
        struct file filp;
        int retval;
-
+  
        memset(super_blocks, 0, sizeof(super_blocks));
 #ifdef CONFIG_ROOT_NFS
        if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {
@@ -737,9 +854,9 @@ void mount_root(void)
        for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) {
                if(retval)
                        break;
-               if (!fs_type->requires_dev)
-                       continue;
-               sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1);
+               if (!fs_type->requires_dev)
+                       continue;
+               sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1);
                if (sb) {
                        inode = sb->s_mounted;
                        inode->i_count += 3 ;   /* NOTE! it is logically used 4 times, not 1 */
@@ -750,6 +867,8 @@ void mount_root(void)
                        printk ("VFS: Mounted root (%s filesystem)%s.\n",
                                fs_type->name,
                                (sb->s_flags & MS_RDONLY) ? " readonly" : "");
+                       vfsmnt = add_vfsmnt(ROOT_DEV, "rootfs", "/");
+                       vfsmnt->mnt_sb = sb;
                        return;
                }
        }
index d6c9ca56d6004da03053294f12ad0a224db581d8..99924142be8cd6820ce87edbc426589712cf454b 100644 (file)
@@ -30,4 +30,8 @@
 #define MCL_CURRENT     8192           /* lock all currently mapped pages */
 #define MCL_FUTURE     16384           /* lock all additions to address space */
 
+/* compatibility flags */
+#define MAP_ANON       MAP_ANONYMOUS
+#define MAP_FILE       0
+
 #endif /* __ALPHA_MMAN_H__ */
index a9b8dd87277bcf35e8dbde766c2d71cedac5b389..d1205f45ea69089bbdec697b13a5be647868de70 100644 (file)
@@ -48,24 +48,6 @@ typedef unsigned long pgprot_t;
 
 #endif
 
-#define invalidate_all() \
-__asm__ __volatile__( \
-       "lda $16,-2($31)\n\t" \
-       ".long 51" \
-       : : :"$1", "$16", "$17", "$22","$23","$24","$25")
-
-#define invalidate() \
-__asm__ __volatile__( \
-       "lda $16,-1($31)\n\t" \
-       ".long 51" \
-       : : :"$1", "$16", "$17", "$22","$23","$24","$25")
-
-/* Certain architectures need to do special things when pte's
- * within a page table are directly modified.  Thus, the following
- * hook is made available.
- */
-#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval))
-
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)               (((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
index 7c0f9d0825c42eaabebb4d2e0a92bc05ff88edab..9a4e87617379f90b2bfcbc9f7c11c1482c80dfb4 100644 (file)
@@ -9,6 +9,24 @@
  * in <asm/page.h> (currently 8192).
  */
 
+#define invalidate_all() \
+__asm__ __volatile__( \
+       "lda $16,-2($31)\n\t" \
+       ".long 51" \
+       : : :"$1", "$16", "$17", "$22","$23","$24","$25")
+
+#define invalidate() \
+__asm__ __volatile__( \
+       "lda $16,-1($31)\n\t" \
+       ".long 51" \
+       : : :"$1", "$16", "$17", "$22","$23","$24","$25")
+
+/* Certain architectures need to do special things when pte's
+ * within a page table are directly modified.  Thus, the following
+ * hook is made available.
+ */
+#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval))
+
 /* PMD_SHIFT determines the size of the area a second-level page table can map */
 #define PMD_SHIFT      (PAGE_SHIFT + (PAGE_SHIFT-3))
 #define PMD_SIZE       (1UL << PMD_SHIFT)
index 554087a9be81cc3cd60f566e657b69a8ee4ff1ef..20ca49a4fee588545f95fc458e17d825082d69d9 100644 (file)
@@ -40,5 +40,6 @@
 /* linux-specific, might as well be the same as on i386 */
 #define SO_NO_CHECK    11
 #define SO_PRIORITY    12
+#define SO_BSDCOMPAT   14
 
 #endif /* _ASM_SOCKET_H */
index e2ea5b3bee5fae2c56a20f7504c7f627fc37d8bc..01c59fd4f35e2749a99021610052221dd9325383 100644 (file)
 #define __NR_getrlimit         144
 #define __NR_setrlimit         145
 #define __NR_setsid            147
+#define __NR_quotactl          148
 #define __NR_getsockname       150
 #define __NR_sigaction         156
 #define __NR_msgctl            200
index 9b3d6cf2696edda622c82c70408bb1f805d19852..5387f31d8bc7569c2bfc63c296dfa5ca261eb69e 100644 (file)
@@ -64,7 +64,7 @@ extern __inline__ int change_bit(int nr, void * addr)
  */
 extern __inline__ int test_bit(int nr, const void * addr)
 {
-       return 1UL & (((unsigned int *) addr)[nr >> 5] >> (nr & 31));
+       return 1UL & (((const unsigned int *) addr)[nr >> 5] >> (nr & 31));
 }
 
 /*
index 4e125ecfd8b5e44b77a0db6015052f65ef3dcfeb..6b9b957c49ca3f1cdcead101a8a46ae54694b8f4 100644 (file)
@@ -24,4 +24,8 @@
 #define MCL_CURRENT    1               /* lock all current mappings */
 #define MCL_FUTURE     2               /* lock all future mappings */
 
+/* compatibility flags */
+#define MAP_ANON       MAP_ANONYMOUS
+#define MAP_FILE       0
+
 #endif /* __I386_MMAN_H__ */
index defbb59226021e9c34f2520fd8d315455f1e5746..f315634618b9793d27fa814372f39aebb845b1ae 100644 (file)
@@ -50,41 +50,6 @@ typedef unsigned long pgprot_t;
 
 #endif
 
-/*
- * TLB invalidation:
- *
- *  - invalidate() invalidates the current task TLBs
- *  - invalidate_all() invalidates all processes TLBs
- *  - invalidate_task(task) invalidates the specified tasks TLB's
- *  - invalidate_page(task, vmaddr) invalidates one page
- *
- * ..but the i386 has somewhat limited invalidation capabilities.
- */
-#ifndef __SMP__
-#define invalidate() \
-__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax")
-
-#define invalidate_all() invalidate()
-#define invalidate_task(task) \
-do { if ((task)->mm == current->mm) invalidate(); } while (0)
-#define invalidate_page(task,addr) \
-do { if ((task)->mm == current->mm) invalidate(); } while (0)
-
-#else
-#include <asm/smp.h>
-#define local_invalidate() \
-__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax")
-#define invalidate() \
-       smp_invalidate();
-#endif
-
-/* Certain architectures need to do special things when pte's
- * within a page table are directly modified.  Thus, the following
- * hook is made available.
- */
-#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval))
-
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)       (((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
index 0b0511f667907c65f6e3e52024e6271f27161a53..f1bd7899e8243c014f635f2f45d7c3238531385d 100644 (file)
  * the i386 page table tree.
  */
 
+/*
+ * TLB invalidation:
+ *
+ *  - invalidate() invalidates the current mm struct TLBs
+ *  - invalidate_all() invalidates all processes TLBs
+ *  - invalidate_mm(mm) invalidates the specified mm context TLB's
+ *  - invalidate_page(mm, vmaddr) invalidates one page
+ *  - invalidate_range(mm, start, end) invalidates a range of pages
+ *
+ * ..but the i386 has somewhat limited invalidation capabilities.
+ */
+#ifndef __SMP__
+#define invalidate() \
+__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax")
+#else
+#include <asm/smp.h>
+#define local_invalidate() \
+__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax")
+#define invalidate() \
+       smp_invalidate();
+#endif
+
+/*
+ * We aren't very clever about this yet. On a 486+ we could actually do
+ * page-granularity invalidates for better performance in some cases.
+ * And SMP could certainly avoid some global invalidates..
+ */
+#define invalidate_all() invalidate()
+#define invalidate_mm(mm_struct) \
+do { if ((mm_struct) == current->mm) invalidate(); } while (0)
+#define invalidate_page(mm_struct,addr) \
+do { if ((mm_struct) == current->mm) invalidate(); } while (0)
+#define invalidate_range(mm_struct,start,end) \
+do { if ((mm_struct) == current->mm) invalidate(); } while (0)
+
+/* Certain architectures need to do special things when pte's
+ * within a page table are directly modified.  Thus, the following
+ * hook is made available.
+ */
+#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval))
+
 /* PMD_SHIFT determines the size of the area a second-level page table can map */
 #define PMD_SHIFT      22
 #define PMD_SIZE       (1UL << PMD_SHIFT)
diff --git a/include/linux/apm_bios.h b/include/linux/apm_bios.h
new file mode 100644 (file)
index 0000000..8cf36cc
--- /dev/null
@@ -0,0 +1,140 @@
+#ifndef _LINUX_APM_H
+#define _LINUX_APM_H
+
+/*
+ * Include file for the interface to an APM BIOS
+ * Copyright 1994, 1995 Stephen Rothwell (Stephen.Rothwell@pd.necisa.oz.au)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * $Id: apm_bios.h,v 0.9 1995/03/09 13:50:05 sfr Exp $
+ */
+
+typedef unsigned short apm_event_t;
+
+#ifdef __KERNEL__
+
+#include <linux/tasks.h>       /* for NR_TASKS */
+#include <linux/sched.h>       /* for _TSS */
+
+#define APM_CS         _TSS(NR_TASKS)
+#define APM_CS_16      (APM_CS + 8)
+#define APM_DS         (APM_CS_16 + 8)
+
+struct apm_bios_info {
+       unsigned short  version;
+       unsigned short  cseg;
+       unsigned long   offset;
+       unsigned short  cseg_16;
+       unsigned short  dseg;
+       unsigned short  flags;
+       unsigned short  cseg_len;
+       unsigned short  dseg_len;
+};
+
+                               /* Results of APM Installation Check */
+#define APM_16_BIT_SUPPORT     0x0001
+#define APM_32_BIT_SUPPORT     0x0002
+#define APM_IDLE_SLOWS_CLOCK   0x0004
+#define APM_BIOS_DISABLED              0x0008
+#define APM_BIOS_DISENGAGED     0x0010
+
+/*
+ * Maximum number of events stored
+ */
+#define APM_MAX_EVENTS         20
+
+/*
+ * The per-file APM data
+ */
+struct apm_bios_struct {
+       int             magic;
+       struct apm_bios_struct *        next;
+       int             suser;
+       int             suspends_pending;
+       int             standbys_pending;
+       int             event_head;
+       int             event_tail;
+       apm_event_t     events[APM_MAX_EVENTS];
+};
+
+/*
+ * The magic number in apm_bios_struct
+ */
+#define APM_BIOS_MAGIC         0x4101
+
+/*
+ * in init/main.c
+ */
+extern struct apm_bios_info    apm_bios_info;
+
+extern void            apm_bios_init(void);
+
+extern int             apm_register_callback(int (*callback)(apm_event_t));
+extern void            apm_unregister_callback(int (*callback)(apm_event_t));
+
+extern int             apm_proc(char *);
+
+extern int             apm_display_blank(void);
+extern int             apm_display_unblank(void);
+
+#endif /* __KERNEL__ */
+
+/*
+ * Power states
+ */
+#define APM_STATE_READY                0x0000
+#define APM_STATE_STANDBY      0x0001
+#define APM_STATE_SUSPEND      0x0002
+#define APM_STATE_OFF          0x0003
+#define APM_STATE_BUSY         0x0004
+#define APM_STATE_REJECT       0x0005
+
+/*
+ * Events (results of Get PM Event)
+ */
+#define APM_SYS_STANDBY                0x0001
+#define APM_SYS_SUSPEND                0x0002
+#define APM_NORMAL_RESUME      0x0003
+#define APM_CRITICAL_RESUME    0x0004
+#define APM_LOW_BATTERY                0x0005
+#define APM_POWER_STATUS_CHANGE        0x0006
+#define APM_UPDATE_TIME                0x0007
+#define APM_CRITICAL_SUSPEND   0x0008
+#define APM_USER_STANDBY       0x0009
+#define APM_USER_SUSPEND       0x000a
+#define APM_STANDBY_RESUME     0x000b
+
+/*
+ * Error codes
+ */
+#define APM_SUCCESS            0x00
+#define APM_DISABLED           0x01
+#define APM_CONNECTED          0x02
+#define APM_NOT_CONNECTED      0x03
+#define APM_16_CONNECTED       0x05
+#define APM_16_UNSUPPORTED     0x06
+#define APM_32_CONNECTED       0x07
+#define APM_32_UNSUPPORTED     0x08
+#define APM_BAD_DEVICE         0x09
+#define APM_BAD_PARAM          0x0a
+#define APM_NOT_ENGAGED                0x0b
+#define APM_BAD_STATE          0x60
+#define APM_NO_EVENTS          0x80
+#define APM_NOT_PRESENT                0x86
+
+/* ioctl operations */
+#include <linux/ioctl.h>
+
+#define APM_IOC_STANDBY                _IO('A', 1)
+#define APM_IOC_SUSPEND                _IO('A', 2)
+
+#endif /* LINUX_APM_H */
index da1ce69f03ef306352d6817f455ab7bc3a61d61a..144bbb22c8ba56970df6f2c86f41c304b5301c82 100644 (file)
@@ -32,6 +32,12 @@ struct ax25_routes_struct
        ax25_address digi_addr[AX25_MAX_DIGIS];
 };
 
+struct ax25_bpqaddr_struct
+{
+       char dev[16];
+       ax25_address addr;
+};
+
 #define AX25_WINDOW    1
 #define AX25_T1                2
 #define AX25_N2                3
@@ -45,6 +51,7 @@ struct ax25_routes_struct
 #define SIOCAX25ADDUID         (SIOCPROTOPRIVATE+1)
 #define SIOCAX25DELUID         (SIOCPROTOPRIVATE+2)
 #define SIOCAX25NOUID          (SIOCPROTOPRIVATE+3)
+#define        SIOCAX25BPQADDR         (SIOCPROTOPRIVATE+4)
 #define        SIOCAX25GETPARMS        (SIOCPROTOPRIVATE+5)
 #define        SIOCAX25SETPARMS        (SIOCPROTOPRIVATE+6)
 
index bc93d267c767477aee60f454f0b10dd553843601..c61b84ae3ad2953ae552455b1a354d606d1a353c 100644 (file)
@@ -336,7 +336,7 @@ static void (DEVICE_REQUEST)(void);
 
 #if ! SCSI_MAJOR(MAJOR_NR)
 
-#if defined(_IDE_CD_C) || defined(_TRITON_C) /* shares copy with ide.c */
+#if defined(_IDE_CD_C) || defined(_TRITON_C) || defined(_IDE_TAPE_C) /* shares copy with ide.c */
 void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
 #else
 
@@ -375,6 +375,7 @@ static void end_request(int uptodate) {
                }
        }
 #ifdef IDE_DRIVER
+       blk_dev[MAJOR(req->rq_dev)].current_request = req->next;
        hwgroup->rq = NULL;
 #else
        DEVICE_OFF(req->rq_dev);
index a443aa2771fffbdc254a6a663d2b5fc622616090..fcb5e5f62762d61b16625450e4fdf128175fd362 100644 (file)
@@ -404,9 +404,9 @@ struct ext2_dir_entry {
 extern int ext2_permission (struct inode *, int);
 
 /* balloc.c */
-extern int ext2_new_block (struct super_block *, unsigned long,
-                          __u32 *, __u32 *);
-extern void ext2_free_blocks (struct super_block *, unsigned long,
+extern int ext2_new_block (const struct inode *, unsigned long,
+                          __u32 *, __u32 *, int *);
+extern void ext2_free_blocks (const struct inode *, unsigned long,
                              unsigned long);
 extern unsigned long ext2_count_free_blocks (struct super_block *);
 extern void ext2_check_blocks_bitmap (struct super_block *);
@@ -427,7 +427,7 @@ extern int ext2_write (struct inode *, struct file *, char *, int);
 extern int ext2_sync_file (struct inode *, struct file *);
 
 /* ialloc.c */
-extern struct inode * ext2_new_inode (const struct inode *, int);
+extern struct inode * ext2_new_inode (const struct inode *, int, int *);
 extern void ext2_free_inode (struct inode *);
 extern unsigned long ext2_count_free_inodes (struct super_block *);
 extern void ext2_check_inodes_bitmap (struct super_block *);
index 52ab5413f1a6e18188d8370a663424eff14a95d0..07f689286eb0c6a746ad1e1f4a4e413eb0ca88d3 100644 (file)
@@ -63,7 +63,7 @@ struct ext_dir_entry {
 extern int ext_open(struct inode * inode, struct file * filp);
 extern void ext_release(struct inode * inode, struct file * filp);
 extern int ext_lookup(struct inode * dir,const char * name, int len,
-       struct inode ** result);
+                      struct inode ** result);
 extern int ext_create(struct inode * dir,const char * name, int len, int mode,
        struct inode ** result);
 extern int ext_mkdir(struct inode * dir, const char * name, int len, int mode);
index b9761e566e907084b6375a83717d3d47b3b53a3a..87e285c0d119e636ab48e2dcfd6d27781d0f24c0 100644 (file)
@@ -39,6 +39,9 @@
 #define MAY_WRITE 2
 #define MAY_READ 4
 
+#define FMODE_READ 1
+#define FMODE_WRITE 2
+
 #define READ 0
 #define WRITE 1
 #define READA 2                /* read-ahead - don't pause */
 /*
  * These are the fs-independent mount-flags: up to 16 flags are supported
  */
-#define MS_RDONLY       1 /* mount read-only */
-#define MS_NOSUID       2 /* ignore suid and sgid bits */
-#define MS_NODEV        4 /* disallow access to device special files */
-#define MS_NOEXEC       8 /* disallow program execution */
-#define MS_SYNCHRONOUS 16 /* writes are synced at once */
-#define MS_REMOUNT     32 /* alter flags of a mounted FS */
-
-#define S_APPEND    256 /* append-only file */
-#define S_IMMUTABLE 512 /* immutable file */
+#define MS_RDONLY       1      /* Mount read-only */
+#define MS_NOSUID       2      /* Ignore suid and sgid bits */
+#define MS_NODEV        4      /* Disallow access to device special files */
+#define MS_NOEXEC       8      /* Disallow program execution */
+#define MS_SYNCHRONOUS 16      /* Writes are synced at once */
+#define MS_REMOUNT     32      /* Alter flags of a mounted FS */
+#define S_WRITE                128     /* Write on file/directory/symlink */
+#define S_APPEND       256     /* Append-only file */
+#define S_IMMUTABLE    512     /* Immutable file */
 
 /*
  * Flags that can be altered by MS_REMOUNT
@@ -74,8 +77,8 @@
 /*
  * Magic mount flag number. Has to be or-ed to the flag values.
  */
-#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */
-#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */
+#define MS_MGC_VAL 0xC0ED0000  /* magic flag number to indicate "new" flags */
+#define MS_MGC_MSK 0xffff0000  /* magic flag number mask */
 
 /*
  * Note that read-only etc flags are inode-specific: setting some file-system
@@ -91,6 +94,7 @@
 #define IS_NOEXEC(inode) ((inode)->i_flags & MS_NOEXEC)
 #define IS_SYNC(inode) ((inode)->i_flags & MS_SYNCHRONOUS)
 
+#define IS_WRITABLE(inode) ((inode)->i_flags & S_WRITE)
 #define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
 #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
 
@@ -222,6 +226,8 @@ struct iattr {
        time_t          ia_ctime;
 };
 
+#include <linux/quota.h>
+
 struct inode {
        kdev_t          i_dev;
        unsigned long   i_ino;
@@ -238,17 +244,17 @@ struct inode {
        unsigned long   i_blocks;
        unsigned long   i_version;
        struct semaphore i_sem;
-       struct inode_operations * i_op;
-       struct super_block * i_sb;
-       struct wait_queue * i_wait;
-       struct file_lock * i_flock;
-       struct vm_area_struct * i_mmap;
-       struct inode * i_next, * i_prev;
-       struct inode * i_hash_next, * i_hash_prev;
-       struct inode * i_bound_to, * i_bound_by;
-       struct inode * i_mount;
+       struct inode_operations *i_op;
+       struct super_block *i_sb;
+       struct wait_queue *i_wait;
+       struct file_lock *i_flock;
+       struct vm_area_struct *i_mmap;
+       struct dquot *i_dquot[MAXQUOTAS];
+       struct inode *i_next, *i_prev;
+       struct inode *i_hash_next, *i_hash_prev;
+       struct inode *i_bound_to, *i_bound_by;
+       struct inode *i_mount;
        unsigned short i_count;
-       unsigned short i_wcount;
        unsigned short i_flags;
        unsigned char i_lock;
        unsigned char i_dirt;
@@ -256,6 +262,7 @@ struct inode {
        unsigned char i_sock;
        unsigned char i_seek;
        unsigned char i_update;
+       unsigned short i_writecount;
        union {
                struct pipe_inode_info pipe_i;
                struct minix_inode_info minix_i;
@@ -330,6 +337,7 @@ struct super_block {
        unsigned char s_dirt;
        struct file_system_type *s_type;
        struct super_operations *s_op;
+       struct dquot_operations *dq_op;
        unsigned long s_flags;
        unsigned long s_magic;
        unsigned long s_time;
@@ -404,6 +412,16 @@ struct super_operations {
        int (*remount_fs) (struct super_block *, int *, char *);
 };
 
+struct dquot_operations {
+       void (*initialize) (struct inode *, short);
+       void (*drop) (struct inode *);
+       int (*alloc_block) (const struct inode *, unsigned long);
+       int (*alloc_inode) (const struct inode *, unsigned long);
+       void (*free_block) (const struct inode *, unsigned long);
+       void (*free_inode) (const struct inode *, unsigned long);
+       int (*transfer) (struct inode *, struct iattr *, char);
+};
+
 struct file_system_type {
        struct super_block *(*read_super) (struct super_block *, void *, int);
        const char *name;
@@ -510,8 +528,8 @@ extern int notify_change(struct inode *, struct iattr *);
 extern int namei(const char * pathname, struct inode ** res_inode);
 extern int lnamei(const char * pathname, struct inode ** res_inode);
 extern int permission(struct inode * inode,int mask);
-extern int get_write_access(struct inode * inode);
-extern void put_write_access(struct inode * inode);
+extern int get_write_access(struct inode *inode);
+extern void put_write_access(struct inode *inode);
 extern int open_namei(const char * pathname, int flag, int mode,
        struct inode ** res_inode, struct inode * base);
 extern int do_mknod(const char * filename, int mode, dev_t dev);
@@ -575,7 +593,7 @@ extern void inode_setattr(struct inode *, struct iattr *);
 
 extern inline struct inode * iget(struct super_block * sb,int nr)
 {
-       return __iget(sb,nr,1);
+       return __iget(sb, nr, 1);
 }
 
 #endif /* __KERNEL__ */
index 222d93f753ea32091a6279dfded37974700750a7..10799419b0af8db31ae78a906f4475324bd4c2f3 100644 (file)
@@ -97,7 +97,6 @@ struct ifreq
        union
        {
                char    ifrn_name[IFNAMSIZ];            /* if name, e.g. "en0" */
-               char    ifrn_hwaddr[IFHWADDRLEN];       /* Obsolete */
        } ifr_ifrn;
        
        union {
@@ -116,7 +115,6 @@ struct ifreq
 };
 
 #define ifr_name       ifr_ifrn.ifrn_name      /* interface name       */
-#define old_ifr_hwaddr ifr_ifrn.ifrn_hwaddr    /* interface hardware   */
 #define ifr_hwaddr     ifr_ifru.ifru_hwaddr    /* MAC address          */
 #define        ifr_addr        ifr_ifru.ifru_addr      /* address              */
 #define        ifr_dstaddr     ifr_ifru.ifru_dstaddr   /* other end of p-p lnk */
index d765b125cb6481832e2ac250f75ec2e51d764180..161528f12cdd4cb322b9c4948561fb475710813b 100644 (file)
@@ -2,7 +2,7 @@
  *     Linux NET3:     Internet Gateway Management Protocol  [IGMP]
  *
  *     Authors:
- *             Alan Cox <Alan.Cox@linux.org>   
+ *             Alan Cox <Alan.Cox@linux.org>
  *
  *     Extended to talk the BSD extended IGMP protocol of mrouted 3.6
  *
@@ -65,8 +65,8 @@ struct igmphdr
                                                /* specifies time in 10th of seconds     */
 
 #define IGMP_AGE_THRESHOLD             540     /* If this host don't hear any IGMP V1  */
-                                               /* message in this period of time, revert */
-                                               /* to IGMP v2 router */
+                                               /* message in this period of time,      */
+                                               /* revert to IGMP v2 router.            */
 
 #define IGMP_ALL_HOSTS         htonl(0xE0000001L)
 #define IGMP_ALL_ROUTER        htonl(0xE0000002L)
@@ -94,12 +94,13 @@ struct ip_mc_list
        int users;
 };
 
-struct router_info
+struct ip_router_info
 {
        struct device *dev;
-       int    type; /* type of router which is querier on this interface */
-       int    time; /* # of slow timeouts since last old query */
-       struct router_info *next;
+       int    type;    /* type of router which is querier on this interface */
+       int    time;    /* # of slow timeouts since last old query */
+       struct timer_list timer;
+       struct ip_router_info *next;
 };
 
 extern struct ip_mc_list *ip_mc_head;
index 82a49a3c6c57fce55c0c26388a4f31524d373484..a89ddca93f7a085fc59e77e0ca803b28ab7665e1 100644 (file)
@@ -167,7 +167,7 @@ extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inod
 extern int isofs_open(struct inode * inode, struct file * filp);
 extern void isofs_release(struct inode * inode, struct file * filp);
 extern int isofs_lookup(struct inode * dir,const char * name, int len,
-       struct inode ** result);
+                       struct inode ** result);
 extern unsigned long isofs_count_free_inodes(struct super_block *sb);
 extern int isofs_new_block(int dev);
 extern int isofs_free_block(int dev, int block);
index e253ae18b0267240073c38c72281113137e76fe6..2a1282c8add2b0dcb69376a98a6222166daa43eb 100644 (file)
@@ -69,6 +69,7 @@
 #define LPGETSTATUS 0x060b  /* return LP_S(minor) */
 #define LPRESET     0x060c  /* reset printer */
 #define LPGETSTATS  0x060d  /* get statistics (struct lp_stats) */
+#define LPGETFLAGS  0x060e  /* get status flags */
 
 /* timeout for printk'ing a timeout, in jiffies (100ths of a second).
    This is also used for re-checking error conditions if LP_ABORT is
index bc68a82e13c11cca2602973f321e23423fc59e30..fe433fd4685114c1e4588758a4c129083b4ba8d4 100644 (file)
@@ -99,6 +99,7 @@
 #define IDE2_MAJOR     33
 #define IDE3_MAJOR     34
 #define NETLINK_MAJOR  36
+#define IDETAPE_MAJOR  37
 
 /*
  * Tests for SCSI devices.
index 33fd9c1d914448bf19e335167750584a57c24b81..82fad64471a8bac0fefec8e56575340c3133e24d 100644 (file)
@@ -82,7 +82,7 @@ struct minix_dir_entry {
 #ifdef __KERNEL__
 
 extern int minix_lookup(struct inode * dir,const char * name, int len,
-       struct inode ** result);
+                        struct inode ** result);
 extern int minix_create(struct inode * dir,const char * name, int len, int mode,
        struct inode ** result);
 extern int minix_mkdir(struct inode * dir, const char * name, int len, int mode);
diff --git a/include/linux/mount.h b/include/linux/mount.h
new file mode 100644 (file)
index 0000000..602025f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *
+ * Definitions for mount interface. This describes the in the kernel build 
+ * linkedlist with mounted filesystems.
+ *
+ * Author:  Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net> <mvw@cistron.nl>
+ *
+ * Version: $Id: mount.h,v 1.3 1994/07/20 22:01:00 mvw Exp mvw $
+ *
+ */
+#ifndef _LINUX_MOUNT_H
+#define _LINUX_MOUNT_H
+
+struct vfsmount
+{
+   dev_t mnt_dev;                      /* Device this applies to */
+   char *mnt_devname;                  /* Name of device e.g. /dev/dsk/hda1 */
+   char *mnt_dirname;                  /* Name of directory mounted on */
+   unsigned int mnt_flags;             /* Flags of this device */
+   struct semaphore mnt_sem;           /* lock device while I/O in progress */
+   struct super_block *mnt_sb;         /* pointer to superblock */
+   struct file *mnt_quotas[MAXQUOTAS]; /* fp's to quotafiles */
+   time_t mnt_iexp[MAXQUOTAS];         /* expiretime for inodes */
+   time_t mnt_bexp[MAXQUOTAS];         /* expiretime for blocks */
+   struct vfsmount *mnt_next;          /* pointer to next in linkedlist */
+};
+
+struct vfsmount *lookup_vfsmnt(dev_t dev);
+
+#endif /* _LINUX_MOUNT_H */
index 92bdeb913dc167669009fbaf02e1dbcd0ab7e9b5..62677673d71136400e8a964f4c05e580d406a61f 100644 (file)
@@ -148,7 +148,7 @@ int get_cluster(struct inode *inode,int cluster);
 /* namei.c */
 
 extern int msdos_lookup(struct inode *dir,const char *name,int len,
-       struct inode **result);
+                        struct inode **result);
 extern int msdos_create(struct inode *dir,const char *name,int len,int mode,
        struct inode **result);
 extern int msdos_mkdir(struct inode *dir,const char *name,int len,int mode);
index 3d7bb3e1510df3307d35539f3fd5da751fab6d24..39809b4d82bda3029db65a41c90fe03909dbc7f7 100644 (file)
 #define PCI_DEVICE_ID_CIRRUS_5434_8    0x00A8
 #define PCI_DEVICE_ID_CIRRUS_6729      0x1100
 #define PCI_DEVICE_ID_CIRRUS_7542      0x1200
+#define PCI_DEVICE_ID_CIRRUS_7543      0x1202
 
 #define PCI_VENDOR_ID_IBM              0x1014
 
 #define PCI_VENDOR_ID_ZEITNET          0x1193
 #define PCI_DEVICE_ID_ZEITNET_1221     0x0001
 
-#define PCI_VENDOR_ID_HAL              0x11cd
-#define PCI_DEVICE_ID_HAL_RIO          0x8000
+#define PCI_VENDOR_ID_SPECIALIX                0x11cb
+#define PCI_DEVICE_ID_SPECIALIX_XIO    0x4000
+#define PCI_DEVICE_ID_SPECIALIX_RIO    0x8000
 
 #define PCI_VENDOR_ID_CYCLADES         0x120e
 #define PCI_DEVICE_ID_CYCLADES_Y       0x0100
index 9716ac786c1481e0c25a7452847ac47fbecee96a..0888e39da5d747025db085eae686d54631fa65bd 100644 (file)
@@ -34,6 +34,7 @@ enum root_directory_inos {
        PROC_KSYMS,
        PROC_DMA,       
        PROC_IOPORTS,
+       PROC_APM,
        PROC_PROFILE /* whether enabled or not */
 };
 
@@ -87,6 +88,7 @@ enum net_directory_inos {
        PROC_NET_NR,
        PROC_NET_SOCKSTAT,
        PROC_NET_RTCACHE,
+       PROC_NET_AX25_BPQETHER,
        PROC_NET_LAST
 };
 
@@ -111,6 +113,7 @@ enum scsi_directory_inos {
        PROC_SCSI_ULTRASTOR,
        PROC_SCSI_7000FASST,
        PROC_SCSI_EATA2X,
+       PROC_SCSI_SSC,
        PROC_SCSI_SCSI_DEBUG,   
        PROC_SCSI_NOT_PRESENT,
        PROC_SCSI_FILE,                        /* I'm asuming here that we */
diff --git a/include/linux/quota.h b/include/linux/quota.h
new file mode 100644 (file)
index 0000000..7c39797
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *   This product includes software developed by the University of
+ *   California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Version: $Id: quota.h,v 1.8 1995/03/11 11:43:07 mvw Exp mvw $
+ */
+
+#ifndef _LINUX_QUOTA_
+#define _LINUX_QUOTA_
+
+#include <linux/errno.h>
+
+/*
+ * Convert diskblocks to blocks and the other way around.
+ * currently only to fool the BSD source. :-)
+ */
+#define dbtob(num) (num << 10)
+#define btodb(num) (num >> 10)
+
+/*
+ * Convert count of filesystem blocks to diskquota blocks, meant
+ * for filesystems where i_blksize != BLOCK_SIZE
+ */
+#define fs_to_dq_blocks(num, blksize) (((num) * (blksize)) / BLOCK_SIZE)
+
+/*
+ * Definitions for disk quotas imposed on the average user
+ * (big brother finally hits Linux).
+ *
+ * The following constants define the amount of time given a user
+ * before the soft limits are treated as hard limits (usually resulting
+ * in an allocation failure). The timer is started when the user crosses
+ * their soft limit, it is reset when they go below their soft limit.
+ */
+#define MAX_IQ_TIME  604800    /* (7*24*60*60) 1 week */
+#define MAX_DQ_TIME  604800    /* (7*24*60*60) 1 week */
+
+#define MAXQUOTAS 2
+#define USRQUOTA  0            /* element used for user quotas */
+#define GRPQUOTA  1            /* element used for group quotas */
+
+/*
+ * Definitions for the default names of the quotas files.
+ */
+#define INITQFNAMES { \
+   "user",      /* USRQUOTA */ \
+   "group",   /* GRPQUOTA */ \
+   "undefined", \
+};
+
+#define QUOTAFILENAME "quota"
+#define QUOTAGROUP "staff"
+
+#define NR_DQHASH 43            /* Just an arbitrary number any suggestions ? */
+#define NR_DQUOTS 256           /* Number of quotas active at one time */
+
+/*
+ * Command definitions for the 'quotactl' system call.
+ * The commands are broken into a main command defined below
+ * and a subcommand that is used to convey the type of
+ * quota that is being manipulated (see above).
+ */
+#define SUBCMDMASK  0x00ff
+#define SUBCMDSHIFT 8
+#define QCMD(cmd, type)  (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))
+
+#define Q_QUOTAON  0x0100      /* enable quotas */
+#define Q_QUOTAOFF 0x0200      /* disable quotas */
+#define Q_GETQUOTA 0x0300      /* get limits and usage */
+#define Q_SETQUOTA 0x0400      /* set limits and usage */
+#define Q_SETUSE   0x0500      /* set usage */
+#define Q_SYNC     0x0600      /* sync disk copy of a filesystems quotas */
+#define Q_SETQLIM  0x0700      /* set limits */
+#define Q_GETSTATS 0x0800      /* get collected stats */
+
+/*
+ * The following structure defines the format of the disk quota file
+ * (as it appears on disk) - the file is an array of these structures
+ * indexed by user or group number.
+ */
+struct dqblk {
+    __u32 dqb_bhardlimit;      /* absolute limit on disk blks alloc */
+    __u32 dqb_bsoftlimit;      /* preferred limit on disk blks */
+    __u32 dqb_curblocks;       /* current block count */
+    __u32 dqb_ihardlimit;      /* maximum # allocated inodes */
+    __u32 dqb_isoftlimit;      /* preferred inode limit */
+    __u32 dqb_curinodes;       /* current # allocated inodes */
+    time_t dqb_btime;          /* time limit for excessive disk use */
+    time_t dqb_itime;          /* time limit for excessive files */
+};
+
+/*
+ * Shorthand notation.
+ */
+#define        dq_bhardlimit   dq_dqb.dqb_bhardlimit
+#define        dq_bsoftlimit   dq_dqb.dqb_bsoftlimit
+#define        dq_curblocks    dq_dqb.dqb_curblocks
+#define        dq_ihardlimit   dq_dqb.dqb_ihardlimit
+#define        dq_isoftlimit   dq_dqb.dqb_isoftlimit
+#define        dq_curinodes    dq_dqb.dqb_curinodes
+#define        dq_btime        dq_dqb.dqb_btime
+#define        dq_itime        dq_dqb.dqb_itime
+
+#define dqoff(UID)      ((off_t)((UID) * sizeof (struct dqblk)))
+
+struct dqstats {
+   __u32 lookups;
+   __u32 drops;
+   __u32 reads;
+   __u32 writes;
+   __u32 cache_hits;
+   __u32 pages_allocated;
+   __u32 allocated_dquots;
+   __u32 free_dquots;
+   __u32 syncs;
+};
+
+#ifdef __KERNEL__
+
+/*
+ * Maximum lenght of a message generated in the quota system,
+ * that needs to be kicked onto the tty.
+ */
+#define MAX_QUOTA_MESSAGE 75
+
+#define DQ_LOCKED     0x01     /* locked for update */
+#define DQ_WANT       0x02     /* wanted for update */
+#define DQ_MOD        0x04     /* dquot modified since read */
+#define DQ_BLKS       0x10     /* uid/gid has been warned about blk limit */
+#define DQ_INODES     0x20     /* uid/gid has been warned about inode limit */
+#define DQ_FAKE       0x40     /* no limits only usage */
+
+struct dquot {
+   unsigned int dq_id;         /* id this applies to (uid, gid) */
+   short dq_type;              /* type of quota */
+   kdev_t dq_dev;                /* Device this applies to */
+   short dq_flags;             /* see DQ_* */
+   short dq_count;             /* reference count */
+   struct vfsmount *dq_mnt;     /* vfsmountpoint this applies to */
+   struct dqblk dq_dqb;         /* diskquota usage */
+   struct wait_queue *dq_wait; /* pointer to waitqueue */
+   struct dquot *dq_prev;      /* pointer to prev dquot */
+   struct dquot *dq_next;      /* pointer to next dquot */
+   struct dquot *dq_hash_prev; /* pointer to prev dquot */
+   struct dquot *dq_hash_next; /* pointer to next dquot */
+};
+
+#define NODQUOT (struct dquot *)NULL
+
+/*
+ * Flags used for set_dqblk.
+ */
+#define QUOTA_SYSCALL     0x01
+#define SET_QUOTA         0x02
+#define SET_USE           0x04
+#define SET_QLIMIT        0x08
+
+#define QUOTA_OK          0
+#define NO_QUOTA          1
+
+/*
+ * declaration of quota_function calls in kernel.
+ */
+int quota_off (kdev_t dev, short type);
+int sync_dquots (kdev_t dev, short type);
+
+extern void dquot_initialize(struct inode *inode, short type);
+extern void dquot_drop(struct inode *inode);
+extern int dquot_alloc_block(const struct inode *inode, unsigned long number);
+extern int dquot_alloc_inode(const struct inode *inode, unsigned long number);
+extern void dquot_free_block(const struct inode *inode, unsigned long number);
+extern void dquot_free_inode(const struct inode *inode, unsigned long number);
+extern int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction);
+
+extern void invalidate_dquots(kdev_t dev, short type);
+extern int quota_off(kdev_t dev, short type);
+extern int sync_dquots(kdev_t dev, short type);
+
+#else
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int quotactl __P ((int, const char *, int, caddr_t));
+__END_DECLS
+
+#endif /* __KERNEL__ */
+#endif /* _QUOTA_ */
index ad9d593bee6d4584f279e8cd33bc3e21415977b6..5a930857809068e3d2fd0811fdb939e8ccf2a1a4 100644 (file)
@@ -133,6 +133,7 @@ struct fs_struct {
 struct mm_struct {
        int count;
        pgd_t * pgd;
+       unsigned long context;
        unsigned long start_code, end_code, start_data, end_data;
        unsigned long start_brk, brk, start_stack, start_mmap;
        unsigned long arg_start, arg_end, env_start, env_end;
@@ -145,6 +146,7 @@ struct mm_struct {
 #define INIT_MM { \
                1, \
                swapper_pg_dir, \
+               0, \
                0, 0, 0, 0, \
                0, 0, 0, 0, \
                0, 0, 0, 0, \
index 60b9868a941405b3f949f66e6b04df04f2a1d461..f85c78b678006bcb977a1c851eaa37d6eb883fcd 100644 (file)
@@ -50,6 +50,7 @@ typedef unsigned long dword;
 
 #define LANMAN1
 #define LANMAN2
+#define NT1
 
 enum smb_protocol { 
        PROTOCOL_NONE, 
index bb33bdbecc996a4bc26596ea2ad2dfcac3d0d954..dcd3256684cf9b6f372fbd43e9c1d8e2cc1131cc 100644 (file)
  * These are system calls that haven't been implemented yet
  * but have an entry in the table for future expansion..
  */
-#ifdef __ELF__
-#define sys_quotactl   sys_ni_syscall
-#else
-#define _sys_quotactl  _sys_ni_syscall
-#endif
-
 #endif
index f96f32676648d7540dc39b40eb3c8b5d94548049..daa2bba2241cbb6fef102a6e740789242ea29e2d 100644 (file)
@@ -362,7 +362,7 @@ sv_bread (struct super_block *sb, kdev_t dev, unsigned int block)
  */
 
 extern int sysv_lookup(struct inode * dir,const char * name, int len,
-       struct inode ** result);
+                      struct inode ** result);
 extern int sysv_create(struct inode * dir,const char * name, int len, int mode,
        struct inode ** result);
 extern int sysv_mkdir(struct inode * dir, const char * name, int len, int mode);
index e9290ba88245f349cde1ca5078a9a51ae42146e9..ff1e9c15f5ff178b292ca7edfd5124ae639202b1 100644 (file)
@@ -297,6 +297,7 @@ extern int tty_register_driver(struct tty_driver *driver);
 extern int tty_unregister_driver(struct tty_driver *driver);
 extern int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp,
                             int buflen);
+extern void tty_write_message(struct tty_struct *tty, char *msg);
 
 extern int is_orphaned_pgrp(int pgrp);
 extern int is_ignored(int sig);
index 3f8876bd7dc202385bf511971ef0cb3777c4e021..356524706e3e37d997328631a5a71b77647964fc 100644 (file)
@@ -203,6 +203,9 @@ extern unsigned short ax25_dev_get_value(struct device *, int);
 extern void ax25_dev_device_up(struct device *);
 extern void ax25_dev_device_down(struct device *);
 extern int  ax25_dev_ioctl(unsigned int, void *);
+extern int  ax25_bpq_get_info(char *, char **, off_t, int, int);
+extern ax25_address *ax25_bpq_get_addr(struct device *);
+extern int  ax25_bpq_ioctl(unsigned int, void *);
 
 /* ax25_subr.c */
 extern void ax25_clear_queues(ax25_cb *);
index 27db2d1a5f0dd46868079d4f091b988a160e39d4..e32af15b743c00b9ae54bba2c2ecdaec624a96be 100644 (file)
@@ -1,9 +1,9 @@
 #ifndef __NET_NETLINK_H
 #define __NET_NETLINK_H
 
-#define NET_MAJOR 18           /* Major 18 is reserved for networking          */
-#define MAX_LINKS 3            /* 18,0 for route updates, 18,1 for SKIP        */
-#define MAX_QBYTES 32768       /* Maximum bytes in the queue                   */
+#define NET_MAJOR 36           /* Major 18 is reserved for networking                                          */
+#define MAX_LINKS 4            /* 18,0 for route updates, 18,1 for SKIP, 18,2 debug tap 18,3 PPP reserved      */
+#define MAX_QBYTES 32768       /* Maximum bytes in the queue                                                   */
 
 #include <linux/config.h>
 
index 5207b2d00a290f298f750163c9b6c95ea6168536..63dafb83394ea58403561861b866494ae8f40047 100644 (file)
@@ -29,6 +29,9 @@
 #include <linux/hdreg.h>
 #include <linux/mm.h>
 #include <linux/major.h>
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+#endif
 
 #include <asm/bugs.h>
 
@@ -127,6 +130,8 @@ char nfs_root_name[256] = { NFS_ROOT };
 char nfs_root_addrs[128] = { "" };
 #endif
 
+extern void dquot_init(void);
+
 static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
 static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
 
@@ -400,6 +405,7 @@ static void parse_options(char *line)
                if (!strncmp(line, "nfsroot=", 8)) {
                        int n;
                        line += 8;
+                       ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255);
                        if (line[0] == '/' || (line[0] >= '0' && line[0] <= '9')) {
                                strncpy(nfs_root_name, line, sizeof(nfs_root_name));
                                nfs_root_name[sizeof(nfs_root_name)] = '\0';
@@ -580,6 +586,10 @@ asmlinkage void start_kernel(void)
 #ifdef CONFIG_SYSVIPC
        ipc_init();
 #endif
+#ifdef CONFIG_APM
+       apm_bios_init();
+#endif
+       dquot_init();
        sti();
        check_bugs();
 
index 4dfae8351269bbd514c724b8be439e7b59877e44..5e6b6a764c63593da354bb3063e9e45fb4cbe106 100644 (file)
@@ -336,6 +336,7 @@ struct symbol_table symbol_table = {
 #endif 
        X(jiffies),
        X(xtime),
+       X(do_gettimeofday),
        X(loops_per_sec),
        X(need_resched),
        X(kill_proc),
index c6d79b3e3f87e41d1d25c794d9b95d40dcc210ec..d0005f4a898b573defecc63f4dd1665b89b7b4eb 100644 (file)
@@ -547,8 +547,10 @@ int get_module_list(char *buf)
                        *p++ = *q++;
                if (mp->state == MOD_UNINITIALIZED)
                        q = "  (uninitialized)";
-               else if (mp->state == MOD_RUNNING)
-                       q = "";
+               else if (mp->state == MOD_RUNNING) {
+                       sprintf(size,"\t%ld",GET_USE_COUNT(mp));
+                       q=size;
+               }
                else if (mp->state == MOD_DELETED)
                        q = "  (deleted)";
                else
index c5976bdcf6196ec9ea4fec4ad0ed1f3f3fc3b597..b42621792b02630529987eae4983152068090962 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
 
 #define LOG_BUF_LEN    4096
 
@@ -236,3 +238,16 @@ void register_console(void (*proc)(const char *))
                j = 0;
        }
 }
+
+/*
+ * Write a message to a certain tty, not just the console. This is used for
+ * messages that need to be redirected to a specific tty.
+ * We don't put it into the syslog queue right now maybe in the future if
+ * really needed.
+ */
+void tty_write_message(struct tty_struct *tty, char *msg)
+{
+       if (tty && tty->driver.write)
+               tty->driver.write(tty, 0, msg, strlen(msg));
+       return;
+}
index 6a76c7cea4b8a5e32e7422bd3667f2ba4ac78a02..3a7a27dafc486e2a63db343de45829b9e33d29b9 100644 (file)
@@ -7,7 +7,6 @@
 #
 
 L_TARGET := lib.a
-L_OBJS   := ctype.o _exit.o open.o close.o errno.o write.o dup.o \
-            setsid.o execve.o wait.o string.o vsprintf.o
+L_OBJS   := errno.o ctype.o string.o vsprintf.o
 
 include $(TOPDIR)/Rules.make
diff --git a/lib/_exit.c b/lib/_exit.c
deleted file mode 100644 (file)
index 6dd10fc..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- *  linux/lib/_exit.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-
-#define exit __sys_exit
-static inline _syscall0(int, exit)
-#undef exit
-
-volatile void _exit(int exit_code)
-{
-fake_volatile:
-       __sys_exit();
-       goto fake_volatile;
-}
-
-
diff --git a/lib/close.c b/lib/close.c
deleted file mode 100644 (file)
index 2c3f436..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- *  linux/lib/close.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-
-_syscall1(int,close,int,fd)
-
diff --git a/lib/dup.c b/lib/dup.c
deleted file mode 100644 (file)
index 6d5ed11..0000000
--- a/lib/dup.c
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- *  linux/lib/dup.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-
-_syscall1(int,dup,int,fd)
-
diff --git a/lib/execve.c b/lib/execve.c
deleted file mode 100644 (file)
index 32a93f0..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- *  linux/lib/execve.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-
-_syscall3(int,execve,const char *,file,char **,argv,char **,envp)
-
diff --git a/lib/open.c b/lib/open.c
deleted file mode 100644 (file)
index 4ab3443..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- *  linux/lib/open.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-#include <stdarg.h>
-
-#define open __sys_open
-static inline _syscall3(int, open, const char *, filename, int, flag, int, mode)
-#undef open
-
-int open(const char * filename, int flag, ...)
-{
-       int res;
-       va_list arg;
-
-       va_start(arg,flag);
-       res = __sys_open(filename, flag, va_arg(arg, int));
-       va_end(arg);
-       return res;
-}
diff --git a/lib/setsid.c b/lib/setsid.c
deleted file mode 100644 (file)
index c157b71..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- *  linux/lib/setsid.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/types.h>
-#include <linux/unistd.h>
-
-_syscall0(pid_t,setsid)
-
diff --git a/lib/wait.c b/lib/wait.c
deleted file mode 100644 (file)
index 727a271..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- *  linux/lib/wait.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-#include <linux/types.h>
-
-_syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
-
-pid_t wait(int * wait_stat)
-{
-       return waitpid(-1,wait_stat,0);
-}
-
diff --git a/lib/write.c b/lib/write.c
deleted file mode 100644 (file)
index 9336ed9..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- *  linux/lib/write.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- */
-
-#define __LIBRARY__
-#include <linux/unistd.h>
-#include <linux/types.h>
-
-_syscall3(int,write,int,fd,const char *,buf,off_t,count)
-
index c88eb5f881e04279ba6f525c9e149892a88d9e42..96271ec70ad04b3572e1168c8e9b25a31dd567ea 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -95,7 +95,7 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
                        return -EINVAL;
                }
                if (flags & MAP_DENYWRITE) {
-                       if (file->f_inode->i_wcount > 0)
+                       if (file->f_inode->i_writecount > 0)
                                return -ETXTBSY;
                }
        } else if ((flags & MAP_TYPE) != MAP_PRIVATE)
index f498c8c65eb5b8fc1e1c5dcd939ea805c24c87f2..19e3751e7ddbf469894778102afb24859b508e3f 100644 (file)
@@ -277,8 +277,8 @@ o   Proper netlink device major(36)                 [TESTED]
 o      First parts of the SKIP support                 [IN, not useful]
 o      TCP ICMP (SOSS should work again)               [IN]
 o      IPFW support for TOS changing (Al Longyear)     [IN]
-o      DECNET PPP test code                            [IN]
-o      NFS root                                        [IN]
+o      DECNET PPP test code [Steve]                    [IN]
+o      NFS root        [Miguel/Gero]                   [IN]
 o      Path MTU discovery [ANK]                        [IN]
 
 -------->>>>> 1.3.44 <<<<<<--------
@@ -291,6 +291,14 @@ o  ARP ioctl() call fixes  [Bernd]                 [IN]
 o      Fixes to the name set functions (maybe fixes
        netrom)         [Steve]                         [IN]
 o      Packet protocol labelling (not IPX yet)         [IN]
+o      Faster buffer copy/clone [Linus]                [IN]
+
+-------->>>>> 1.3.46 <<<<<<--------
+
+o      AX.25/NetROM fixes/changes [John Naylor]        [IN]
+o      Further attempts to fix the IPX memory bug      [IN]
+o      ARP fixes (Assorted)                            [IN]
+o      Driver fixes for multicast lists                [IN]
 
 ---------- Things I thought Linus had for a while and not merged ----------------
 
index c233ea201a496c738f97b001f6e94781bfe7e515..14abef40c3ffc6f9b096f4f918996aa3df709449 100644 (file)
@@ -12,6 +12,7 @@ bool 'The IPX protocol' CONFIG_IPX
 bool 'Appletalk DDP' CONFIG_ATALK
 bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25
 if [ "$CONFIG_AX25" = "y" ]; then
+  bool 'AX.25 over Ethernet' CONFIG_BPQETHER
   bool 'Amateur Radio NET/ROM' CONFIG_NETROM
 fi
 bool 'Kernel/User network link driver(ALPHA)' CONFIG_NETLINK
index 6276a24751da81ee2f7c5bea2fd351ab74d19dc6..5c5b2251c56d26693bff7c31af661f8bfa3fedc9 100644 (file)
@@ -651,7 +651,6 @@ int ax25_send_frame(struct sk_buff *skb, ax25_address *src, ax25_address *dest,
 struct device *ax25rtr_get_dev(ax25_address *addr)
 {
        struct device *dev;
-       ax25_address dev_addr;
        
        for (dev = dev_base; dev != NULL; dev = dev->next) {
                if (dev->flags & IFF_UP) {
@@ -660,11 +659,16 @@ struct device *ax25rtr_get_dev(ax25_address *addr)
                                        if (ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
                                                return dev;
                                        break;
-                               case ARPHRD_ETHER:
-                                       if (arp_query((unsigned char *)&dev_addr, dev->pa_addr, dev))
-                                               if (ax25cmp(addr, &dev_addr) == 0)
-                                                       return dev;
+#ifdef CONFIG_BPQETHER
+                               case ARPHRD_ETHER: {
+                                               ax25_address *dev_addr;
+
+                                               if ((dev_addr = ax25_bpq_get_addr(dev)) != NULL)
+                                                       if (ax25cmp(addr, dev_addr) == 0)
+                                                               return dev;
+                                       }
                                        break;
+#endif
                                default:
                                        break;
                        }
@@ -1659,17 +1663,18 @@ static int kiss_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
        return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
 }
 
+#ifdef CONFIG_BPQETHER
 /*
  *     Receive an AX.25 frame via an Ethernet interface.
  */
 static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
 {
-       ax25_address port_call;
+       ax25_address *port_call;
        int len;
 
        skb->sk = NULL;         /* Initially we don't know who its for */
 
-       if (!arp_query((unsigned char *)&port_call, dev->pa_addr, dev)) {
+       if ((port_call = ax25_bpq_get_addr(dev)) == NULL) {
                kfree_skb(skb, FREE_READ);      /* We have no port callsign */
                return 0;
        }
@@ -1679,8 +1684,9 @@ static int bpq_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *
        skb_pull(skb, 2);       /* Remove the length bytes */
        skb_trim(skb, len);     /* Set the length of the data */
 
-       return ax25_rcv(skb, dev, &port_call, ptype);
+       return ax25_rcv(skb, dev, port_call, ptype);
 }
+#endif
 
 static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags)
 {
@@ -1970,6 +1976,13 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                        ax25_uid_policy = amount;
                        return 0;
 
+#ifdef CONFIG_BPQETHER
+               case SIOCAX25BPQADDR:
+                       if (!suser())
+                               return -EPERM;
+                       return ax25_bpq_ioctl(cmd, (void *)arg);
+#endif
+
                case SIOCAX25GETPARMS:
                case SIOCAX25SETPARMS:
                        return ax25_dev_ioctl(cmd, (void *)arg);
@@ -2097,6 +2110,7 @@ static struct packet_type ax25_packet_type =
        NULL,
 };
 
+#ifdef CONFIG_BPQETHER
 static struct packet_type bpq_packet_type = 
 {
        0,      /* MUTTER ntohs(ETH_P_BPQ),*/
@@ -2105,6 +2119,7 @@ static struct packet_type bpq_packet_type =
        NULL,
        NULL,
 };
+#endif
 
 static struct notifier_block ax25_dev_notifier = {
        ax25_device_event,
@@ -2116,8 +2131,10 @@ void ax25_proto_init(struct net_proto *pro)
        sock_register(ax25_proto_ops.family, &ax25_proto_ops);
        ax25_packet_type.type = htons(ETH_P_AX25);
        dev_add_pack(&ax25_packet_type);        
+#ifdef CONFIG_BPQETHER
        bpq_packet_type.type  = htons(ETH_P_BPQ);
        dev_add_pack(&bpq_packet_type);
+#endif
        register_netdevice_notifier(&ax25_dev_notifier);
                          
        proc_net_register(&(struct proc_dir_entry) {
@@ -2140,6 +2157,17 @@ void ax25_proto_init(struct net_proto *pro)
        });
 
        printk("G4KLX/GW4PTS AX.25 for Linux. Version 0.30 BETA for Linux NET3.032 (Linux 1.3.35)\n");
+
+#ifdef CONFIG_BPQETHER
+       proc_net_register(&(struct proc_dir_entry) {
+               PROC_NET_AX25_BPQETHER, 13, "ax25_bpqether",
+               S_IFREG | S_IRUGO, 1, 0, 0,
+               0, &proc_net_inode_operations,
+               ax25_bpq_get_info
+       });
+
+       printk("G8BPQ Encapsulation of AX.25 frames enabled\n");
+#endif
 }
 
 /*
@@ -2149,9 +2177,7 @@ void ax25_proto_init(struct net_proto *pro)
 
 void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
 {
-       static char bcast_addr[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
        unsigned char *ptr;
-       int size;
        
 #ifdef CONFIG_FIREWALL
 
@@ -2163,10 +2189,12 @@ void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
        
 #endif 
        skb->protocol = htons (ETH_P_AX25);
-
-       if (dev->type == ARPHRD_ETHER) 
+#ifdef CONFIG_BPQETHER
+       if(dev->type == ARPHRD_ETHER)
        {
-               if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) 
+               static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
+               int size;
+               if(skb_headroom(skb) < AX25_BPQ_HEADER_LEN)
                {
                        printk("ax25_queue_xmit: not enough space to add BPQ Ether header\n");
                        skb->free = 1;
@@ -2182,12 +2210,15 @@ void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
                *ptr++ = (size + 5) / 256;
 
                dev->hard_header(skb, dev, ETH_P_BPQ, bcast_addr, NULL, 0);
+
+               dev_queue_xmit(skb, dev, pri);
+
+               return;
        } 
-       else 
-       {
-               ptr = skb_push(skb, 1);
-               *ptr++ = 0;                     /* KISS */
-       }
+#endif
+
+       ptr = skb_push(skb, 1);
+       *ptr++ = 0;                     /* KISS */
 
        dev_queue_xmit(skb, dev, pri);
 }
index 7a9102671e04f6f4a476cf553a19f6588b591033..e429f0d76a6cd0ca0ac26ad3744f8060b55435c9 100644 (file)
@@ -593,4 +593,97 @@ int ax25_dev_ioctl(unsigned int cmd, void *arg)
        return 0;
 }
 
+#ifdef CONFIG_BPQETHER
+static struct ax25_bpqdev {
+       struct ax25_bpqdev *next;
+       struct device *dev;
+       ax25_address callsign;
+} *ax25_bpqdev = NULL;
+
+int ax25_bpq_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+       struct ax25_bpqdev *bpqdev;
+       int len     = 0;
+       off_t pos   = 0;
+       off_t begin = 0;
+  
+       cli();
+
+       len += sprintf(buffer, "dev  callsign\n");
+
+       for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next) {
+               len += sprintf(buffer + len, "%-4s %-9s\n",
+                       bpqdev->dev ? bpqdev->dev->name : "???",
+                       ax2asc(&bpqdev->callsign));
+
+               pos = begin + len;
+
+               if (pos < offset) {
+                       len   = 0;
+                       begin = pos;
+               }
+               
+               if (pos > offset + length)
+                       break;
+       }
+
+       sti();
+
+       *start = buffer + (offset - begin);
+       len   -= (offset - begin);
+
+       if (len > length) len = length;
+
+       return len;
+} 
+
+ax25_address *ax25_bpq_get_addr(struct device *dev)
+{
+       struct ax25_bpqdev *bpqdev;
+       
+       for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next)
+               if (bpqdev->dev == dev)
+                       return &bpqdev->callsign;
+
+       return NULL;
+}
+
+int ax25_bpq_ioctl(unsigned int cmd, void *arg)
+{
+       unsigned long flags;
+       struct ax25_bpqdev *bpqdev;
+       struct ax25_bpqaddr_struct bpqaddr;
+       struct device *dev;
+       int err;
+
+       switch (cmd) {
+               case SIOCAX25BPQADDR:
+                       if ((err = verify_area(VERIFY_READ, arg, sizeof(bpqaddr))) != 0)
+                               return err;
+                       memcpy_fromfs(&bpqaddr, arg, sizeof(bpqaddr));
+                       if ((dev = dev_get(bpqaddr.dev)) == NULL)
+                               return -EINVAL;
+                       for (bpqdev = ax25_bpqdev; bpqdev != NULL; bpqdev = bpqdev->next) {
+                               if (bpqdev->dev == dev) {
+                                       bpqdev->callsign = bpqaddr.addr;
+                                       return 0;
+                               }
+                       }
+                       if ((bpqdev = (struct ax25_bpqdev *)kmalloc(sizeof(struct ax25_bpqdev), GFP_ATOMIC)) == NULL)
+                               return -ENOMEM;
+                       bpqdev->dev      = dev;
+                       bpqdev->callsign = bpqaddr.addr;
+                       save_flags(flags);
+                       cli();
+                       bpqdev->next = ax25_bpqdev;
+                       ax25_bpqdev  = bpqdev;
+                       restore_flags(flags);
+                       break;
+       }
+
+       return 0;
+}
+
+#endif
+
 #endif
index 39c838c0d53fb95deccfde12ef1b0c9a6a190301..fb150634b226c27ae5270e8d4440326d48a495e3 100644 (file)
@@ -3,10 +3,10 @@
  *
  *     Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top
  *     of these would make sense. Not tonight however 8-).
- *     This is used because UDP, RAW, PACKET and the to be released IPX layer all have identical select code and mostly
- *     identical recvfrom() code. So we share it here. The select was shared before but buried in udp.c so I moved it.
+ *     This is used because UDP, RAW, PACKET, DDP, IPX, AX.25 and NetROM layer all have identical select code and mostly
+ *     identical recvmsg() code. So we share it here. The select was shared before but buried in udp.c so I moved it.
  *
- *     Authors:        Alan Cox <iiitac@pyr.swan.ac.uk>. (datagram_select() from old udp.c code)
+ *     Authors:        Alan Cox <alan@cymru.net>. (datagram_select() from old udp.c code)
  *
  *     Fixes:
  *             Alan Cox        :       NULL return from skb_peek_copy() understood
@@ -19,9 +19,6 @@
  *             Linus Torvalds  :       BSD semantic fixes.
  *             Alan Cox        :       Datagram iovec handling
  *
- *     Note:
- *             A lot of this will change when the protocol/socket separation
- *     occurs. Using this will make things reasonably clean.
  */
 
 #include <linux/types.h>
index 6647ca6b5af6d180d9f4b3610b1f31af17caf1a3..94b5e9bb6f1066433148a29a055cc94f1d460b27 100644 (file)
@@ -1603,6 +1603,7 @@ static void arp_run_bh()
 /*
  * Test if a hardware address is all zero
  */
+
 static inline int empty(unsigned char * addr, int len)
 {
        while (len > 0) {
@@ -1628,12 +1629,7 @@ static int arp_req_set(struct arpreq *r, struct device * dev)
        u32 ip;
 
        /*
-        * Find out about the hardware type.
-        * We have to be compatible with BSD UNIX, so we have to
-        * assume that a "not set" value (i.e. 0) means Ethernet.
-        *
-        * ANK: Hey, who wrote it? Do you really mean that BSD considers 
-        *      ARPHRD_NETROM as ARPHRD_ETHER, or somthing another?
+        *      Find out about the hardware type.
         */
        
        si = (struct sockaddr_in *) &r->arp_pa;
index c7dc2fcd42e7fbf6cd17da80cf61e84682ca7010..5501914fd07b6f1cbdde4fbf888894ab9b1b8cba 100644 (file)
  *                                     desired by mrouted, fixed the fact it has been
  *                                     broken since 1.3.6 and cleaned up a few minor
  *                                     points.
+ *
  *             Chih-Jen Chang  :       Tried to revise IGMP to Version 2
- *             Tsu-Sheng Tsao          chihjenc@scf.usc.edu and tsusheng@scf.usc.edu
- *                                     The enhancements are based on Steve Deering's ipmulti-3.5 code
+ *             Tsu-Sheng Tsao          E-mail: chihjenc@scf.usc.edu and tsusheng@scf.usc.edu
+ *                                     The enhancements are mainly based on Steve Deering's 
+ *                                     ipmulti-3.5 source code.
  *             Chih-Jen Chang  :       Added the igmp_get_mrouter_info and
  *             Tsu-Sheng Tsao          igmp_set_mrouter_info to keep track of
  *                                     the mrouted version on that device.
@@ -38,6 +40,8 @@
  *             Tsu-Sheng Tsao          igmp_heard_query(). Using this parameter
  *                                     to identify the multicast router verion
  *                                     and do what the IGMP version 2 specified.
+ *             Chih-Jen Chang  :       Added a timer to revert to IGMP V2 router
+ *             Tsu-Sheng Tsao          if the specified time expired.
  */
 
 
 #ifdef CONFIG_IP_MULTICAST
 
 
+/*
+ *     If time expired, change the router type to IGMP_NEW_ROUTER.
+ */
+
+static void ip_router_timer_expire(unsigned long data)
+{
+       struct ip_router_info *i=(struct ip_router_info *)data;
+
+       del_timer(&i->timer);
+       i->type=IGMP_NEW_ROUTER;        /* Revert to new multicast router */
+       i->time=0;
+}
+
 /*
  *     Multicast router info manager
  */
 
-struct router_info     *router_info_head=(struct router_info *)0;
+struct ip_router_info  *ip_router_info_head=(struct ip_router_info *)0;
 
 /*
  *     Get the multicast router info on that device
  */
 
-static struct  router_info     *igmp_get_mrouter_info(struct device *dev)
+static struct  ip_router_info  *igmp_get_mrouter_info(struct device *dev)
 {
-       register struct router_info *i;
+       register struct ip_router_info *i;
 
-       for(i=router_info_head;i!=NULL;i=i->next)
+       for(i=ip_router_info_head;i!=NULL;i=i->next)
        {
                if (i->dev == dev)
                {
@@ -90,12 +107,17 @@ static     struct  router_info     *igmp_get_mrouter_info(struct device *dev)
        /*
         *  Not found. Create a new entry. The default is IGMP V2 router
         */
-       i=(struct router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
+       i=(struct ip_router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
        i->dev = dev;
        i->type = IGMP_NEW_ROUTER;
        i->time = IGMP_AGE_THRESHOLD;
-       i->next = router_info_head;
-       router_info_head = i;
+       i->next = ip_router_info_head;
+       ip_router_info_head = i;
+
+       init_timer(&i->timer);
+       i->timer.data=(unsigned long)i;
+       i->timer.function=&ip_router_timer_expire;
+
        return i;
 }
 
@@ -103,16 +125,27 @@ static    struct  router_info     *igmp_get_mrouter_info(struct device *dev)
  *     Set the multicast router info on that device
  */
 
-static struct  router_info     *igmp_set_mrouter_info(struct device *dev,int type,int time)
+static struct  ip_router_info  *igmp_set_mrouter_info(struct device *dev,int type,int time)
 {
-       register struct router_info *i;
+       register struct ip_router_info *i;
 
-       for(i=router_info_head;i!=NULL;i=i->next)
+       for(i=ip_router_info_head;i!=NULL;i=i->next)
        {
                if (i->dev == dev)
                {
+                       if(i->type==IGMP_OLD_ROUTER)
+                       {
+                               del_timer(&i->timer);
+                       }
+
                        i->type = type;
                        i->time = time;
+
+                       if(i->type==IGMP_OLD_ROUTER)
+                       {
+                               i->timer.expires=jiffies+i->time*HZ;
+                               add_timer(&i->timer);
+                       }
                        return i;
                }
        }
@@ -120,12 +153,22 @@ static    struct  router_info     *igmp_set_mrouter_info(struct device *dev,int type,int
        /*
         *  Not found. Create a new entry.
         */
-       i=(struct router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
+       i=(struct ip_router_info *)kmalloc(sizeof(*i), GFP_KERNEL);
        i->dev = dev;
        i->type = type;
        i->time = time;
-       i->next = router_info_head;
-       router_info_head = i;
+       i->next = ip_router_info_head;
+       ip_router_info_head = i;
+
+       init_timer(&i->timer);
+       i->timer.data=(unsigned long)i;
+       i->timer.function=&ip_router_timer_expire;
+       if(i->type==IGMP_OLD_ROUTER)
+       {
+               i->timer.expires=jiffies+i->time*HZ;
+               add_timer(&i->timer);
+       }
+
        return i;
 }
 
@@ -134,7 +177,6 @@ static      struct  router_info     *igmp_set_mrouter_info(struct device *dev,int type,int
  *     Timer management
  */
 
-
 static void igmp_stop_timer(struct ip_mc_list *im)
 {
        del_timer(&im->timer);
@@ -197,7 +239,7 @@ static void igmp_send_report(struct device *dev, unsigned long address, int type
 static void igmp_timer_expire(unsigned long data)
 {
        struct ip_mc_list *im=(struct ip_mc_list *)data;
-       struct router_info *r;
+       struct ip_router_info *r;
        igmp_stop_timer(im);
        r=igmp_get_mrouter_info(im->interface);
        if(r->type==IGMP_NEW_ROUTER)
@@ -337,7 +379,7 @@ extern __inline__ void igmp_group_dropped(struct ip_mc_list *im)
 
 extern __inline__ void igmp_group_added(struct ip_mc_list *im)
 {
-       struct router_info *r;
+       struct ip_router_info *r;
        igmp_init_timer(im);
        ip_mc_filter_add(im->interface, im->multiaddr);
        r=igmp_get_mrouter_info(im->interface);
@@ -462,20 +504,9 @@ void ip_mc_drop_device(struct device *dev)
 void ip_mc_allhost(struct device *dev)
 {
        struct ip_mc_list *i;
-       /*
-        *      Don't add to non multicast units
-        */
-       if(!(dev->flags&IFF_MULTICAST))
-               return;
-       /*
-        *      No duplicates
-        */
        for(i=dev->ip_mc_list;i!=NULL;i=i->next)
                if(i->multiaddr==IGMP_ALL_HOSTS)
                        return;
-       /*
-        *      Add it
-        */
        i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL);
        if(!i)
                return;
index 68363a52b5cf5515749323f3a5697bb7acc027f1..e6e768c98b50b6992826e02f3576fd84258e2b03 100644 (file)
@@ -29,6 +29,7 @@
  *             Alan Cox        :       BSD style RAW socket demultiplexing.
  *             Alan Cox        :       Beginnings of mrouted support.
  *             Alan Cox        :       Added IP_HDRINCL option.
+ *             Alan Cox        :       Skip broadcast check if BSDism set.
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
@@ -228,7 +229,11 @@ static int raw_sendto(struct sock *sk, const unsigned char *from,
        if (sin.sin_addr.s_addr == INADDR_ANY)
                sin.sin_addr.s_addr = ip_my_addr();
 
-       if (sk->broadcast == 0 && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
+       /*
+        *      BSD raw sockets forget to check SO_BROADCAST ....
+        */
+        
+       if (!sk->bsdism && sk->broadcast == 0 && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
                return -EACCES;
 
        if(sk->ip_hdrincl)
index d14fead2ea25870e97cbdc27584d5e5e15792cbb..bff29053b14f04892da3338a1bb98e6713252ff4 100644 (file)
@@ -37,6 +37,8 @@
  *             Alan Cox        :       Faster /proc handling
  *     Alexey Kuznetsov        :       Massive rework to support tree based routing,
  *                                     routing caches and better behaviour.
+ *             
+ *             Olaf Erb        :       irtt wasnt being copied right.
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
@@ -496,6 +498,7 @@ static struct fib_info * fib_create_info(__u32 gw, struct device * dev,
        fi->fib_refcnt++;
        fi->fib_next = fib_info_list;
        fi->fib_prev = NULL;
+       fi->fib_irtt = irtt;
        if (fib_info_list)
                fib_info_list->fib_prev = fi;
        fib_info_list = fi;
index aace6c137aaeed6888f822202391e27ff41f15ea..13cb71e5e26091a8bf3b4e27600beb8ecb9a3290 100644 (file)
@@ -324,12 +324,12 @@ ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *
        return NOTIFY_DONE;
 }
 
-static int
-ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb)
+static int ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb)
 {
        int     retval;
 
-       if((retval = sock_queue_rcv_skb(sock, skb))<0) {
+       if((retval = sock_queue_rcv_skb(sock, skb))<0) 
+       {
                /*
                 *      We do a FREE_WRITE here because this indicates how
                 *      to treat the socket with which the packet is 
@@ -343,8 +343,13 @@ ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb)
        return retval;
 }
 
-static int
-ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) 
+/*
+ *     On input if skb->sk is set the buffer is a socket sending. We need to ensure the
+ *     accounting is kept right in these cases. At the moment the socket write queue is
+ *     charged for the memory.
+ */
+static int ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) 
 {
        ipx_packet      *ipx = (ipx_packet *)(skb->h.raw);
        ipx_socket      *sock1 = NULL, *sock2 = NULL;
@@ -359,33 +364,42 @@ ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
         *      The *SPECIAL* socket list contains: 0x452(SAP), 0x453(RIP) and
         *      0x456(Diagnostic).
         */
-       if (ipx_primary_net && (intrfc != ipx_primary_net)) {
-               switch (ntohs(ipx->ipx_dest.sock)) {
-               case 0x452:
-               case 0x453:
-               case 0x456:
-                       /*
-                        *      The appropriate thing to do here is to
-                        *      dup the packet and route to the primary net
-                        *      interface via ipxitf_send; however, we'll cheat
-                        *      and just demux it here.
-                        */
-                       sock2 = ipxitf_find_socket(ipx_primary_net, 
+        
+       if (ipx_primary_net && (intrfc != ipx_primary_net)) 
+       {
+               switch (ntohs(ipx->ipx_dest.sock)) 
+               {
+                       case 0x452:
+                       case 0x453:
+                       case 0x456:
+                               /*
+                                *      The appropriate thing to do here is to
+                                *      dup the packet and route to the primary net
+                                *      interface via ipxitf_send; however, we'll cheat
+                                *      and just demux it here.
+                                */
+                               sock2 = ipxitf_find_socket(ipx_primary_net, 
                                        ipx->ipx_dest.sock);
-                       break;
-               default:
-                       break;
+                               break;
+                       default:
+                               break;
                }
        }
 
-       /* if there is nothing to do, return */
-       if ((sock1 == NULL) && (sock2 == NULL)) {
+       /* 
+        *      if there is nothing to do, return. The kfree will
+        *      cancel any charging.
+        */
+        
+       if (sock1 == NULL && sock2 == NULL) 
+       {
                if (!copy) 
                        kfree_skb(skb,FREE_WRITE);
                return 0;
        }
 
-       /* This next segment of code is a little awkward, but it sets it up
+       /*
+        * This next segment of code is a little awkward, but it sets it up
         * so that the appropriate number of copies of the SKB are made and 
         * that skb1 and skb2 point to it (them) so that it (they) can be 
         * demuxed to sock1 and/or sock2.  If we are unable to make enough
@@ -396,43 +410,48 @@ ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
         * this is ok as no socket owns an input buffer.
         */
         
-       if(skb->sk)
+       if(skb->sk && !copy)
        {
                skb->sk->wmem_alloc -= skb->truesize;   /* Adjust */
                skb->sk=NULL;                           /* Disown */
        }
 
         
-       if (copy) {
+       if (copy) 
+       {
                skb1 = skb_clone(skb, GFP_ATOMIC);
-               if (skb1 != NULL) {
+               if (skb1 != NULL) 
                        skb1->arp = skb1->free = 1;
-               }
-       } else {
+       } 
+       else 
+       {
                skb1 = skb;
        }
        
-       if (skb1 == NULL) return -ENOMEM; 
+       if (skb1 == NULL) 
+               return -ENOMEM; 
 
-       /* Do we need 2 SKBs? */
-       if (sock1 && sock2) {
+       /*
+        *      Do we need 2 SKBs? 
+        */
+        
+       if (sock1 && sock2) 
+       {
                skb2 = skb_clone(skb1, GFP_ATOMIC);
-               if (skb2 != NULL) {
+               if (skb2 != NULL) 
                        skb2->arp = skb2->free = 1;
-               }
-       } else {
-               skb2 = skb1;
        }
+       else 
+               skb2 = skb1;
                
-       if (sock1) {
+       if (sock1)
                (void) ipxitf_def_skb_handler(sock1, skb1);
-       }
 
-       if (skb2 == NULL) return -ENOMEM;
+       if (skb2 == NULL) 
+               return -ENOMEM;
 
-       if (sock2) {
+       if (sock2)
                (void) ipxitf_def_skb_handler(sock2, skb2);
-       }
 
        return 0;
 }
@@ -443,30 +462,14 @@ ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb)
        struct sk_buff  *skb2;
        int     in_offset = skb->h.raw - skb->head;
        int     out_offset = intrfc->if_ipx_offset;
-#if 0
-       char    *oldraw;
-#endif 
        int     len;
 
        /* Hopefully, most cases */
        if (in_offset >= out_offset) {
-/*             skb_push(skb,out_offset);*/
                skb->arp = skb->free = 1;
                return skb;
        }
 
-#if 0
-       /* Existing SKB will work, just need to move things around a little */
-       if (in_offset > out_offset) {
-               oldraw = skb->h.raw;
-               skb->h.raw = &(skb->data[out_offset]);
-               memmove(skb->h.raw, oldraw, skb->len);
-               skb->len += out_offset;
-               skb->arp = skb->free = 1;
-               return skb;
-       }
-#endif 
-
        /* Need new SKB */
        len = skb->len + out_offset;
        skb2 = alloc_skb(len, GFP_ATOMIC);
@@ -481,8 +484,7 @@ ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb)
        return skb2;
 }
 
-static int
-ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
+static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
 {
        ipx_packet      *ipx = (ipx_packet *)(skb->h.raw);
        struct device   *dev = intrfc->if_dev;
@@ -491,29 +493,51 @@ ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
        int             send_to_wire = 1;
        int             addr_len;
        
-       /* We need to know how many skbuffs it will take to send out this
-        * packet to avoid unnecessary copies.
+       /* 
+        *      We need to know how many skbuffs it will take to send out this
+        *      packet to avoid unnecessary copies.
         */
+        
        if ((dl == NULL) || (dev == NULL) || (dev->flags & IFF_LOOPBACK)) 
                send_to_wire = 0;
 
-       /* See if this should be demuxed to sockets on this interface */
-       if (ipx->ipx_dest.net == intrfc->if_netnum) {
+       /*
+        *      See if this should be demuxed to sockets on this interface 
+        *
+        *      We want to ensure the original was eaten or that we only use
+        *      up clones.
+        */
+        
+       if (ipx->ipx_dest.net == intrfc->if_netnum) 
+       {
+               /*
+                *      To our own node, loop and free the original.
+                */
                if (memcmp(intrfc->if_node, node, IPX_NODE_LEN) == 0) 
                        return ipxitf_demux_socket(intrfc, skb, 0);
-               if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) {
+               /*
+                *      Broadcast, loop and possibly keep to send on.
+                */
+               if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) 
+               {
                        ipxitf_demux_socket(intrfc, skb, send_to_wire);
-                       if (!send_to_wire) return 0;
+                       if (!send_to_wire) 
+                               return 0;
                }
        }
 
-       /* if the originating net is not equal to our net; this is routed */
-       if (ipx->ipx_source.net != intrfc->if_netnum) {
+       /*
+        *      if the originating net is not equal to our net; this is routed 
+        */
+        
+       if (ipx->ipx_source.net != intrfc->if_netnum) 
+       {
                if (++(ipx->ipx_tctrl) > ipxcfg_max_hops) 
                        send_to_wire = 0;
        }
 
-       if (!send_to_wire) {
+       if (!send_to_wire) 
+       {
                /*
                 *      We do a FREE_WRITE here because this indicates how
                 *      to treat the socket with which the packet is 
@@ -526,45 +550,47 @@ ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
                return 0;
        }
 
-       /* determine the appropriate hardware address */
+       /*
+        *      Determine the appropriate hardware address 
+        */
+        
        addr_len = dev->addr_len;
-       if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) {
+       if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) 
                memcpy(dest_node, dev->broadcast, addr_len);
-       } else {
+       else
                memcpy(dest_node, &(node[IPX_NODE_LEN-addr_len]), addr_len);
-       }
 
-       /* make any compensation for differing physical/data link size */
+       /*
+        *      Make any compensation for differing physical/data link size 
+        */
+        
        skb = ipxitf_adjust_skbuff(intrfc, skb);
-       if (skb == NULL) return 0;
+       if (skb == NULL) 
+               return 0;
 
        /* set up data link and physical headers */
        skb->dev = dev;
        dl->datalink_header(dl, skb, dest_node);
-#ifdef ALREADY_DONE_GUV
-       if (skb->sk != NULL) {
-               /* This is an outbound packet from this host.  We need to 
-                * increment the write count.
-                */
-               skb->sk->wmem_alloc += skb->truesize;
-       }
-#endif
 #if 0
-       /* Now log the packet just before transmission */
+       /* 
+        *      Now log the packet just before transmission 
+        */
+        
        dump_pkt("IPX snd:", (ipx_packet *)skb->h.raw);
        dump_data("ETH hdr:", skb->data, skb->h.raw - skb->data);
 #endif
 
-       /* Send it out */
+       /*
+        *      Send it out 
+        */
+        
        dev_queue_xmit(skb, dev, SOPRI_NORMAL);
        return 0;
 }
 
-static int
-ipxrtr_add_route(unsigned long, ipx_interface *, unsigned char *);
+static int ipxrtr_add_route(unsigned long, ipx_interface *, unsigned char *);
 
-static int
-ipxitf_add_local_route(ipx_interface *intrfc)
+static int ipxitf_add_local_route(ipx_interface *intrfc)
 {
        return ipxrtr_add_route(intrfc->if_netnum, intrfc, NULL);
 }
@@ -1757,8 +1783,10 @@ static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nob
        if (sk->zapped) return -EIO; /* Socket not bound */
        if(flags) return -EINVAL;
                
-       if(usipx) {
-               if(sk->ipx_port == 0) {
+       if(usipx) 
+       {
+               if(sk->ipx_port == 0) 
+               {
                        struct sockaddr_ipx uaddr;
                        int ret;
 
@@ -1772,7 +1800,9 @@ static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nob
                        return -EINVAL;
                if(usipx->sipx_family != AF_IPX)
                        return -EINVAL;
-       } else {
+       }
+       else 
+       {
                if(sk->state!=TCP_ESTABLISHED)
                        return -ENOTCONN;
                usipx=&local_sipx;
index 3b7d7fb00a21d19faea4696c731515b0c6dd21f3..46d4c33581c5308b4a10bb464e848a0869158a50 100644 (file)
@@ -90,7 +90,7 @@ static int netlink_read(struct inode * inode, struct file * file, char * buf, in
                if(file->f_flags&O_NONBLOCK)
                {
                        sti();
-                       return -EWOULDBLOCK;
+                       return -EAGAIN;
                }
                interruptible_sleep_on(&read_space_wait[minor]);
                if(current->signal & ~current->blocked)
@@ -199,7 +199,7 @@ int netlink_post(int unit, struct sk_buff *skb)
                save_flags(flags);
                cli();
                if(rdq_size[unit]+skb->len>MAX_QBYTES)
-                       ret=-EWOULDBLOCK;
+                       ret=-EAGAIN;
                else
                {       
                        skb_queue_tail(&skb_queue_rd[unit], skb);
index 60791593f2a6316804d9ab82e06396c3d54dd89b..19274b94b5e062dda8e28e65607d9e9498aac2b4 100644 (file)
@@ -1368,7 +1368,7 @@ void nr_proto_init(struct net_proto *pro)
 {
        sock_register(nr_proto_ops.family, &nr_proto_ops);
        register_netdevice_notifier(&nr_dev_notifier);
-       printk("G4KLX NET/ROM for Linux. Version 0.3 ALPHA for AX25.030 Linux 1.3.35\n");
+       printk("G4KLX NET/ROM for Linux. Version 0.4 ALPHA for AX25.030 Linux 1.3.45\n");
 
        nr_default.quality    = NR_DEFAULT_QUAL;
        nr_default.obs_count  = NR_DEFAULT_OBS;
index 2181e60e9d294b61236b04ddd0cd0313e7415bb3..bb87f74fd5ab5bbfee61a74b37cc1ac36038d929 100644 (file)
@@ -515,7 +515,6 @@ void nr_rt_device_down(struct device *dev)
 static struct device *nr_ax25_dev_get(char *devname)
 {
        struct device *dev;
-       ax25_address callsign;
 
        if ((dev = dev_get(devname)) == NULL)
                return NULL;
@@ -523,9 +522,11 @@ static struct device *nr_ax25_dev_get(char *devname)
        if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25)
                return dev;
 
+#ifdef CONFIG_BPQETHER
        if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER)
-               if (arp_query((unsigned char *)&callsign, dev->pa_addr, dev))
+               if (ax25_bpq_get_addr(dev) != NULL)
                        return dev;
+#endif
        
        return NULL;
 }