]> git.neil.brown.name Git - history.git/commitdiff
Import 2.1.66 2.1.66
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:14:04 +0000 (15:14 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:14:04 +0000 (15:14 -0500)
152 files changed:
CREDITS
Documentation/00-INDEX
Documentation/Configure.help
Documentation/devices.tex
Documentation/devices.txt
Documentation/filesystems/smbfs.txt
Documentation/ftape.txt [new file with mode: 0644]
MAINTAINERS
Makefile
arch/alpha/lib/strrchr.S
arch/i386/defconfig
arch/i386/kernel/entry.S
arch/i386/kernel/traps.c
arch/i386/mm/fault.c
drivers/block/floppy.c
drivers/block/md.c
drivers/char/ChangeLog
drivers/char/Config.in
drivers/char/Makefile
drivers/char/esp.c
drivers/char/ftape/Config.in [new file with mode: 0644]
drivers/char/ftape/Makefile
drivers/char/ftape/README.PCI
drivers/char/ftape/RELEASE-NOTES
drivers/char/ftape/calibr.c [deleted file]
drivers/char/ftape/calibr.h [deleted file]
drivers/char/ftape/compressor/Makefile [new file with mode: 0644]
drivers/char/ftape/compressor/lzrw3.c [new file with mode: 0644]
drivers/char/ftape/compressor/lzrw3.h [new file with mode: 0644]
drivers/char/ftape/compressor/zftape-compress.c [new file with mode: 0644]
drivers/char/ftape/compressor/zftape-compress.h [new file with mode: 0644]
drivers/char/ftape/ecc.c [deleted file]
drivers/char/ftape/ecc.h [deleted file]
drivers/char/ftape/fc-10.c [deleted file]
drivers/char/ftape/fc-10.h [deleted file]
drivers/char/ftape/fdc-io.c [deleted file]
drivers/char/ftape/fdc-io.h [deleted file]
drivers/char/ftape/fdc-isr.c [deleted file]
drivers/char/ftape/fdc-isr.h [deleted file]
drivers/char/ftape/ftape-bsm.c [deleted file]
drivers/char/ftape/ftape-bsm.h [deleted file]
drivers/char/ftape/ftape-ctl.c [deleted file]
drivers/char/ftape/ftape-ctl.h [deleted file]
drivers/char/ftape/ftape-eof.c [deleted file]
drivers/char/ftape/ftape-eof.h [deleted file]
drivers/char/ftape/ftape-io.c [deleted file]
drivers/char/ftape/ftape-io.h [deleted file]
drivers/char/ftape/ftape-read.c [deleted file]
drivers/char/ftape/ftape-read.h [deleted file]
drivers/char/ftape/ftape-rw.c [deleted file]
drivers/char/ftape/ftape-rw.h [deleted file]
drivers/char/ftape/ftape-write.c [deleted file]
drivers/char/ftape/ftape-write.h [deleted file]
drivers/char/ftape/kernel-interface.c [deleted file]
drivers/char/ftape/kernel-interface.h [deleted file]
drivers/char/ftape/lowlevel/Makefile [new file with mode: 0644]
drivers/char/ftape/lowlevel/fc-10.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/fc-10.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/fdc-io.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/fdc-io.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/fdc-isr.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/fdc-isr.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-bsm.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-bsm.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-buffer.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-buffer.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-calibr.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-calibr.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-ctl.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-ctl.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-ecc.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-ecc.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-format.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-format.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-init.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-init.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-io.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-io.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-proc.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-proc.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-read.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-read.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-rw.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-rw.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-setup.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-tracing.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-tracing.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-write.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape-write.h [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape_syms.c [new file with mode: 0644]
drivers/char/ftape/lowlevel/ftape_syms.h [new file with mode: 0644]
drivers/char/ftape/qic117.h [deleted file]
drivers/char/ftape/tracing.c [deleted file]
drivers/char/ftape/tracing.h [deleted file]
drivers/char/ftape/vendors.h [deleted file]
drivers/char/ftape/zftape/Makefile [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-buffers.c [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-buffers.h [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-ctl.c [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-ctl.h [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-eof.c [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-eof.h [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-init.c [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-init.h [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-read.c [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-read.h [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-rw.c [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-rw.h [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-vtbl.c [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-vtbl.h [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-write.c [new file with mode: 0644]
drivers/char/ftape/zftape/zftape-write.h [new file with mode: 0644]
drivers/char/ftape/zftape/zftape_syms.c [new file with mode: 0644]
drivers/char/ftape/zftape/zftape_syms.h [new file with mode: 0644]
drivers/char/rocket.c
drivers/char/serial.c
drivers/char/tty_io.c
drivers/char/tty_ioctl.c
drivers/pci/pci.c
drivers/scsi/ppa.c
drivers/scsi/ppa.h
fs/buffer.c
fs/ext2/fsync.c
fs/hpfs/hpfs_fs.c
fs/nfsd/nfsfh.c
fs/smbfs/cache.c
fs/smbfs/dir.c
fs/smbfs/file.c
fs/smbfs/inode.c
fs/smbfs/proc.c
fs/smbfs/sock.c
include/linux/fs.h
include/linux/ftape-header-segment.h [new file with mode: 0644]
include/linux/ftape-vendors.h [new file with mode: 0644]
include/linux/ftape.h
include/linux/mtio.h
include/linux/pci.h
include/linux/qic117.h [new file with mode: 0644]
include/linux/serial.h
include/linux/serialP.h [new file with mode: 0644]
include/linux/smb_fs.h
include/linux/smb_fs_i.h
include/linux/smbno.h
include/linux/tty.h
include/linux/tty_driver.h
include/linux/zftape.h [new file with mode: 0644]
init/main.c
kernel/ksyms.c
mm/filemap.c
net/netlink.c
net/sunrpc/clnt.c
net/sunrpc/xprt.c

diff --git a/CREDITS b/CREDITS
index 534534cf8a784575541ac10f61431d2238dcbb0d..774023566dd60a68e58d66bdc93f912ea8327cd6 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -1609,11 +1609,12 @@ S: 1098 VA Amsterdam
 S: The Netherlands
 
 N: Tim Waugh
-E: tmw20@cyberelk.demon.co.uk
+E: tim@cyberelk.demon.co.uk
 D: Co-architect of the parallel-port sharing system
-S: 51 Frensham Road
-S: Southsea
-S: Hampshire, UK. PO4 8AE
+S: 12 Station Road
+S: Park Gate
+S: Southampton SO31 7GJ
+S: United Kingdom
 
 N: Juergen Weigert
 E: jnweiger@immd4.informatik.uni-erlangen.de
index c1c171f2bfa239a02b0fe9c815b9fb9b0befda24..a1d31366091a53dadc96c684106854c12fa76e2c 100644 (file)
@@ -36,6 +36,8 @@ ez.txt
        - documentation for the SyQuest parallel port EZ drive support.
 filesystems/
        - directory with info on the various filesystems that Linux supports.
+ftape.txt
+       - notes about the floppy tape device driver
 ide.txt
        - important info for users of ATA devices (IDE/EIDE disks and CD-ROMS)
 initrd.txt
index 9347961228680e167db049c8810e79be7871e7d3..4330f07332e126c8734028f36ed7225ae6975a27 100644 (file)
@@ -5009,22 +5009,278 @@ CONFIG_QIC02_DYNCONF
   support package.
   If you want to use the qic02conf program, say Y.
 
-Ftape (QIC-80/Travan) support
+Floppy tape drive (QIC-80/40/3010/3020/TR-1/TR-2/TR-3) support
 CONFIG_FTAPE
-  If you have a tape drive that is connected to your floppy
-  controller, say Y here. Some tape drives (like the Iomega Ditto
-  3200) come with a high speed controller of its own.  These drives
-  (and their companion controller) are also supported by this driver,
-  so say Y if you have one. If you have a special controller (such as
-  the CMS FC-10, FC-20, Iomega Mach-II, or Ditto Dash), you must say Y
-  here and configure it by editing the file
-  drivers/char/ftape/Makefile. If you want to use such a tape drive on
-  a PCI-bus based system, please read the file
-  drivers/char/ftape/README.PCI. This driver is also available as a
-  runtime loadable module ( = code which can be inserted in and
-  removed from the running kernel whenever you want). If you want to
-  compile it as a module, say M here and read
-  Documentation/modules.txt. The module will be called ftape.o.
+    If you have a tape drive that is connected to your floppy
+  controller, say `Y' here. Some tape drives (like the Seagate "Tape
+  Store 3200" or the Iomega "Ditto 3200" or the Exabyte "Eagle TR-3")
+  come with a "high speed" controller of their own. These drives (and
+  their companion controllers) are also supported.
+    If you have a special controller (such as the CMS FC-10, FC-20,
+  Mountain Mach-II, or any controller that is based on the Intel 82078
+  FDC like the high speed controllers by Seagate and Exabyte and
+  Iomega's "Ditto Dash") you must configure it by selecting the
+  appropriate entries from the "Floppy tape controllers" sub-menu and
+  possibly modify the default values for the IRQ and DMA channel and
+  the IO base in ftape's configuration menu. If you want to use your
+  floppy tape drive on a PCI-bus based system, please read the file
+  `./drivers/char/ftape/README.PCI'.
+    The ftape kernel driver is also available as a runtime loadable
+  module ( = code which can be inserted in and removed from the
+  running kernel whenever you want). If you want to compile it as a
+  module, say `M' here and read `./Documentation/modules.txt'.
+    Note that the `Ftape-HOWTO' is out of date (sorry), but there is a
+  web page with more recent documentation at
+  `http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/'. This page
+  always contains the latest release of the ftape driver and useful
+  information (backup software, ftape related patches and
+  documentation, FAQ). The Ftape-HOWTO still contains useful
+  information, though, but documents the older 2.08 version of ftape,
+  whereas this version of ftape is 3.04.
+    Note that the file system interface has changed quite a lot
+  compared to previous versions of ftape. Please read
+  `./Documentation/ftape.txt'
+
+The file system interface for ftape
+CONFIG_ZFTAPE
+    Normally, you want to say `Y' or `M'. DON'T say `N' here or you
+  WON'T BE ABLE TO USE YOUR FLOPPY TAPE DRIVE.
+    The ftape module itself no longer contains the routines necessary
+  to interface with the kernel VFS layer (i.e. to actually write data
+  to and read data from the tape drive). Instead the file system
+  interface (i.e. the hardware independent part of the driver) has
+  been moved to a separate module.
+    If you say `M' zftape will be compiled as as a runtime loadable
+  module ( = code which can be inserted in and removed from the
+  running kernel whenever you want). In this case you should read
+  `./Documentation/modules.txt'.
+    There will also be another module called `zft-compressor.o' which
+  contains code to support user transparent on-the-fly compression
+  based on Ross William's lzrw3 algorithm. `zft-compressor.o' will be
+  compiled as a runtime loadable module only. If you have enabled
+  auto-loading of kernel modules via `kerneld' (i.e. have said `Y' to
+  CONFIG_KERNELD) then `zft-compressor.o' will be loaded automatically
+  by zftape when needed.
+    Despite of its name zftape does NOT use compression by
+  default. The file `./Documentation/ftape.txt' contains a short
+  description of the most important changes in the file system
+  interface compared to previous versions of ftape. The ftape home
+  page `http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/'
+  contains further information. IMPORTANT NOTE: zftape can read
+  archives created by previous versions of ftape and provide file mark
+  support (i.e. fast skipping between tape archives) but previous
+  version of ftape will lack file mark support when reading archives
+  produced by zftape.
+
+Builtin on-the-fly compression for zftape, based on lzrw3
+CONFIG_ZFT_COMPRESSOR
+    This module implements builtin on-the-fly compression for ftape's
+  file system interface zftape. `zft-compressor.o' is compiled as a
+  runtime loadable module only and will be loaded by zftape on demand
+  if support for auto-loading of modules via `kerneld' has been
+  enabled (CONFIG_KERNELD).
+
+Default block size for zftape
+CONFIG_ZFT_DFLT_BLK_SZ
+    If unsure leave this at its default value, i.e. 10240.  Note that
+  you specify only the default block size here. The block size can be
+  changed at run time using the MTSETBLK tape operation with the
+  MTIOCTOP ioctl (i.e. with "mt -f /dev/qft0 setblk #BLKSZ" from the
+  shell command line).
+    The probably most striking difference between zftape and previous
+  versions of ftape is the fact that all data must be written or read
+  in multiples of a fixed block size. The block size defaults to
+  10240 which is what GNU tar uses. The values for the block size
+  should be either 1 or multiples of 1024 up to a maximum value of
+  63488 (i.e. 62k). If you specify `1' then zftape's builtin
+  compression will be disabled.
+    Reasonable values are `10240' (GNU tar's default block size),
+  `5120' (afio's default block size), `32768' (default block size some
+  backup programs assume for SCSI tape drives) or `1' (no restriction
+  on block size, but disables builtin compression).
+
+Number of DMA buffers
+CONFIG_FT_NR_BUFFERS
+    Please leave this at `3"' unless you REALLY know what you are
+  doing. It is not necessary to change this value. Values below 3 make
+  the proper use of ftape impossible, values greater than 3 are waste
+  of memory. You can change the amount of DMA memory used by ftape at
+  runtime with "mt -f /dev/qft0 setdrvbuffer #NUMBUFFERS". Each buffer
+  wastes 32kb of memory. Please note that this memory cannot be
+  swapped out.
+
+Procfs entry for ftape
+CONFIG_FT_PROC_FS
+    Optional. Saying `Y' will result in creation of a file
+  `/proc/ftape' under the proc file system. This files can be viewed
+  with your favorite pager (i.e. use "more /proc/ftape/history" or
+  "less /proc/ftape/history" or simply "cat /proc/ftape/history"). The
+  file will contain some status information about the inserted
+  cartridge, the kernel driver, your tape drive, the floppy disk
+  controller and the error history for the most recent use of the
+  kernel driver. Saying `Y' will enlarge the size of the ftape driver
+  by approximately 2k.
+  WARNING: When compiling ftape as a module (i.e. saying `M' to
+  `CONFIG_FTAPE') it is dangerous to use ftape's proc file system
+  interface. Accessing `/proc/ftape' while the module is unloaded will
+  result in a kernel Oops. This cannot be fixed from inside ftape.
+
+Controlling the amount of debugging output of ftape
+CONFIG_FT_NORMAL_DEBUG
+    This option controls the amount of debugging output the ftape driver
+  is ABLE to produce; it does not increase or diminish the debugging
+  level itself. If unsure, leave this at its default setting,
+  i.e. choose "Normal".
+    Ftape can print lots of debugging messages to the system console
+  resp. kernel log files. Reducing the amount of possible debugging
+  output reduces the size of the kernel module by some kb, so it might
+  be a good idea to use "None" for emergency boot floppies.
+    If you want to save memory then the following strategy is
+  recommended: leave this option at its default setting "Normal" until
+  you know that the driver works as expected, afterwards reconfigure
+  the kernel, this time specifying "Reduced" or "None" and recompile
+  and install the kernel as usual. Note that choosing "Excessive"
+  debugging output does not increase the amount of debugging output
+  printed to the console but only makes it possible to produce
+  "Excessive" debugging output.
+    Please read `./Documentation/ftape.txt' for a short description
+  how to control the amount of debugging output.
+
+The floppy drive controller for ftape
+CONFIG_FT_STD_FDC
+    Only change this setting if you have a special controller. If you
+  didn't plug any add-on card into your computer system but just
+  plugged the floppy tape cable into the already existing floppy drive
+  controller then you don't want to change the default setting,
+  i.e. choose "Standard".
+  Choose "MACH-2" if you have a Mountain Mach-2 controller.
+  Choose "FC-10/FC-20" if you have a Colorado FC-10 or FC-20
+  controller.
+  Choose "Alt/82078" if you have another controller that is located at
+  an IO base address different from the standard floppy drive
+  controller's base address of `0x3f0', or uses an IRQ (interrupt)
+  channel different from `6', or a DMA channel different from
+  `2'. This is necessary for any controller card that is based on
+  Intel's 82078 FDC such as Seagate's, Exabyte's and Iomega's "high
+  speed" controllers.
+    If you choose something other than "Standard" then please make
+  sure that the settings for the IO base address and the IRQ and DMA
+  channel in the configuration menus below are correct. Use the manual
+  of your tape drive to determine the correct settings!
+    If you are already successfully using your tape drive with another
+  operating system then you definitely should use the same settings
+  for the IO base, the IRQ and DMA channel that have proven to work
+  with that other OS.
+    Note that this menu lets you specify only the default setting for
+  the hardware setup. The hardware configuration can be changed at
+  boot time (when ftape is compiled into the kernel, i.e. if you
+  have said `Y' to `CONFIG_FTAPE') or module load time (i.e. if you
+  have said `M' to `CONFIG_FTAPE').
+    Please read also the file `./Documentation/ftape.txt' which
+  contains a short description of the parameters that can be set at
+  boot or load time. If you want to use your floppy tape drive on a
+  PCI-bus based system, please read the file
+  `./drivers/char/ftape/README.PCI'.
+
+IO base of the floppy disk controller used with Ftape
+CONFIG_FT_FDC_BASE
+    You don't need to specify a value if the following default
+  settings for the base IO address are correct:
+  <<< MACH-2     : 0x1E0 >>>
+  <<< FC-10/FC-20: 0x180 >>>
+  <<< Secondary  : 0x370 >>>
+  Secondary refers to a secondary FDC controller like the "high speed"
+  controllers delivered by Seagate or Exabyte or Iomega's Ditto Dash.
+    Please make sure that the setting for the IO base address
+  specified here is correct. USE THE MANUAL OF YOUR TAPE DRIVE OR
+  CONTROLLER CARD TO DETERMINE THE CORRECT SETTING. If you are already
+  successfully using the tape drive with another operating system then
+  you definitely should use the same settings for the IO base that has
+  proven to work with that other OS.
+    Note that this menu lets you specify only the default setting for
+  the IO base. The hardware configuration can be changed at boot time
+  (when ftape is compiled into the kernel, i.e. if you specify `Y' to
+  `CONFIG_FTAPE') or module load time (i.e. if you have say `M' to
+  `CONFIG_FTAPE').
+    Please read also the file `./Documentation/ftape.txt' which
+  contains a short description of the parameters that can be set at
+  boot or load time.
+  
+IRQ channel for the floppy disk controller used with Ftape
+CONFIG_FT_FDC_IRQ
+    You don't need to specify a value if the following default
+  settings for the interrupt channel are correct:
+  <<< MACH-2     : 6 >>>
+  <<< FC-10/FC-20: 9 >>>
+  <<< Secondary  : 6 >>>
+  Secondary refers to secondary a FDC controller like the "high speed"
+  controllers delivered by Seagate or Exabyte or Iomega's Ditto Dash.
+    Please make sure that the setting for the IO base address
+  specified here is correct. USE THE MANUAL OF YOUR TAPE DRIVE OR
+  CONTROLLER CARD TO DETERMINE THE CORRECT SETTING. If you are already
+  successfully using the tape drive with another operating system then
+  you definitely should use the same settings for the IO base that has
+  proven to work with that other OS.
+    Note that this menu lets you specify only the default setting for
+  the IRQ channel. The hardware configuration can be changed at boot
+  time (when ftape is compiled into the kernel, i.e. if you specify
+  `Y' to `CONFIG_FTAPE') or module load time (i.e. if you have say `M'
+  to `CONFIG_FTAPE').
+    Please read also the file `./Documentation/ftape.txt' which
+  contains a short description of the parameters that can be set at
+  boot or load time.
+  
+DMA channel for the floppy disk controller used with Ftape
+CONFIG_FT_FDC_DMA
+    You don't need to specify a value if the following default
+  settings for the DMA channel are correct:
+  <<< MACH-2     : 2 >>>
+  <<< FC-10/FC-20: 3 >>>
+  <<< Secondary  : 2 >>>
+  Secondary refers to a secondary FDC controller like the "high speed"
+  controllers delivered by Seagate or Exabyte or Iomega's Ditto Dash.
+    Please make sure that the setting for the IO base address
+  specified here is correct. USE THE MANUAL OF YOUR TAPE DRIVE OR
+  CONTROLLER CARD TO DETERMINE THE CORRECT SETTING. If you are already
+  successfully using the tape drive with another operating system then
+  you definitely should use the same settings for the IO base that has
+  proven to work with that other OS.
+    Note that this menu lets you specify only the default setting for
+  the DMA channel. The hardware configuration can be changed at boot
+  time (when ftape is compiled into the kernel, i.e. if you specify
+  `Y' to `CONFIG_FTAPE') or module load time (i.e. if you have say `M'
+  to `CONFIG_FTAPE').
+    Please read also the file `./Documentation/ftape.txt' which
+  contains a short description of the parameters that can be set at
+  boot or load time.
+
+FDC FIFO Threshold before requesting DMA service
+CONFIG_FT_FDC_THR
+    Set the FIFO threshold of the FDC. If this is higher the DMA
+  controller may serve the FCD after a higher latency time. If this is
+  lower, less DMA transfers occur leading to less bus contention.
+    You may try to tune this if ftape annoys you with "reduced data
+  rate because of excessive overrun errors" messages. However, this
+  doesn't seem to have too much an effect.
+  If unsure, don't touch the initial value, i.e. leave it at "8".
+
+FDC maximum data rate
+CONFIG_FT_FDC_MAX_RATE
+    With some mother board/FDC combinations ftape will not be able to
+  run your FDC/tape drive combination at the highest available
+  speed. If this is the case you'll encounter "reduced data rate
+  because of excessive overrun errors" messages and lots of retries
+  before ftape finally decides to reduce the data rate.
+    In this case it might be desirable to tell ftape beforehand that
+  it need not try to run the tape drive at the highest available
+  speed. If unsure, leave this disabled, i.e. leave it at 2000
+  bits/sec.
+
+Main CPU frequency, only for DEC alpha machine
+CONFIG_FT_ALPHA_CLOCK
+  On some DEC Alpha machines the CPU clock frequency cannot be
+  determined automatically, so you need to specify it here ONLY if
+  running a DEC Alpha, otherwise this setting has no effect.
 
 Zilog serial support
 CONFIG_SUN_ZS
@@ -6342,3 +6598,10 @@ CONFIG_MACE
 # LocalWords:  dataless kerneltype SYSNAME Netbeui Comtrol Rocketport palmtop
 # LocalWords:  nvram SYSRQ SysRq PrintScreen sysrq NVRAMs NvRAM Shortwave RTTY
 # LocalWords:  HFMODEM shortwave Sitor Amtor Pactor GTOR hfmodem hayes TX TMOUT
+# LocalWords:  QIC TR CONFIG FTAPE Iomega CMS FC FDC Exabyte Iomega's DFLT
+# LocalWords:  tapedrive THR FCD IRQ DMA SZ PCI ftape README txt HOWTO
+# LocalWords:  http www rwth aachen LBFM claus FAQ mt ZFTAPE VFS
+# LocalWords:  zftape zft William's lzrw kerneld BLK zftape's tar's
+# LocalWords:  afio's MTSETBLK MTIOCTOP dev qft setblk BLKSZ NR
+# LocalWords:  setdrvbuffer kb NUMBUFFERS Procfs PROC FS proc resp STD
+# LocalWords:  Alt LocalWords
index b48b37b3eccffe5019997579cd13b1cfcabed766..41329703afd855b459ac8be80d79dce4ba053eb1 100644 (file)
@@ -797,14 +797,30 @@ major number 3).
 
 \begin{devicelist}
 \major{27}{}{char }{QIC-117 tape}
-       \minor{0}{/dev/rft0}{Unit 0, rewind-on-close}
-       \minor{1}{/dev/rft1}{Unit 1, rewind-on-close}
-       \minor{2}{/dev/rft2}{Unit 2, rewind-on-close}
-       \minor{3}{/dev/rft3}{Unit 3, rewind-on-close}
-       \minor{4}{/dev/nrft0}{Unit 0, no rewind-on-close}
-       \minor{5}{/dev/nrft1}{Unit 1, no rewind-on-close}
-       \minor{6}{/dev/nrft2}{Unit 2, no rewind-on-close}
-       \minor{7}{/dev/nrft3}{Unit 3, no rewind-on-close}
+        \minor{0}{/dev/qft0}{Unit 0, rewind-on-close}
+        \minor{1}{/dev/qft1}{Unit 1, rewind-on-close}
+        \minor{2}{/dev/qft2}{Unit 2, rewind-on-close}
+        \minor{3}{/dev/qft3}{Unit 3, rewind-on-close}
+        \minor{4}{/dev/nqft0}{Unit 0, no rewind-on-close}
+        \minor{5}{/dev/nqft1}{Unit 1, no rewind-on-close}
+        \minor{6}{/dev/nqft2}{Unit 2, no rewind-on-close}
+        \minor{7}{/dev/nqft3}{Unit 3, no rewind-on-close}
+        \minor{16}{/dev/zqft0}{Unit 0, rewind-on-close, compression}
+        \minor{17}{/dev/zqft1}{Unit 1, rewind-on-close, compression}
+        \minor{18}{/dev/zqft2}{Unit 2, rewind-on-close, compression}
+        \minor{19}{/dev/zqft3}{Unit 3, rewind-on-close, compression}
+        \minor{20}{/dev/nzqft0}{Unit 0, no-rewind-on-close, compression}
+        \minor{21}{/dev/nzqft1}{Unit 1, no-rewind-on-close, compression}
+        \minor{22}{/dev/nzqft2}{Unit 2, no-rewind-on-close, compression}
+        \minor{23}{/dev/nzqft3}{Unit 3, no-rewind-on-close, compression}
+        \minor{32}{/dev/rawft0}{Unit 0, rewind-on-close, no file marks}
+        \minor{33}{/dev/rawft1}{Unit 1, rewind-on-close, no file marks}
+        \minor{34}{/dev/rawft2}{Unit 2, rewind-on-close, no file marks}
+        \minor{35}{/dev/rawft3}{Unit 3, rewind-on-close, no file marks}
+        \minor{36}{/dev/zqft0}{Unit 0, no-rewind-on-close, no file marks}
+        \minor{37}{/dev/zqft1}{Unit 1, no-rewind-on-close, no file marks}
+        \minor{38}{/dev/zqft2}{Unit 2, no-rewind-on-close, no file marks}
+        \minor{39}{/dev/zqft3}{Unit 3, no-rewind-on-close, no file marks}
 \\
 \major{  }{}{block}{Third Matsushita (Panasonic/SoundBlaster) CD-ROM}
        \minor{0}{/dev/sbpcd8}{Panasonic CD-ROM controller 2 unit 0}
@@ -1613,6 +1629,7 @@ It is recommended that these links exist on all systems:
 \begin{nodelist}
 \link{/dev/core}{/proc/kcore}{symbolic}{Backward compatibility}
 \link{/dev/ramdisk}{ram0}{symbolic}{Backward compatibility}
+\link{/dev/rft0}{qft0}{symbolic}{Backward compatibility}
 \link{/dev/ftape}{rft0}{symbolic}{Backward compatibility}
 \link{/dev/scd?}{sr?}{hard}{Alternate name for CD-ROMs}
 \link{/dev/fd?D*}{fd?u*}{hard}{Backward compatibility}
index 1a84f5563c81f82559ad7b386aec93c35ecd5fc1..86799025bf1d6b2609ebc4b5407a82c6efd190de 100644 (file)
@@ -507,14 +507,30 @@ Your cooperation is appreciated.
                  3 = /dev/sbpcd7       Panasonic CD-ROM controller 1 unit 3
 
  27 char       QIC-117 tape
-                 0 = /dev/rft0         Unit 0, rewind-on-close
-                 1 = /dev/rft1         Unit 1, rewind-on-close
-                 2 = /dev/rft2         Unit 2, rewind-on-close
-                 3 = /dev/rft3         Unit 3, rewind-on-close
-                 4 = /dev/nrft0        Unit 0, no rewind-on-close
-                 5 = /dev/nrft1        Unit 1, no rewind-on-close
-                 6 = /dev/nrft2        Unit 2, no rewind-on-close
-                 7 = /dev/nrft3        Unit 3, no rewind-on-close
+                 0 = /dev/qft0         Unit 0, rewind-on-close
+                 1 = /dev/qft1         Unit 1, rewind-on-close
+                 2 = /dev/qft2         Unit 2, rewind-on-close
+                 3 = /dev/qft3         Unit 3, rewind-on-close
+                 4 = /dev/nqft0        Unit 0, no rewind-on-close
+                 5 = /dev/nqft1        Unit 1, no rewind-on-close
+                 6 = /dev/nqft2        Unit 2, no rewind-on-close
+                 7 = /dev/nqft3        Unit 3, no rewind-on-close
+                 16 = /dev/zqft0       Unit 0, rewind-on-close, compression
+                 17 = /dev/zqft1       Unit 1, rewind-on-close, compression
+                 18 = /dev/zqft2       Unit 2, rewind-on-close, compression
+                 19 = /dev/zqft3       Unit 3, rewind-on-close, compression
+                 20 = /dev/nzqft0      Unit 0, no-rewind, compression
+                 21 = /dev/nzqft1      Unit 1, no-rewind, compression
+                 22 = /dev/nzqft2      Unit 2, no-rewind, compression
+                 23 = /dev/nzqft3      Unit 3, no-rewind, compression
+                 32 = /dev/rawft0      Unit 0, rewind-on-close, no file marks
+                 33 = /dev/rawft1      Unit 1, rewind-on-close, no file marks
+                 34 = /dev/rawft2      Unit 2, rewind-on-close, no file marks
+                 35 = /dev/rawft3      Unit 3, rewind-on-close, no file marks
+                 36 = /dev/zqft0       Unit 0, no-rewind, no file marks
+                 37 = /dev/zqft1       Unit 1, no-rewind, no file marks
+                 38 = /dev/zqft2       Unit 2, no-rewind, no file marks
+                 39 = /dev/zqft3       Unit 3, no-rewind, no file marks
     block      Third Matsushita (Panasonic/SoundBlaster) CD-ROM
                  0 = /dev/sbpcd8       Panasonic CD-ROM controller 2 unit 0
                  1 = /dev/sbpcd9       Panasonic CD-ROM controller 2 unit 1
@@ -1137,7 +1153,8 @@ It is recommended that these links exist on all systems:
 
 /dev/core      /proc/kcore     symbolic        Backward compatibility
 /dev/ramdisk   ram0            symbolic        Backward compatibility
-/dev/ftape     rft0            symbolic        Backward compatibility
+/dev/rft0      qft0            symbolic        Backward compatibility
+/dev/ftape     qft0            symbolic        Backward compatibility
 /dev/scd?      sr?             hard            Alternate SCSI CD-ROM name
 
 
index fb2d55bdb47d81f673f52ec5f70fe34c627ba8a5..04eb2e58617b09337405ec52feff2c8e7fb7527b 100644 (file)
@@ -1,13 +1,54 @@
-smbfs is a filesystem which understands the SMB protocol. This is the
-protocol Windows for Workgroups, Windows NT or Lan Manager use to talk
-to each other. smbfs was inspired by samba, the program written by
-Andrew Tridgell that turns any unix host into a file server for DOS or
-Windows clients.  See ftp://nimbus.anu.edu.au/pub/tridge/samba/ for
-this interesting program suite and lots of more information on SMB and
-NetBIOS over TCP/IP. There you also find explanation for concepts like
-netbios name or share.
-
-To use smbfs, you need a special mount program, which can be found in
-the smbfs package, found on
-
-       ftp://ftp.gwdg.de/pub/linux/misc/smbfs/
+Smbfs is a filesystem that implements the SMB protocol, which is the
+protocol used by Windows for Workgroups, Windows 95 and Windows NT.
+Smbfs was inspired by samba, the program written by Andrew Tridgell
+that turns any unix host into a file server for DOS or Windows clients.
+See ftp://nimbus.anu.edu.au/pub/tridge/samba/ for this interesting
+program suite and much more information on SMB, NetBIOS over TCP/IP,
+and explanations for concepts like netbios name or share.
+
+To use smbfs, you need to install the Samba package (Samba-1.9.17p1 or
+later), and you need the special mount program from the smbfs package
+(smbfs-2.1.0 or later), found on
+
+       ftp://ftp.gwdg.de/pub/linux/misc/smbfs/dontuse
+
+After downloading the smbfs package, apply the patch to the smbclient
+program and recompile.  Smbfs can then be mounted from the smbclient
+command line, as for example:
+
+       smb: \>mount /mnt/tmp -f 755
+
+For convenience, you may wish to package the command in a script like this:
+
+#!/bin/sh
+echo "mount /mnt/tmp -f 755" | smbclient //server/c$ -U administrator%
+
+Mount-Time Options
+Windows 95 has several bugs that affect SMB operations, and smbfs includes
+work-arounds for all of the bugs found (so far, at least.) These can be
+enabled at compile-time with the CONFIG_SMB_WIN95 kernel option.
+
+Unfortunately, some of the Win 95 work-arounds interact with Win NT bugs,
+so if you're using several different types of servers on your network you
+probably want to enable the work-arounds at mount time. To do this, answer
+`N' to the CONFIG_SMB_WIN95 option, and add the needed options listed below
+to the file mode argument of the mount command for the Win 95 servers.
+
+Option                  Value  Effect
+Identify Win 95 Server    1    Enables bug fixes
+Use Core Attributes       2    Speeds up directory scans, only mtime
+
+To apply the options, sum the values and prepend it to the file mode. For
+example, to use both options with file mode 755, you would prepend 3 to 755:
+
+       cnt>mount /mnt/tmp -f 3755
+
+Smbfs will print a message at mount time confirming the selected options.
+Note that _only_ Windows 95 servers require special treatment; using the
+"core attributes" option with Win NT will give trash timestamp values.
+
+To summarize, if your network includes both Win 95 and NT servers:
+(1) Do _not_ enable the CONFIG_SMB_WIN95 kernel option
+
+(2) Add the desired work-around options to the mount command for your
+    Win 95 server(s).
diff --git a/Documentation/ftape.txt b/Documentation/ftape.txt
new file mode 100644 (file)
index 0000000..28c3912
--- /dev/null
@@ -0,0 +1,326 @@
+Intro
+=====
+
+This file describes some issues involved when using the "ftape"
+floppy tape device driver that comes with the Linux kernel. This
+document deals with ftape-3.04 and later. Please read the section
+"Changes" for the most striking differences between version 3.04 and
+2.08; the latter was the version of ftape delivered with the kernel
+until kernel version 2.030 and 2.1.57. ftape-3.x developed as the
+re-unification of ftape-2.x and zftape. zftape was developed in
+parallel with the stock ftape-2.x driver sharing the same hardware
+support but providing an enhanced file system interface. zftape also
+provided user transparent block-wise on-the-fly compression (regard it
+as a feature or bug of zftape).
+
+ftape has a home page at
+
+http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape
+
+which contains further information about ftape. Please cross check
+this WWW address against the address given (if any) in the MAINTAINERS
+file located in the top level directory of the Linux kernel source
+tree.
+
+Contents
+========
+
+A minus 1: Ftape documentation
+
+A. Changes
+   1. Goal
+   2. I/O Block Size
+   3. Write Access when not at EOD (End Of Data) or BOT (Begin Of Tape)
+   4. MTBSF - backspace over file mark and position at its EOT side
+   5. Formatting
+   6. Interchanging cartridges with other operating systems
+
+B. Debugging Output
+   1. Introduction
+   2. Tuning the debugging output
+
+C. Boot and load time configuration
+   1. Setting boot time parameters
+   2. Module load time parameters
+   3. Ftape boot- and load time options
+   4. Example kernel parameter setting
+   5. Example module parameter setting
+
+D. Support and contacts
+
+*******************************************************************************
+
+A minus 1. Ftape documentation
+==============================
+
+Unluckily, the ftape-HOWTO is out of date. This really needs to be
+changed. Up to data documentation as well as recent development
+versions of ftape and useful links to related topics can be found at
+the ftape home page at
+
+http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape
+
+*******************************************************************************
+
+A. Changes
+==========
+
+1. Goal
+   ~~~~
+   The goal of all that incompatibilities was to give ftape an interface
+   that resembles the interface provided by SCSI tape drives as close
+   as possible. Thus any Unix backup program that is known to work
+   with SCSI tape drives should also work with ftape-3.04 and above.
+
+   The concept of a fixed block size for read/write transfers is
+   rather unrelated to this SCSI tape compatibility at the file system
+   interface level. It developed out of a feature of zftape, a
+   block wise user transparent on-the-fly compression. That compression
+   support will not be dropped in future releases for compatibility
+   reasons with previous releases of zftape.
+
+2. I/O Block Size
+   ~~~~~~~~~~~~~~
+   The probably most striking difference between ftape-2.x and
+   ftape-3.x with the zftape file system interface is the concept of a
+   fixed block size: data must be written to or read from the tape in
+   multiples of a fixed block size. The block size defaults to 10k
+   which is the default block size of GNU tar. While this is quite
+   usual for SCSI tapes (block size of 32k?) and the QIC-150 driver
+   `./drivers/char/tpqic02.c' ftape-2.x allowed data to be written in
+   arbitrary portions to the tape.
+
+   The block size can be tuned either during kernel configuration or
+   at runtime with the MTIOCTOP ioctl using the MTSETBLK operation
+   (i.e. do "mt -f /dev/qft0" setblk #BLKSZ). A block size of 0
+   switches to variable block size mode i.e. "mt setblk 0" switches
+   off the block size restriction. However, this disables zftape's
+   built in on-the-fly compression which doesn't work with variable
+   block size mode.
+
+   The BLKSZ parameter must be given as a byte count and must be a
+   multiple of 32k or 0, i.e. use "mt setblk 32768" to switch to a
+   block size of 32k.
+
+   The typical symptom of a block size mismatch is an "invalid
+   argument" error message.
+
+3. Write Access when not at EOD (End Of Data) or BOT (Begin Of Tape)
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   zftape (the file system interface of ftape-3.x) denies write access
+   to the tape cartridge when it isn't positioned either at BOT or
+   EOD. This inconvenience has been introduced as it was reported that
+   the former behavior of ftape-2.x which allowed write access at
+   arbitrary locations already has caused data loss with some backup
+   programs.
+
+4. MTBSF - backspace over file mark and position at its EOT side
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   ftape-2.x didn't handle the MTBSF tape operation correctly. A MTBSF
+   call (i.e. "mt -f /dev/nqft0 bsf #COUNT") should space over #COUNT
+   file marks and then position at the EOT tape side of the the file
+   mark. This has to be taken literally, i.e. "mt -f /dev/nqft0 bsf 1"
+   should simply position at the start of the current volume.
+
+5. Formatting
+   ~~~~~~~~~~
+   ftape-3.x DOES support formatting of floppy tape cartridges. You
+   need the `ftformat' program that is shipped with the modules version
+   of ftape-3.x. Please get the latest version of ftape from
+
+   ftp://sunsite.unc.edu/pub/Linux/kernel/tapes
+
+   or from the ftape home page at
+
+   http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape
+
+   `ftformat' is contained in the `./contrib/' subdirectory of that
+   separate ftape package.
+
+6. Interchanging cartridges with other operating systems
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+   The internal emulation of Unix tape device file marks has changed
+   completely. ftape-3.x now uses the volume table segment as specified
+   by the QIC-40/80/3010/3020/113 standards to emulate file marks. As
+   a consequence there is limited support to interchange cartridges
+   with other operating systems.
+
+   To be more precise: ftape will detect volumes written by other OS's
+   programs and other OS's programs will detect volumes written by
+   ftape-3.x.
+
+   However, it isn't possible to extract the data dumped to the tape
+   by some MSDOG program with ftape-3.x. This exceeds the scope of a
+   kernel device driver. If you need such functionality, then go ahead
+   and write a user space utility that is able to do
+   that. ftape-3.x/zftape already provides all kernel level support
+   necessary to do that.
+
+*******************************************************************************
+
+B. Debugging Output
+   ================
+
+1. Introduction
+   ~~~~~~~~~~~~
+   The ftape driver can be very noisy in that is can print lots of
+   debugging messages to the kernel log files and the system console.
+   While this is useful for debugging it might be annoying during
+   normal use and enlarges the size of the driver by several kilobytes.
+
+   To reduce the size of the driver you can trim the maximal amount of
+   debugging information available during kernel configuration. Please
+   refer to the kernel configuration script and its on-line help
+   functionality.
+
+   The amount of debugging output maps to the "tracing" boot time
+   option and the "ft_tracing" modules option as follows:
+
+   0              bugs
+   1              + errors (with call-stack dump)
+   2              + warnings
+   3              + information
+   4              + more information
+   5              + program flow
+   6              + fdc/dma info
+   7              + data flow
+   8              + everything else
+
+2. Tuning the debugging output
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   To reduce the amount of debugging output printed to the system
+   console you can
+
+   i)  trim the debugging output at run-time with
+
+       mt -f /dev/nqft0 setdensity #DBGLVL
+
+       where "#DBGLVL" is a number between 0 and 9
+
+   ii) trim the debugging output at module load time with
+
+       insmod ftape.o ft_tracing=#DBGLVL
+
+       Of course, this applies only if you have configured ftape to be
+       compiled as a module.
+
+   iii) trim the debugging output during system boot time. Add the
+       following to the kernel command line:
+
+       ftape=#DBGLVL,tracing
+
+       Please refer also to the next section if you don't know how to
+       set boot time parameters.
+
+*******************************************************************************
+
+C. Boot and load time configuration
+   ================================
+
+1. Setting boot time parameters
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
+   Assuming that you use lilo, the LI)nux LO)ader, boot time kernel
+   parameters can be set by adding a line
+
+   append some_kernel_boot_time_parameter
+
+   to `/etc/lilo.conf' or at real boot time by typing in the options
+   at the prompt provided by LILO. I can't give you advice on how to
+   specify those parameters with other loaders as I don't use them.
+
+   For ftape, each "some_kernel_boot_time_parameter" looks like
+   "ftape=value,option". As an example, the debugging output can be
+   increased with
+
+   ftape=4,tracing
+
+   NOTE: the value precedes the option name.
+
+2. Module load time parameters
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   Module parameters can be specified either directly when invoking
+   the program 'insmod' at the shell prompt:
+
+   insmod ftape.o ft_tracing=4
+
+   or by editing the file `/etc/conf.modules' in which case they take
+   affect each time when the module is loaded with `modprobe' (please
+   refer to the modules documentation, i.e. `modules.txt' and the
+   respective manual pages). Thus, you should add a line
+
+   options ftape ft_tracing=4
+
+   to `/etc/conf.modules` if you intend to increase the debugging
+   output of the driver.
+
+
+3. Ftape boot- and load time options
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+   i.   Controlling the amount of debugging output
+        DBGLVL has to be replaced by a number between 0 and 8.
+
+        module                 |  kernel command line
+        -----------------------|----------------------
+        ft_tracing=DBGLVL      |  ftape=DBGLVL,tracing
+
+   ii.  Hardware setup
+       BASE is the base address of your floppy disk controller,
+        IRQ and DMA give its interrupt and dma channel, respectively.
+        BOOL is an integer, "0" means: "NO!", any other value means:
+       "YES!". You don't need to specify anything if connecting your tape
+        drive to the standard floppy disk controller. All of these
+       values have reasonable defaults. The defaults can be modified
+       during kernel configuration, i.e. while running "make config",
+       "make menuconfig" or "make xconfig" in the top level directory
+       of the Linux kernel source tree. Please refer also to the on
+       line documentation provided during that kernel configuration
+       process.
+
+        module                 |  kernel command line
+        -----------------------|----------------------
+        ft_fdc_base=BASE       |  ftape=BASE,ioport
+        ft_fdc_irq=IRQ         |  ftape=IRQ,irq
+        ft_fdc_dma=DMA         |  ftape=DMA,dma
+        ft_probe_fc10=BOOL     |  ftape=BOOL,fc10
+        ft_mach2=BOOL          |  ftape=BOOL,mach2
+        ft_fdc_threshold=THR   |  ftape=THR,threshold
+        ft_fdc_rate_limit=RATE |  ftape=RATE,datarate
+
+4. Example kernel parameter setting
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
+   To configure ftape to probe for a Colorado FC-10/FC-20 controller
+   and to increase the amount of debugging output a little bit, add
+   the following line to `/etc/lilo.conf':
+
+   append ftape=1,fc10 ftape=4,tracing
+
+5. Example module parameter setting
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   To do the same, but with ftape compiled as a loadable kernel
+   module, add the following line to `/etc/conf.modules':
+
+   options ftape ft_probe_fc10=1 ft_tracing=4
+
+*******************************************************************************
+
+D. Support and contacts
+   ====================
+
+   Ftape is distributed under the GNU General Public License. There is
+   absolutely no warranty for this software. However, you can reach
+   the current maintainer of the ftape package under the email address
+   given in the MAINTAINERS file which is located in the top level
+   directory of the Linux kernel source tree. There you'll find also
+   the relevant mailing list to use as a discussion forum and the web
+   page to query for the most recent documentation, related work and
+   development versions of ftape.
+
+
+ LocalWords:  ftape Linux zftape http www rwth aachen LBFM claus EOD config
+ LocalWords:  datarate LocalWords BOT MTBSF EOT HOWTO QIC tpqic menuconfig
+ LocalWords:  MTIOCTOP MTSETBLK mt dev qft setblk BLKSZ bsf zftape's xconfig
+ LocalWords:  nqft ftformat ftp sunsite unc edu contrib ft MSDOG fdc
+ LocalWords:  dma setdensity DBGLVL insmod lilo LI nux ader conf txt
+ LocalWords:  modprobe IRQ BOOL ioport irq fc mach THR
index 8d88bd1fbd87c901c56925f578525f536348657d..b21816264a8018a3d18dd8c45b0ac136536a5c3f 100644 (file)
@@ -239,6 +239,7 @@ FTAPE/QIC-117:
 P:     Claus-Justus Heine
 M:     claus@momo.math.rwth-aachen.de
 L:     linux-tape@vger.rutgers.edu
+W:     http://www-math.math.rwth-aachen.de/~LBFM/claus/ftape/
 S:     Maintained
 
 IPX NETWORK LAYER
index f4f1d7b47b504411598f162e07db4686900c19db..96e709217736db2d4c5b9759c18b3b3643702a95 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 65
+SUBLEVEL = 66
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
index dbfedc504c28181bd2f68c24f4c20af46a18a109..88bca0ee9b36be95760ba74823cfcf3bcf82b7ff 100644 (file)
@@ -21,7 +21,7 @@ strrchr:
        zapnot  a1, 1, a1       # e0    : zero extend our test character
        mov     zero, t6        # .. e1 : t6 is last match aligned addr
        sll     a1, 8, t5       # e0    : replicate our test character
-       mov     zero, t7        # .. e1 : t7 is last match byte compare mask
+       mov     zero, t8        # .. e1 : t8 is last match byte compare mask
        or      t5, a1, a1      # e0    :
        ldq_u   t0, 0(a0)       # .. e1 : load first quadword
        sll     a1, 16, t5      # e0    :
@@ -43,7 +43,7 @@ strrchr:
 $loop:
        ldq     t0, 8(v0)       # e0    : load next quadword
        cmovne  t3, v0, t6      # .. e1 : save previous comparisons match
-       cmovne  t3, t3, t7      # e0    :
+       cmovne  t3, t3, t8      # e0    :
        addq    v0, 8, v0       # .. e1 :
        xor     t0, a1, t2      # e0    :
        cmpbge  zero, t0, t1    # .. e1 : bits set iff byte == zero
@@ -58,22 +58,22 @@ $eos:
        or      t4, t5, t4      # e1    : ... and including the null
 
        and     t3, t4, t3      # e0    : mask out char matches after null
-       cmovne  t3, t3, t7      # .. e1 : save it, if match found
+       cmovne  t3, t3, t8      # .. e1 : save it, if match found
        cmovne  t3, v0, t6      # e0    :
 
        /* Locate the address of the last matched character */
 
        /* Retain the early exit for the ev4 -- the ev5 mispredict penalty
           is 5 cycles -- the same as just falling through.  */
-       beq     t7, $retnull    # .. e1 :
+       beq     t8, $retnull    # .. e1 :
 
-       and     t7, 0xf0, t2    # e0    : binary search for the high bit set
-       cmovne  t2, t2, t7      # .. e1 (zdb)
+       and     t8, 0xf0, t2    # e0    : binary search for the high bit set
+       cmovne  t2, t2, t8      # .. e1 (zdb)
        cmovne  t2, 4, t2       # e0    :
-       and     t7, 0xcc, t1    # .. e1 :
-       cmovne  t1, t1, t7      # e0    :
+       and     t8, 0xcc, t1    # .. e1 :
+       cmovne  t1, t1, t8      # e0    :
        cmovne  t1, 2, t1       # .. e1 :
-       and     t7, 0xaa, t0    # e0    :
+       and     t8, 0xaa, t0    # e0    :
        cmovne  t0, 1, t0       # .. e1 (zdb)
        addq    t2, t1, t1      # e0    :
        addq    t6, t0, v0      # .. e1 : add our aligned base ptr to the mix
index e60528b141a2d8b90ca0781849da6469a2840fa9..c73955c2d62fbbcb85b24757625818c8d68a02d7 100644 (file)
@@ -211,7 +211,7 @@ CONFIG_ISO9660_FS=y
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
 # CONFIG_ROOT_NFS is not set
-# CONFIG_NFSD is not set
+CONFIG_NFSD=y
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
 # CONFIG_SMB_FS is not set
@@ -245,7 +245,6 @@ CONFIG_82C710_MOUSE=y
 # CONFIG_PC110_PAD is not set
 # CONFIG_UMISC is not set
 # CONFIG_QIC02_TAPE is not set
-# CONFIG_FTAPE is not set
 # CONFIG_APM is not set
 # CONFIG_WATCHDOG is not set
 # CONFIG_RTC is not set
@@ -253,6 +252,11 @@ CONFIG_82C710_MOUSE=y
 # CONFIG_JOYSTICK is not set
 # CONFIG_MISC_RADIO is not set
 
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+
 #
 # Sound
 #
index 693df69a40e812767982a61f06878d36c3d5c609..dffc1379576945070cbe3901e4e3ca8260c2329a 100644 (file)
@@ -349,10 +349,6 @@ ENTRY(page_fault)
        pushl $ SYMBOL_NAME(do_page_fault)
        jmp error_code
 
-ENTRY(page_fault_f00f)
-       pushl $ SYMBOL_NAME(do_page_fault_f00f)
-       jmp error_code
-
 ENTRY(spurious_interrupt_bug)
        pushl $0
        pushl $ SYMBOL_NAME(do_spurious_interrupt_bug)
index 54a833a351081a62abed7a744ac72f6dc0f01aed..f08b7de6bb1f6d24b9f71628763b6513c5692a41 100644 (file)
@@ -103,7 +103,6 @@ asmlinkage void segment_not_present(void);
 asmlinkage void stack_segment(void);
 asmlinkage void general_protection(void);
 asmlinkage void page_fault(void);
-asmlinkage void page_fault_f00f(void);
 asmlinkage void coprocessor_error(void);
 asmlinkage void reserved(void);
 asmlinkage void alignment_check(void);
@@ -417,14 +416,9 @@ asmlinkage void math_emulate(long arg)
 __initfunc(void trap_init_f00f_bug(void))
 {
        unsigned long page;
-
-       /*
-        * We use a special page fault handler, to actually detect
-        * 'bounced' traps/exceptions #0-6. This new page fault
-        * handler is a few tens of cycles slower than the 'normal'
-        * one.
-        */
-       set_trap_gate(14,&page_fault_f00f);
+       pgd_t * pgd;
+       pmd_t * pmd;
+       pte_t * pte;
 
        /*
         * Allocate a new page in virtual address space, 
@@ -433,16 +427,21 @@ __initfunc(void trap_init_f00f_bug(void))
         * fault for IDT entries #0-#6..
         */
        page = (unsigned long) vmalloc(PAGE_SIZE);
-       memcpy((void *) page, idt_table + 7, (256-7)*8);
+       memcpy((void *) page, idt_table, 256*8);
+
+       pgd = pgd_offset(&init_mm, page);
+       pmd = pmd_offset(pgd, page);
+       pte = pte_offset(pmd, page);
+       *pte = pte_wrprotect(*pte);
+       local_flush_tlb();
 
        /*
         * "idt" is magic - it overlaps the idt_descr
         * variable so that updating idt will automatically
         * update the idt descriptor..
         */
-       idt = (struct desc_struct *)(page - 7*8);
+       idt = (struct desc_struct *)page;
        __asm__ __volatile__("lidt %0": "=m" (idt_descr));
-
 }
 
 
index 711079fdecaf9595439c971d8a133257df2cf9b4..beb9f91a467a1070e08a7bb7a35cda42e552ed67 100644 (file)
@@ -74,6 +74,9 @@ bad_area:
        return 0;
 }
 
+asmlinkage void do_invalid_op (struct pt_regs *, unsigned long);
+
+extern int pentium_f00f_bug;
 
 /*
  * This routine handles page faults.  It determines the address,
@@ -85,16 +88,19 @@ bad_area:
  *     bit 1 == 0 means read, 1 means write
  *     bit 2 == 0 means kernel, 1 means user-mode
  */
-static void __do_page_fault(struct pt_regs *regs, unsigned long error_code,
-                               unsigned long address)
+asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
 {
        struct task_struct *tsk;
        struct mm_struct *mm;
        struct vm_area_struct * vma;
+       unsigned long address;
        unsigned long page;
        unsigned long fixup;
        int write;
 
+       /* get the address */
+       __asm__("movl %%cr2,%0":"=r" (address));
+
        lock_kernel();
        tsk = current;
        mm = tsk->mm;
@@ -171,6 +177,21 @@ bad_area:
                goto out;
        }
 
+       /*
+        * Pentium F0 0F C7 C8 bug workaround.
+        */
+       if (pentium_f00f_bug) {
+               unsigned long nr;
+               
+               nr = (address - (unsigned long) idt) >> 3;
+
+               if (nr == 6) {
+                       unlock_kernel();
+                       do_invalid_op(regs, 0);
+                       return;
+               }
+       }
+
        /* Are we prepared to handle this kernel fault?  */
        if ((fixup = search_exception_table(regs->eip)) != 0) {
                printk(KERN_DEBUG "%s: Exception at [<%lx>] cr2=%lx (fixup: %lx)\n",
@@ -216,65 +237,3 @@ bad_area:
 out:
        unlock_kernel();
 }
-
-
-/*
- * One of these two functions is the real page fault handler, which one depends
- * on wether the CPU has the F00F bug:
- */
-
-asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
-{
-       unsigned long address;
-
-       /* get the address */
-       __asm__("movl %%cr2,%0":"=r" (address));
-
-       __do_page_fault(regs, error_code, address);
-}
-
-asmlinkage void do_divide_error (struct pt_regs *, unsigned long);
-asmlinkage void do_debug (struct pt_regs *, unsigned long);
-asmlinkage void do_nmi (struct pt_regs *, unsigned long);
-asmlinkage void do_int3 (struct pt_regs *, unsigned long);
-asmlinkage void do_overflow (struct pt_regs *, unsigned long);
-asmlinkage void do_bounds (struct pt_regs *, unsigned long);
-asmlinkage void do_invalid_op (struct pt_regs *, unsigned long);
-
-extern int pentium_f00f_bug;
-
-asmlinkage void do_page_fault_f00f(struct pt_regs *regs, unsigned long error_code)
-{
-       unsigned long address;
-
-       /* get the address */
-       __asm__("movl %%cr2,%0":"=r" (address));
-
-       /*
-        * Pentium F0 0F C7 C8 bug workaround. Do this first,
-        * to make sure we don't have locking problems with
-        * asynchronous traps (ie NMI). 
-        */
-       if ( !(error_code & 5) && pentium_f00f_bug ) {
-               unsigned long nr;
-               
-               nr = (address - (unsigned long) idt) >> 3;
-
-               if (nr < 7) {
-                       static void (*handler[])(struct pt_regs *, unsigned long) = {
-                               do_divide_error,        /* 0 - divide overflow */
-                               do_debug,               /* 1 - debug trap */
-                               do_nmi,                 /* 2 - NMI */
-                               do_int3,                /* 3 - int 3 */
-                               do_overflow,            /* 4 - overflow */
-                               do_bounds,              /* 5 - bound range */
-                               do_invalid_op };        /* 6 - invalid opcode */
-                       if (nr == 3 || nr == 4) regs->eip++;
-                       handler[nr](regs, 0);
-                       return;
-               }
-       }
-       __do_page_fault(regs, error_code, address);
-}
-
-
index b251946599be10ea86ef4c637f9e61995de42b97..3c9b085c8900f89d8152999ece311e738e1770c0 100644 (file)
@@ -4016,11 +4016,6 @@ __initfunc(int floppy_init(void))
                        continue;
                }
 
-               request_region(FDCS->address, 6, "floppy");
-               request_region(FDCS->address+7, 1, "floppy DIR");
-               /* address + 6 is reserved, and may be taken by IDE.
-                * Unfortunately, Adaptec doesn't know this :-(, */
-
                have_no_fdc = 0;
                /* Not all FDCs seem to be able to handle the version command
                 * properly, so force a reset for the standard FDC clones,
@@ -4042,7 +4037,6 @@ __initfunc(int floppy_init(void))
 
 static int floppy_grab_irq_and_dma(void)
 {
-       int i;
        unsigned long flags;
 
        INT_OFF;
@@ -4052,16 +4046,6 @@ static int floppy_grab_irq_and_dma(void)
        }
        INT_ON;
        MOD_INC_USE_COUNT;
-       for (i=0; i< N_FDC; i++){
-               if (fdc_state[i].address != -1){
-                       fdc = i;
-                       reset_fdc_info(1);
-                       fd_outb(FDCS->dor, FD_DOR);
-               }
-       }
-       fdc = 0;
-       set_dor(0, ~0, 8);  /* avoid immediate interrupt */
-
        if (fd_request_irq()) {
                DPRINT("Unable to grab IRQ%d for the floppy driver\n",
                        FLOPPY_IRQ);
@@ -4077,6 +4061,36 @@ static int floppy_grab_irq_and_dma(void)
                usage_count--;
                return -1;
        }
+       for (fdc=0; fdc< N_FDC; fdc++){
+               if (FDCS->address != -1){
+                       if (check_region(FDCS->address, 6) < 0 ||
+                           check_region(FDCS->address+7, 1) < 0) {
+                               DPRINT("Floppy io-port 0x%04x in use\n", FDCS->address);
+                               fd_free_irq();
+                               fd_free_dma();
+                               while(--fdc >= 0) {
+                                       release_region(FDCS->address, 6);
+                                       release_region(FDCS->address+7, 1);
+                               }
+                               MOD_DEC_USE_COUNT;
+                               usage_count--;
+                               return -1;
+                       }
+                       request_region(FDCS->address, 6, "floppy");
+                       request_region(FDCS->address+7, 1, "floppy DIR");
+                       /* address + 6 is reserved, and may be taken by IDE.
+                        * Unfortunately, Adaptec doesn't know this :-(, */
+               }
+       }
+       for (fdc=0; fdc< N_FDC; fdc++){
+               if (FDCS->address != -1){
+                       reset_fdc_info(1);
+                       fd_outb(FDCS->dor, FD_DOR);
+               }
+       }
+       fdc = 0;
+       set_dor(0, ~0, 8);  /* avoid immediate interrupt */
+
        for (fdc = 0; fdc < N_FDC; fdc++)
                if (FDCS->address != -1)
                        fd_outb(FDCS->dor, FD_DOR);
@@ -4087,6 +4101,7 @@ static int floppy_grab_irq_and_dma(void)
 
 static void floppy_release_irq_and_dma(void)
 {
+       int old_fdc;
 #ifdef FLOPPY_SANITY_CHECK
 #ifndef __sparc__
        int drive;
@@ -4136,6 +4151,13 @@ static void floppy_release_irq_and_dma(void)
        if (floppy_tq.sync)
                printk("task queue still active\n");
 #endif
+       old_fdc = fdc;
+       for (fdc = 0; fdc < N_FDC; fdc++)
+               if (FDCS->address != -1) {
+                       release_region(FDCS->address, 6);
+                       release_region(FDCS->address+7, 1);
+               }
+       fdc = old_fdc;
        MOD_DEC_USE_COUNT;
 }
 
@@ -4224,12 +4246,6 @@ void cleanup_module(void)
 {
        int fdc, dummy;
                
-       for (fdc=0; fdc<2; fdc++)
-               if (FDCS->address != -1){
-                       release_region(FDCS->address, 6);
-                       release_region(FDCS->address+7, 1);
-       }
-               
        unregister_blkdev(MAJOR_NR, "fd");
 
        blk_dev[MAJOR_NR].request_fn = 0;
index 4a29c87cc86849c48652bc6fb56b43740abe83ad..2018883cabc2955bcd01bbc3c0c78da83995bc24 100644 (file)
@@ -541,6 +541,9 @@ static int do_md_add (int minor, kdev_t dev)
        if (md_dev[minor].nb_dev==MAX_REAL)
                return -EINVAL;
 
+       if (!fs_may_mount (dev))
+               return -EBUSY;
+
        if (blk_size[MAJOR(dev)] == NULL || blk_size[MAJOR(dev)][MINOR(dev)] == 0) {
                printk("md_add(): zero device size, huh, bailing out.\n");
                return -EINVAL;
index 3083c0c543f0b3ea5e4f73be0cf9a7b8fda54748..0e61fb8198485f98573ed672a752465cb0191ee7 100644 (file)
@@ -1,3 +1,47 @@
+Mon Nov 24 10:37:49 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+       * serial.c, esp.c, rocket.c: Change drivers to take advantage of
+               tty_get_baud_rate().
+       
+       * tty_io.c (tty_get_baud_rate): New function which computes the
+               correct baud rate for the tty.  More factoring out of
+               common code out of the serial driver to the high-level tty
+               functions....
+
+Sat Nov 22 07:53:36 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+       * serial.c, esp.c, rocket.c: Add tty->driver.break() routine, and
+               allow high-level tty code to handle the break and soft
+               carrier ioctls.
+       
+       * tty_ioctl.c (n_tty_ioctl): Support TIOCGSOFTCAR and
+               TIOCSSOFTCAR, so that device drivers don't have to support
+               it.
+
+       * serial.c (autoconfig): Change 16750 test to hopefully eliminate
+               false results by people with strange 16550A's being
+               detected as 16750's.  Hopefully 16750's will still be
+               detected as 16750, and other wierd UART's won't get poorly
+               autodetected.  If this doesn't work, I'll have to disable
+               the auto identification for the 16750....
+
+       * tty_io.c (tty_hangup): Now do actually do the tty hangup
+               processing during the timer processing, and disable
+               interrupts while doing the hangup processing.  This avoids
+               several nasty race conditions which happened when the
+               hangup processing was done asynchronously.
+               (tty_ioctl): Do break handling in the tty driver if
+               driver's break function is supported.  
+               (tty_flip_buffer_push): New exported function which should
+               be used by drivers to push characters in the flip buffer
+               to the tty handler.  This may either be done using a task
+               queue function for better CPU efficiency, or directly for
+               low latency operation.
+
+       * serial.c (rs_set_termios): Fix bug rs_set_termios when
+               transitioning away from B0, submitted by Stanislav
+               Voronyi. 
+
 Thu Jun 19 20:05:58 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
 
        * serial.c (begin_break, end_break, rs_ioctl): Applied patch
index 0a3b20d95a231a74407320c5753ef0abea25fc64..e2ee6ceb94d05bb7cceeba5f9990dac74787c903 100644 (file)
@@ -76,11 +76,6 @@ if [ "$CONFIG_QIC02_TAPE" != "n" ]; then
   fi
 fi
 
-tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE
-if [ "$CONFIG_FTAPE" != "n" ]; then
-  comment 'Set IObase/IRQ/DMA for ftape in ./drivers/char/ftape/Makefile'
-fi
-
 bool 'Advanced Power Management BIOS support' CONFIG_APM
 if [ "$CONFIG_APM" = "y" ]; then
   bool '   Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND
@@ -116,4 +111,13 @@ if [ "$CONFIG_MISC_RADIO" != "n" ]; then
     hex '    RadioTrack i/o port (0x20f or 0x30f)' CONFIG_RADIO_RTRACK_PORT 0x20f
   fi
 fi
+
+mainmenu_option next_comment
+comment 'Ftape, the floppy tape device driver'
+tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE
+if [ "$CONFIG_FTAPE" != "n" ]; then
+  source drivers/char/ftape/Config.in
+fi
+endmenu
+
 endmenu
index 74ae53ef29a62fe122cdebbcbde9d599cc76d1e4..dc15a97c116a35b674b0bf41acd00df885da4972 100644 (file)
@@ -297,8 +297,11 @@ else
 endif
 
 ifeq ($(CONFIG_FTAPE),y)
-SUB_DIRS += ftape
-L_OBJS += ftape/ftape.o
+L_OBJS       += ftape/ftape.o
+SUB_DIRS     += ftape
+ifneq ($(CONFIG_ZFTAPE),n)
+MOD_SUB_DIRS += ftape
+endif
 else
   ifeq ($(CONFIG_FTAPE),m)
   MOD_SUB_DIRS += ftape
index 7c42399da5c62ca0fc779a279198a60087a51a83..5fdcd8a7784055896284c00b1755ee5ed11c4598 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial.h>
+#include <linux/serialP.h>
 #include <linux/serial_reg.h>
 #include <linux/major.h>
 #include <linux/string.h>
@@ -140,11 +141,8 @@ static void change_speed(struct esp_struct *info);
 static void rs_wait_until_sent(struct tty_struct *, int);
        
 /*
- * This assumes you have a 1.8432 MHz clock for your UART.
- *
- * It'd be nice if someone built a serial card with a 24.576 MHz
- * clock, since the 16550A is capable of handling a top speed of 1.5
- * megabits/second; but this requires the faster clock.
+ * The ESP card has a clock rate of 14.7456 MHz (that is, 2**ESPC_SCALE
+ * times the normal 1.8432 Mhz clock of most serial boards).
  */
 #define BASE_BAUD ((1843200 / 16) * (1 << ESPC_SCALE))
 
@@ -192,15 +190,6 @@ static inline int serial_paranoia_check(struct esp_struct *info,
        return 0;
 }
 
-/*
- * This is used to figure out the divisor speeds
- */
-static int quot_table[] = {
-/*      0,    50,    75,  110,  134,  150,  200,  300, 600, 1200, */
-       0, 18432, 12288, 8378, 6878, 6144, 4608, 3072, 1536, 768,
-/*     1800,2400,4800,9600,19200,38400,57600,115200,230400,460800 */
-       512, 384, 192,  96,   48,   24,   16,     8,     4,     2, 0 };
-
 static inline unsigned int serial_in(struct esp_struct *info, int offset)
 {
        return inb(info->port + offset);
@@ -1096,7 +1085,7 @@ static void change_speed(struct esp_struct *info)
        unsigned short port;
        int     quot = 0;
        unsigned cflag,cval;
-       int     i, bits;
+       int     baud, bits;
        unsigned char flow1 = 0, flow2 = 0;
        unsigned long flags;
 
@@ -1104,27 +1093,7 @@ static void change_speed(struct esp_struct *info)
                return;
        cflag = info->tty->termios->c_cflag;
        port = info->port;
-       i = cflag & CBAUD;
-       if (i & CBAUDEX) {
-               i &= ~CBAUDEX;
-               if (i < 1 || i > 2) 
-                       info->tty->termios->c_cflag &= ~CBAUDEX;
-               else
-                       i += 15;
-       }
-       if (i == 15) {
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                       i += 1;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                       i += 2;
-                if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-                        i += 3;
-                if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-                        i += 4;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
-                       quot = info->custom_divisor;
-       }
-
+       
        /* byte size and parity */
        switch (cflag & CSIZE) {
              case CS5: cval = 0x00; bits = 7; break;
@@ -1148,14 +1117,20 @@ static void change_speed(struct esp_struct *info)
                cval |= UART_LCR_SPAR;
 #endif
 
-       if (!quot) {
-               quot = quot_table[i];
-
-               /* default to 9600 bps */
-               if (!quot)
-                       quot = BASE_BAUD / 9600;
-       }
-
+       baud = tty_get_baud_rate(info->tty);
+       if (baud == 38400)
+               quot = info->custom_divisor;
+       else {
+               if (baud == 134)
+                       /* Special case since 134 is really 134.5 */
+                       quot = (2*BASE_BAUD / 269);
+               else if (baud)
+                       quot = BASE_BAUD / baud;
+       }
+       /* If the quotient is ever zero, default to 9600 bps */
+       if (!quot)
+               quot = BASE_BAUD / 9600;
+       
        info->timeout = ((1024 * HZ * bits * quot) / BASE_BAUD) + (HZ / 50);
 
        /* CTS flow control flag and modem status interrupts */
@@ -1632,8 +1607,17 @@ check_and_exit:
                if (((old_info.flags & ASYNC_SPD_MASK) !=
                     (info->flags & ASYNC_SPD_MASK)) ||
                    (old_info.custom_divisor != info->custom_divisor) ||
-                   change_flow)
+                   change_flow) {
+                       if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                               info->tty->alt_speed = 57600;
+                       if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                               info->tty->alt_speed = 115200;
+                       if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                               info->tty->alt_speed = 230400;
+                       if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                               info->tty->alt_speed = 460800;
                        change_speed(info);
+               }
        } else
                retval = startup(info);
        return retval;
@@ -1723,30 +1707,27 @@ static int set_modem_info(struct esp_struct * info, unsigned int cmd,
 }
 
 /*
- * This routine sends a break character out the serial port.
+ * rs_break() --- routine which turns the break handling on or off
  */
-static void send_break(        struct esp_struct * info, int duration)
+static void esp_break(struct tty_struct *tty, int break_state)
 {
-       cli();
-       serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
-       serial_out(info, UART_ESI_CMD2, 0x01);
+       struct esp_struct * info = (struct esp_struct *)tty->driver_data;
+       unsigned long flags;
+       
+       if (serial_paranoia_check(info, tty->device, "esp_break"))
+               return;
 
-       interruptible_sleep_on(&info->break_wait);
+       save_flags(flags); cli();
+       if (break_state == -1) {
+               serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
+               serial_out(info, UART_ESI_CMD2, 0x01);
 
-       if (signal_pending(current)) {
+               interruptible_sleep_on(&info->break_wait);
+       } else {
                serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
                serial_out(info, UART_ESI_CMD2, 0x00);
-               sti();
-               return;
        }
-
-       current->state = TASK_INTERRUPTIBLE;
-       current->timeout = jiffies + duration;
-       schedule();
-
-       serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
-       serial_out(info, UART_ESI_CMD2, 0x00);
-       sti();
+       restore_flags(flags);
 }
 
 static int rs_ioctl(struct tty_struct *tty, struct file * file,
@@ -1754,7 +1735,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
 {
        int error;
        struct esp_struct * info = (struct esp_struct *)tty->driver_data;
-       int retval;
        struct async_icount cprev, cnow;        /* kernel counter temps */
        struct serial_icounter_struct *p_cuser; /* user space */
 
@@ -1770,41 +1750,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
        }
        
        switch (cmd) {
-               case TCSBRK:    /* SVID version: non-zero arg --> no break */
-                       retval = tty_check_change(tty);
-                       if (retval)
-                               return retval;
-                       tty_wait_until_sent(tty, 0);
-                       if (signal_pending(current))
-                               return -EINTR;
-                       if (!arg) {
-                               send_break(info, HZ/4); /* 1/4 second */
-                               if (signal_pending(current))
-                                       return -EINTR;
-                       }
-                       return 0;
-               case TCSBRKP:   /* support for POSIX tcsendbreak() */
-                       retval = tty_check_change(tty);
-                       if (retval)
-                               return retval;
-                       tty_wait_until_sent(tty, 0);
-                       if (signal_pending(current))
-                               return -EINTR;
-                       send_break(info, arg ? arg*(HZ/10) : HZ/4);
-                       if (signal_pending(current))
-                               return -EINTR;
-                       return 0;
-               case TIOCGSOFTCAR:
-                       return put_user(C_CLOCAL(tty) ? 1 : 0,
-                                   (int *) arg);
-               case TIOCSSOFTCAR:
-                       error = get_user(arg, (unsigned int *)arg);
-                       if (error)
-                               return error;
-                       tty->termios->c_cflag =
-                               ((tty->termios->c_cflag & ~CLOCAL) |
-                                (arg ? CLOCAL : 0));
-                       return 0;
                case TIOCMGET:
                        return get_modem_info(info, (unsigned int *) arg);
                case TIOCMBIS:
@@ -2527,6 +2472,7 @@ __initfunc(int espserial_init(void))
        esp_driver.stop = rs_stop;
        esp_driver.start = rs_start;
        esp_driver.hangup = esp_hangup;
+       esp_driver.break_ctl = esp_break;
        esp_driver.wait_until_sent = rs_wait_until_sent;
 
        /*
diff --git a/drivers/char/ftape/Config.in b/drivers/char/ftape/Config.in
new file mode 100644 (file)
index 0000000..858f327
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# Ftape configuration
+#
+dep_tristate 'Zftape, the VFS interface' CONFIG_ZFTAPE $CONFIG_FTAPE
+if [ "$CONFIG_ZFTAPE" != "n" ]; then
+  int 'Default block size' CONFIG_ZFT_DFLT_BLK_SZ 10240
+  comment 'The compressor will be built as a module only!'
+  define_bool CONFIG_ZFT_COMPRESSOR m
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  int 'Number of ftape buffers (EXPERIMENTAL)' CONFIG_FT_NR_BUFFERS 3
+fi
+if [ "$CONFIG_PROC_FS" = "y" ]; then
+  bool 'Enable procfs status report (+2kb)' CONFIG_FT_PROC_FS y
+fi
+choice 'Debugging output'                      \
+       "Normal         CONFIG_FT_NORMAL_DEBUG  \
+        Excessive      CONFIG_FT_FULL_DEBUG    \
+        Reduced        CONFIG_FT_NO_TRACE      \
+        None           CONIFG_FT_NO_TRACE_AT_ALL" Normal
+comment 'Hardware configuration'
+choice 'Floppy tape controllers'                               \
+       "Standard                       CONFIG_FT_STD_FDC       \
+        MACH-2                         CONFIG_FT_MACH2         \
+        FC-10/FC-20                    CONFIG_FT_PROBE_FC10    \
+        Alt/82078                      CONFIG_FT_ALT_FDC" Standard
+if [ "$CONFIG_FT_STD_FDC" != "y" ]; then
+  comment '   Consult the manuals of your tape drive for the correct settings!'
+  hex ' IO base of the floppy disk controller' CONFIG_FT_FDC_BASE 0
+  int '   IRQ channel of the floppy disk controller' CONFIG_FT_FDC_IRQ 0
+  int '   DMA channel of the floppy disk controller' CONFIG_FT_FDC_DMA 0
+fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   int 'Default FIFO threshold (EXPERIMENTAL)' CONFIG_FT_FDC_THR 8
+   int 'Maximal data rate to use (EXPERIMENTAL)' CONFIG_FT_FDC_MAX_RATE 2000
+fi
+comment 'ONLY for DEC Alpha architectures'
+int 'CPU clock frequency of your DEC Alpha' CONFIG_FT_ALPHA_CLOCK 0
index 1da53beb1576b33df18d23760ee4f724c2c60c52..5726e788ede18bce8dfcb1ccfd789752b14db1cb 100644 (file)
@@ -1,65 +1,67 @@
 #
-# Makefile for the ftape device driver.
+#       Copyright (C) 1997 Claus Heine.
 #
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING.  If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 #
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes..
+# $Source: /homes/cvs/ftape-stacked/ftape/Makefile,v $
+# $Revision: 1.4 $
+# $Date: 1997/10/05 19:17:56 $
 #
-
-#  Valid ftape options are:
-#       NO_TRACE        - if defined, only information and errors show up.
-#       NO_TRACE_AT_ALL - if defined, no trace output shows up.
-#       GCC_2_4_5_BUG   - must be set if using gcc-2.4.5 to prevent
-#                         bad assembler-code for the dma handling.
-#      NR_BUFFERS      - Number of ftape DMA buffers (keep it at 3!)
-#       VERIFY_HEADERS  - if set the headers segments are verified after
-#                         being written.
-#       PROBE_FC10      - if defined will look for a FC-10 card at specified
-#                         settings (FDC_BASE,FDC_IRQ,FDC_DMA) before using
-#                         the standard fd controller.
-#       FDC_BASE        - sets base address (only!) if using non-standard fdc
-#       FDC_IRQ         - sets interrupt if FDC_BASE is defined
-#       FDC_DMA         - sets dma channel if FDC_BASE is defined
-#       MACH2           - Support for Mountain MACH-2 controller at either 1E0
-#                         or 3E0, don't forget the FDC_OPT's !
-#      CLK_48MHZ       - Set to 1. If you have a i82078-1 FDC and it does not
-#                        work, try setting it to 0. (only used for i82078-1's)
-#      FDC_82078SL     - If you have a 82078SL, define this.
-
-FTAPE_OPT       = -DVERIFY_HEADERS -DNR_BUFFERS=3 -DCLK_48MHZ=1 \
-       -DNO_TRACE -DFDC_82078SL
-
-#  If you're using a non-standard floppy disk controller for the
-#  tape drive, enable one (only!) of the following lines and set
-#  the FDC_BASE, FDC_IRQ and FDC_DMA parameters to the actual values.
-#
-#  Note1: A FC-10/FC-20 controller must use either of DMA 1, 2, or 3.
-#         DMA 5 and 7 does NOT work!.
-#
-#  Note2: IRQ 2 and IRQ 9 can be considered the same. When using IRQ 2
-#         on a controller you must specify IRQ 9 here!
+#      Makefile for the QIC-40/80/3010/3020 floppy-tape driver for
+#      Linux.
 #
-# For a Mountain MACH-2 controller, try 
-#FDC_OPT         = -DMACH2 -DFDC_BASE=0x1E0 -DFDC_IRQ=6 -DFDC_DMA=2
-#
-# For Colorado CMS FC-10 or FC-20 controllers:
-#FDC_OPT         = -DPROBE_FC10 -DFDC_BASE=0x180 -DFDC_IRQ=9 -DFDC_DMA=3
+
 #
-# Secondary floppy disk controller:
-#FDC_OPT         = -DFDC_BASE=0x370 -DFDC_IRQ=9 -DFDC_DMA=3
+#  This isn't used inside the kernel, only for my private development
+#  version
 #
-# This enables some (most?) 2Mbps controllers:
-#FDC_OPT         = -DFDC_BASE=0x3E0 -DFDC_IRQ=6 -DFDC_DMA=2
+ifndef TOPDIR
+TOPDIR= ..
+include $(TOPDIR)/MCONFIG
+endif
+
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS) lowlevel zftape compressor
+
+ifeq ($(CONFIG_FTAPE),y)
+  O_TARGET := ftape.o
+  SUB_DIRS += lowlevel
+  O_OBJS   += lowlevel/ftape.o
+else
+  ifeq ($(CONFIG_FTAPE),m)
+  MOD_SUB_DIRS += lowlevel
+  endif
+endif
 
-EXTRA_CFLAGS := $(FTAPE_OPT) $(FDC_OPT)
+ifeq ($(CONFIG_ZFTAPE),y)
+  SUB_DIRS += zftape
+  O_OBJS   += zftape/zftape.o
+else
+  ifeq ($(CONFIG_ZFTAPE),m)
+  MOD_SUB_DIRS += zftape
+  endif
+endif
 
-O_TARGET := ftape.o
-O_OBJS   = kernel-interface.o tracing.o fdc-io.o fdc-isr.o \
-          ftape-bsm.o ftape-ctl.o ftape-eof.o ftape-read.o ftape-rw.o \
-          ftape-write.o ftape-io.o calibr.o ecc.o fc-10.o
-M_OBJS   = $(O_TARGET)
+ifeq ($(CONFIG_ZFT_COMPRESSOR),y)
+  SUB_DIRS += compressor
+  O_OBJS   += compressor/zft-compressor.o
+else
+  ifeq ($(CONFIG_ZFT_COMPRESSOR),m)
+  MOD_SUB_DIRS += compressor
+  endif
+endif
 
 include $(TOPDIR)/Rules.make
index 05842f071c5830ce13edde0afdbab75bd904eb64..18de159d36e059efbb958a772139fef5dfd697e9 100644 (file)
@@ -30,7 +30,7 @@ bus timing.
 I judge this a hardware problem and not a limitation of ftape ;-)
 My DOS backup software seems to be suffering from the same problems
 and even refuses to run at 1 Mbps !
-Ftape will reduce the datarate from 1 Mbps to 500 Kbps if the number
+Ftape will reduce the data-rate from 1 Mbps to 500 Kbps if the number
 of overrun errors on a track exceeds a threshold.
 
 
@@ -77,3 +77,5 @@ under DOS. If it's very slow and often repositions you're
 probably having this problem.
 
                         --//--
+ LocalWords:  ftape PCI bios GAT ISA DMA chipset Mbps Kbps FDC isa AF ok ASUS
+ LocalWords:  SP linebuffer masterbuffer XPS http www com
index fc82f99009049c3e2f0e1a8c8fbcdf321e1a9040..dcc2508080c8e7a86e89d037fbc87e7016e442d2 100644 (file)
@@ -1,3 +1,392 @@
+Hey, Emacs, we're -*-Text-*- mode!
+
+===== Release notes for ftape-3.04d 25/11/97 =====
+- The correct pre-processor statement for "else if" is "#elif" not
+  "elsif".
+- Need to call zft_reset_position() when overwriting cartridges
+  previously written with ftape-2.x, sftape, or ancient
+  (pre-ftape-3.x) versions of zftape.
+
+===== Release notes for ftape-3.04c 16/11/97 =====
+- fdc_probe() was calling DUMPREGS with a result length of "1" which
+  was just fine. Undo previous change.
+
+===== Release notes for ftape-3.04b 14/11/97 =====
+
+- patches/2.x.x/floppy.c.diff was somewhat broken, releasing i/o
+  regions it never had allocated.
+- fdc_probe() was calling DUMPREGS with a result length of "1" instead
+  of "10"
+- Writing deleted data marks if the first segents on track zero are
+  should work now.
+- ftformat should now be able to handle those cases where the tape
+  drive sets the read only status bit (QIC-40/80 cartridges with
+  QIC-3010/3020 tape drives) because the header segment is damaged.
+- the MTIOCFTCMD ioctl may now be issued by the superuser ONLY.
+
+===== Release notes for ftape-3.04a 12/11/97 =====
+- Fix an "infinite loop can't be killed by signal" bug in
+  ftape_get_drive_status(). Only relevant when trying to access
+  buggy/misconfigured hardware
+- Try to compensate a bug in the HP Colorado T3000's firmware: it
+  doesn't set the write protect bit for QIC80/QIC40 cartridges.
+
+===== Release notes for ftape-3.04 06/11/97 =====
+- If positioning with fast seeking fails fall back to a slow seek
+  before giving up.
+- (nearly) no retries on "no data errors" when verifying after
+  formatting. Improved tuning of the bad sector map after formatting.
+- the directory layout has changed again to allow for easier kernel
+  integration
+- Module parameter "ftape_tracing" now is called "ft_tracing" because
+  the "ftape_tracing" variable has the version checksum attached to it.
+- `/proc/ftape' interface for 2.0.* kernels. `/proc/ftape' no longer
+  is a directory but a file that contains all the information formerly
+  provided in separate files under the `/proc/ftape/' directory.
+- Most of the configuration options have been prefixed by "CONFIG_FT_"
+  in preparation of the kernel inclusion. The Makefiles under
+  "./ftape/" should be directly usable by the kernel.
+- The MODVERSIONS stuff is now auto-detected.
+- Broke backslashed multi line options in MCONFIG into separate lines
+  using GNU-make's "+=" feature.
+- The html and dvi version of the manual is now installed under
+  '/usr/doc/ftape` with 'make install`
+- New SMP define in MCONFIG. ftape works with SMP if this is defined.
+- attempt to cope with "excessive overrun errors" by gradually
+  increasing FDC FIFO threshold. But this doesn't seem to have too
+  much an effect.
+- New load time configuration parameter "ft_fdc_rate_limit". If you
+  encounter too many overrun errors with a 2Mb controller then you
+  might want to set this to 1000.
+- overrun errors on the last sector in a segment sometimes result in
+  a zero DMA residue. Dunno why, but compensate for it.
+- there were still fdc_read() timeout errors. I think I have fixed it
+  now, please FIXME.
+- Sometimes ftape_write() failed to re-start the tape drive when a
+  segment without a good sector was reached ("wait for empty segment
+  failed"). This is fixed. Especially important for > QIC-3010.
+- sftape (aka ftape-2.x) has vanished. I didn't work on it for
+  ages. It is probably still possible to use the old code with
+  ftape-3.04, if one really needs it (BUT RECOMPILE IT)
+- zftape no longer alters the contents of already existing volume
+  table entries, which makes it possible to fill in missing fields,
+  like time stamps using some user space program.
+- ./contrib/vtblc/ contains such a program.
+- new perl script ./contrib/scripts/listtape that list the contents of a
+  floppy tape cartridge parsing the output of "mt volinfo" + "mt fsf"
+- the MTWEOF implementation has changed a little bit (after I had a
+  look at amanda). Calling MTWEOF while the tape is still held open
+  after writing something to the tape now will terminate the current
+  volume, and start a new one at the current position.
+- the volume table maintained by zftape now is a doubly linked list
+  that grows dynamically as needed.
+
+  formatting floppy tape cartridges
+  ---------------------------------
+  * there is a new user space formatting program that does most of the
+    dirty work in user space (auto-detect, computing the sector
+    coordinates, adjusting time stamps and statistics). It has a
+    simple command line interface.
+  * ftape-format.o has vanished, it has been folded into the low level
+    ftape.o module, and the ioctl interface into zftape.o. Most of the
+    complicated stuff has been moved to user space, so there was no
+    need for a separate module anymore.
+  * there is a new ioctl MTIOCFTCMD that sends a bare QIC-117 command
+    to the tape drive.
+  * there is a new mmap() feature to map the dma buffers into user
+    space to be used by the user level formatting program.
+  * Formatting of yet unformatted or totally degaussed cartridges
+    should be possible now. FIXME.
+
+===== Release notes for ftape-3.03b, <forgot the exact date> ====
+
+ftape-3.03b was released as a beta release only. Its main new feature
+was support of the DITTO-2GB drive. This was made possible by reverse
+engineering done by <fill in his name> after Iomega failed to support
+ftape. Although they had promised to do so (this makes me feel a bit
+sad and uncomfortable about Iomega).
+
+===== Release notes for ftape-3.03a, 22/05/97 ====
+
+- Finally fixed auto-un-loading of modules for kernels > 2.1.18
+- Add an "uninstall" target to the Makefile
+- removed the kdtime hack
+- texi2www didn't properly set the back-reference from a footnote back
+  to the regular text.
+
+  zftape specific
+  ---------------
+  * hide the old compression map volume. Taper doesn't accept the
+    presence of non-Taper volumes and Taper-written volume on the same
+    tape.
+  * EOD (End Of Data) handling was still broken: the expected behavior
+    is to return a zero byte count at the first attempt to read past
+    EOD, return a zero byte count at the second attempt to read past
+    EOD and THEN return -EIO.
+  
+  ftape-format specific
+  ---------------------
+  * Detection of QIC-40 cartridges in select_tape_format() was broken
+    and made it impossible to format QIC-3010/3020 cartridges.
+  * There are strange "TR-1 Extra" cartridges out there which weren't
+    detected properly because the don't strictly conform to the
+    QIC-80, Rev. N, spec.
+
+===== Release notes for ftape-3.03, 30/04/97 =====
+
+- Removed kernel integration code from the package. I plan to provide
+  a package that can be integrated into the stock kernel separately
+  (hopefully soon).
+  As a result, a simple `make' command now will build everything.
+- ALL compile time configuration options have been moved to the file
+  `MCONFIG'.
+- Quite a few `low level' changes to allow formatting of cartridges.
+- formatting is implemented as a separate module `ftape-format.o'. The
+  modified `mt' program contains sample code that shows how to use it.
+- The VFS interface has been moved from the `ftape.o' module to the
+  high level modules `zftape.o' resp. `sftape.o'. `ftape.o' contains
+  the hardware support only.
+- A bit of /proc support for kernels > 2.1.28
+- Moved documentation to Doc subdir. INSTALL now contains some real
+  installation notes.
+- `install' target in Makefile.
+
+zftape specific:
+----------------
+
+- zftape works for large cartridges now ( > 2^31 bytes)
+- MTIOCVOLINFO and MTIOCGETSIZE now return the size in KILOBYTES,
+  NO LONGER in bytes.
+
+- permissions for write access to a cartridge have changed:
+  * zftape now also takes the file access mode into account
+  * zftape no longer allows writing in the middle of the recorded
+    media. The tape has to be positioned at BOT or EOD for write
+    access.
+
+- MTBSF has changed. It used to position at the beginning of the
+  previous file when called with count 1. This was different from the
+  expected behavior for other Un*x tape drivers (i.e. SCSI). MTBSF
+  with count 1 should merely position at the beginning of the current
+  volume. Fixed. As a result, `tar --verify' now produces the desired
+  result: it verifies the last written volume, not the pre-last
+  written volume.
+
+- The compression map has vanished --> no need for `mt erase' any
+  more. Fast seeking in a compressed volume is still be possible, but
+  takes slightly longer. As a side effect, you may experience an
+  additional volume showing up in front of all others for old
+  cartridges. This is the tape volume that holds the compression map.
+
+- The compression support for zftape has been moved to a separate
+  module `zft-compressor'. DON'T forget to load it before trying to
+  read back compressed volumes. The stock `zftape.o' module probes for
+  the module `zft-compressor' using the kerneld message channel; you
+  have to install `zft-compressor.o' in a place where modprobe can
+  find it if you want to use this.
+
+- New experimental feature that tries to get the broken down GMT time
+  from user space via a kernel daemon message channel. You need to
+  compile and start the `kdtime' daemon contained in the contrib
+  directory to use it. Needed (?) for time stamps in the header
+  segments and the volume table.
+
+- variable block size mode via MTSETBLK 0
+
+- keep modules locked in memory after the block size has been changed
+
+sftape specific:
+----------------
+
+- end of tape handling should be fixed, i.e. multi volume archives
+  written with `afio' can be read back now.
+
+
+===== Release notes for ftape-3.02a, 09/01/97 =====
+
+No big news:
+- call zft_init() resp. sft_init() when compiling the entire stuff
+  into the kernel image.
+- fix bug in ftape-setup.c when NO_TRACE_AT_ALL was defined.
+- fix bug in sftape-eof.c/zftape-eof.c for old kernels (1.2.*)
+- add support for new module interface for recent kernels
+
+===== Release notes for ftape-3.02, 16/12/96 =====
+- Fixed the `FDC unlock command failed' bug in fdc-io.c. When the FIFO
+  was already locked when ftape was loaded, ftape failed to unlock it.
+- Fixed compilation of `contrib/gnumt'. It now finds `mtio.h' even if
+  ftape is NOT included into the kernel source tree.
+- fc-10.c: include <asm/io.h> for inb() and outb().
+- ftape/sftape/zftape: all global variable now have either a `ftape_',
+  a `ft_', `sft_', `zft_' or `qic_' prefix to prevent name clashes
+  with other parts of the kernel when including ftape into the kernel
+  source tree.
+- Kerneld support has changed. `ftape' now searches for a module
+  `ftape-frontend' when none of the frontend (`sftape' or `zftape') is
+  loaded. Please refer to the `Installation/Loading ftape' section of
+  the TeXinfo manual.
+- Add load resp. boot-time configuration of ftape. There are now
+  variables ft_fdc_base, ft_fdc_dma and ft_fdc_irq corresponding to
+  the former FDC_BASE etc. compile time definitions. One can also use
+  the kernel command line parameters to configure the driver if it is
+  compiled into the kernel. Also, the FC-10/FC-20 support is load-time
+  configurable now as well as the MACH-II hack (ft_probe_fc10,
+  resp. ft_mach2). Please refer to the section `Installation/Configure
+  ftape' of the TeXinfo manual.
+- I removed the MODVERSIONS option from `Makefile.module'. Let me alone
+  with ftape and MODVERSIONS unless you include the ftape sources into
+  the kernel source tree.
+- new vendors in `vendors.h':
+  * HP Colorado T3000 
+  * ComByte DoublePlay (including a bug fix for their broken
+    formatting software, thanks to whraven@njackn.com)
+  * Iomega DITTO 2GIG. NOTE: this drive cannot work with ftape because
+    the logical data layout of the cartridges used by this drive does
+    NOT conform to the QIC standards, it is a special Iomega specific
+    format. I've sent mail to Iomega but didn't receive an answer
+    yet. If you want this drive to be supported by ftape, ask Iomega
+    to give me information about it.
+- zftape:
+  * re-introduced the MTIOC_ZFTAPE_GETBLKSZ ioctl for compatibility
+    with zftape 1.06a and earlier. Please don't use it when writing
+    new software, use the MTIOCVOLINFO ioctl instead.
+  * Major overhaul of the code that updates the header segments. Never
+    change the tape label unless erasing the tape. Thus we almost
+    never need to write the header segments, unless we would modify
+    the bad sector map which isn't done yet. Updating of volume table
+    and compression map more secure now although it takes a bit
+    longer.
+  * Fixed bug when aborting a write operation with a signal: zftape
+    now finishes the current volume (i.e. writes an eof marker) at the
+    current position. It didn't before which led to somehow *strange*
+    behavior in this cases.
+  * Keep module locked in memory when using it with  the non-rewinding
+    devices and the tape is not logical at BOT. Needed for kerneld
+    support.
+- sftape:
+  * Keep module locked in memory when using it with  the non-rewinding
+    devices and the tape is not logical at BOT. Needed for kerneld
+    support.
+
+===== Release notes for ftape-3.01, 14/11/96 =====
+
+- Fixed silly bugs in ftape-3.00:
+  * MAKEDEV.ftape: major device number must be 27, not 23 
+  * sftape/sftape-read.c: sftape_read_header_segments() called 
+    itself recursively instead of calling ftape_read_header_segment()
+  * zftape/qic-vtbl.h: conversion of ftape's file marks to zftape's
+    internal volume table was broken.
+  * patches/2.x.x/linux-2.0.21.dif: my RCS (resp. CVS) system replaced
+    the `$Revison:' etc. macros in the `ftape.h' concerning part of the
+    patch :-( Fixed.
+  * info/ftape.info: Fixed misspellings (`cp' <-> `cp -r' etc.)
+  * when ftape/sftape or ftape/zftape was compiled into the kernel the
+    variable ftape_status was declared twice. Fixed.
+  * removed reference to undeclared variable kernel_version when not
+    compiling as module
+  * fixed a bug introduced by the use of bit-fields for some flags
+    (i.e. write_protected, no_cartridge, formatted)
+  * flag `header_read' is now reset correctly to zero when tape is
+    removed.
+- fixed a bug in sftape/sftape-eof.c that was already in the original
+  ftape code. MTFSF/BSF was not handled correctly when positioned
+  right before the file mark (think of tar)
+- Changed TRACE macros (following a suggestion of Marcin Dalecki) to use
+  the predefined __FUNCTION__ macro of GCC. Spares about 4k of code.
+- added new vendor id for Iomega DITTO 2GIG
+- fixed a bug already present in zftape-1.06 when aborting a write
+  with a signal: we now finish the current volume at that
+  position. Header segments remain NOT up to date until an explicit call
+  to MTREW or MTOFFL is done.  
+
+===== Release notes for ftape-3.00, 14/10/96 =====
+
+- Merged ftape with zftape. There are three modules now:
+  ftape for the hardware support, sftape for the implementation of the
+  original ftape eof mark stuff and zftape that implements zftape's way
+  of handling things (compression, volume table, tape blocks of
+  constant length)
+- Documentation in TeXinfo format in the `info' subdirectory.
+- New ioctls for zftape. See zftape/zftape.h
+- Dummy formatting ioctl for ftape. See ftape.h
+- Kernel patch files for the 2.*.* series to include ftape-3.00 in the
+  kernel source tree. These includes a kernel compatible Config.in
+  script and fairly large online information for the kernel configure
+  script.
+- Support for compiling with Linux-1.2.13. 
+- Modified GNU mt from their cpio package that can handle the new
+  ioctls.
+- ftape/sftape/zftape is kerneld save now!
+
+Notes on sftape:
+- sftape implements the eof handling code of the original ftape. If
+  you like to stick with the original ftape stuff, you have to use
+  this module, not zftape.
+- sftape is kerneld save, unlike the original ftape.
+- we keep the entire header segment now in memory, so no need to read
+  it before updating the header segments. Additional memory
+  consumption: 256 bytes. 
+
+Notes for zftape:
+- zftape has support for tapes with format code 6 now, which use a
+  slightly different volume table format compared with other floppy
+  tapes.
+- new ioctls for zftape. Have a look at zftape/zftape.h
+- The internal volume table representation has changed for zftape. Old
+  cartridges are converted automatically.
+- zftape no longer uses compression map segments, which have vanished
+  from the QIC specs, but creates volume table entry that reserves
+  enough space for the compression map. 
+- zftape is kerneld save now.
+- we keep the entire header segment now in memory, so no need to read
+  it before updating the header segments. Additional memory
+  consumption: 256 bytes. 
+
+Notes for contrib/gnumt:
+- modified mt from the GNU cpio package that supports all the new
+  ioctls of zftape.
+Notes for contrib/swapout:
+- This contains the swapout.c program that was written by Kai
+  Harrekilde-Pederson. I simply added a Makefile.
+
+===== Release notes for ftape-2.10, 14/10/96 =====
+
+The ftape maintainer has changed. 
+Kai Harrekilde-Petersen <khp@dolphinics.no>
+has resigned from maintaining ftape, and I,
+Claus-Justus Heine <claus@momo.math.rwth-aachen.de>,
+have taken over.
+
+- Added support for tapes with `format code 6', i.e. QIC-3020 tapes
+  with more than 2^16 segments.
+- merged changes made by Bas Laarhoven with ftape-2.09. Refer
+  to his release notes below. I've included them into this
+  file unchanged for your reference.
+- disabled call stack back trace for now. This new feature
+  introduced by the interim release 2.0.x still seems to
+  be buggy.
+- Tried to minimize differences between the ftape version
+  to be included into the kernel source tree and the standalone
+  module version.
+- Reintroduced support for Linux-1.2.13. Please refer to the
+  Install-guide. 
+
+===== Release notes for ftape-2.09, 16/06/96 =====
+
+There aren't any really big news in this release, mostly just that I
+(the maintainer) have changed my email address (due to a new job).  My
+new address is <khp@dolphinics.no>
+
+- The CLK_48MHZ and FDC_82078SL options has gone (all 2Mbps cards seem
+  to use a 48MHz oscillator anyway and I haven't heard of an 'SL
+  chip out there).
+- The S82078B has been `downgraded' to i82077AA compability.
+- TESTING option revived.  Right now, it'll enable the (seriously broken)
+  2Mbps code.  If you enable it, you'll experience a tape drive that's
+  *really* out to lunch!
+- Some (bold) changes in the init code.  Please notify me if they
+  break things for you.
+
 ===== Release notes for ftape-2.08, 14/03/96 =====
 
 If you correct a problem with ftape, please send your patch to
@@ -567,3 +956,13 @@ Have fun,
 
 Bas.
 ----
+ LocalWords:  ftape MCONFIG mt VFS zftape resp sftape proc subdir MTIOCVOLINFO
+ LocalWords:  MTIOCGETSIZE BOT EOD MTBSF zft kerneld modprobe kdtime contrib TR
+ LocalWords:  MTSETBLK afio uninstall texi www EIO QIC init sft eof aka dma GB
+ LocalWords:  SIGKILL MTIOCFTCMD mmap Iomega FDC fdc io gnumt mtio fc asm inb
+ LocalWords:  outb ft qic frontend TeXinfo irq mach MODVERSIONS CONFIG html dvi
+ LocalWords:  usr doc SMP Mb Dunno FIXME vtblc perl listtape volinfo fsf MTWEOF
+ LocalWords:  amanda degaussed ComByte DoublePlay whraven njackn com MTIOC vtbl
+ LocalWords:  GETBLKSZ MAKEDEV zftape's linux dif CVS Revison cp MTREW MTOFFL
+ LocalWords:  MTFSF BSF Marcin Dalecki GCC Config cpio swapout Kai Harrekilde
+ LocalWords:  Pederson khp dolphinics Justus claus momo rwth aachen Laarhoven
diff --git a/drivers/char/ftape/calibr.c b/drivers/char/ftape/calibr.c
deleted file mode 100644 (file)
index bd41bdd..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/* Yo, Emacs! we're -*- Linux-C -*-
- *
- *      Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *      GP calibration routine for processor speed dependent
- *      functions.
- */
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/ftape.h>
-#include <asm/system.h>
-#include <asm/io.h>
-
-#include "tracing.h"
-#include "calibr.h"
-#include "fdc-io.h"
-
-#undef DEBUG
-
-unsigned timestamp(void)
-{
-       unsigned count;
-       unsigned long flags;
-
-       save_flags(flags);
-       cli();
-       outb_p(0x00, 0x43);     /* latch the count ASAP */
-       count = inb_p(0x40);    /* read the latched count */
-       count |= inb(0x40) << 8;
-       restore_flags(flags);
-       return (LATCH - count); /* normal: downcounter */
-}
-
-int timediff(int t0, int t1)
-{
-       /*  Calculate difference in usec for timestamp results t0 & t1.
-        *  Note that the maximum timespan allowed is 1/HZ or we'll lose ticks!
-        */
-       if (t1 < t0) {
-               t1 += LATCH;
-       }
-       return (1000 * (t1 - t0)) / ((CLOCK_TICK_RATE + 500) / 1000);
-}
-
-/*      To get an indication of the I/O performance,
- *      measure the duration of the inb() function.
- */
-void time_inb(void)
-{
-       TRACE_FUN(8, "time_inb");
-       int i;
-       int t0, t1;
-       unsigned long flags;
-       int status;
-
-       save_flags(flags);
-       cli();
-       t0 = timestamp();
-       for (i = 0; i < 1000; ++i) {
-               status = inb(fdc.msr);
-       }
-       t1 = timestamp();
-       restore_flags(flags);
-       if (t1 - t0 <= 0) {
-               t1 += LATCH;
-       }
-       TRACEx1(4, "inb() duration: %d nsec", timediff(t0, t1));
-       TRACE_EXIT;
-}
-
-/*  Haven't studied on why, but there sometimes is a problem
- *  with the tick timer readout. The two bytes get swapped.
- *  This hack solves that problem by doing one extra input.
- */
-void fix_clock(void)
-{
-       TRACE_FUN(8, "fix_clock");
-       int t;
-       int i;
-
-       for (i = 0; i < 1000; ++i) {
-               t = timestamp();
-               if (t < 0) {
-                       inb_p(0x40);    /* get in sync again */
-                       TRACE(2, "clock counter fixed");
-                       break;
-               }
-       }
-       TRACE_EXIT;
-}
-
-/*
- *      Input:  function taking int count as parameter.
- *              pointers to calculated calibration variables.
- */
-int calibrate(char *name, void (*fun) (int), int *calibr_count, int *calibr_time)
-{
-       TRACE_FUN(5, "calibrate");
-       static int first_time = 1;
-       int i;
-       int old_tc = 0;
-       int old_count = 1;
-       int old_time = 1;
-
-       if (first_time) {       /* get idea of I/O performance */
-               fix_clock();
-               time_inb();
-               first_time = 0;
-       }
-       /*    value of timeout must be set so that on very slow systems
-        *    it will give a time less than one jiffy, and on
-        *    very fast systems it'll give reasonable precision.
-        */
-
-       *calibr_count = 10;
-       for (i = 0; i < 15; ++i) {
-               int t0, t1;
-               unsigned long flags;
-               int once;
-               int multiple;
-               int tc;
-
-               *calibr_time = *calibr_count;   /* set TC to 1 */
-               fun(0);         /* dummy, get code into cache */
-               save_flags(flags);
-               cli();
-               t0 = timestamp();
-               fun(0);         /* overhead + one test */
-               t1 = timestamp();
-               if (t1 < t0) {
-                       t1 += LATCH;
-               }
-               once = t1 - t0;
-               t0 = timestamp();
-               fun(*calibr_count);     /* overhead + multiple tests */
-               t1 = timestamp();
-               if (t1 < t0) {
-                       t1 += LATCH;
-               }
-               multiple = t1 - t0;
-               restore_flags(flags);
-               *calibr_time = (10000 * (multiple - once)) / (CLOCK_TICK_RATE / 100);
-               --*calibr_count;        /* because delta corresponds to this count */
-               tc = (1000 * *calibr_time) / *calibr_count;
-               TRACEx4(8, "once:%4d us,%5d times:%6d us, TC:%5d ns",
-                       (10000 * once) / (CLOCK_TICK_RATE / 100),
-                       *calibr_count,
-                       (10000 * multiple) / (CLOCK_TICK_RATE / 100),
-                       tc);
-               /*
-                * increase the count until the resulting time nears 2/HZ,
-                * then the tc will drop sharply because we lose LATCH counts.
-                */
-               if (tc <= old_tc / 2) {
-                       *calibr_time = old_time;
-                       *calibr_count = old_count;
-                       break;
-               }
-               old_tc = tc;
-               old_count = *calibr_count;
-               old_time = *calibr_time;
-               *calibr_count *= 2;
-       }
-       TRACEx3(4, "TC for `%s()' = %d nsec (at %d counts)",
-            name, (1000 * *calibr_time) / *calibr_count, *calibr_count);
-       TRACE_EXIT;
-       return 0;
-}
diff --git a/drivers/char/ftape/calibr.h b/drivers/char/ftape/calibr.h
deleted file mode 100644 (file)
index 75bebe7..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef _CALIBRATE_H
-#define _CALIBRATE_H
-
-/*
- *      Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/calibr.h,v $
- $Author: bas $
- *
- $Revision: 1.20 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- *      This file contains a gp calibration routine for
- *      hardware dependent timeout functions.
- */
-
-#include <linux/timex.h>
-
-extern int calibrate(char *name, void (*fun) (int), int *calibr_count, int *calibr_time);
-extern unsigned timestamp(void);
-extern int timediff(int t0, int t1);
-
-#endif
diff --git a/drivers/char/ftape/compressor/Makefile b/drivers/char/ftape/compressor/Makefile
new file mode 100644 (file)
index 0000000..fde3fd1
--- /dev/null
@@ -0,0 +1,48 @@
+#
+#       Copyright (C) 1997 Claus-Justus Heine.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING.  If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Source: /homes/cvs/ftape-stacked/ftape/compressor/Makefile,v $
+# $Revision: 1.1 $
+# $Date: 1997/10/05 19:12:28 $
+#
+#      Makefile for the optional compressor for th zftape VFS
+#      interface to the QIC-40/80/3010/3020 floppy-tape driver for
+#      Linux.
+#
+
+#
+#  This isn't used inside the kernel, only for my private development
+#  version
+#
+ifndef TOPDIR
+TOPDIR=../..
+include $(TOPDIR)/MCONFIG
+endif
+
+O_TARGET := zft-compressor.o
+O_OBJS    = zftape-compress.o lzrw3.o
+
+M_OBJS    = $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
+#
+# sorry, a special rule. 
+#
+lzrw3.o: lzrw3.c
+       $(CC) $(CFLAGS) -O6 -funroll-all-loops -c $<
+
diff --git a/drivers/char/ftape/compressor/lzrw3.c b/drivers/char/ftape/compressor/lzrw3.c
new file mode 100644 (file)
index 0000000..64ad325
--- /dev/null
@@ -0,0 +1,750 @@
+/*
+ * $Source: /homes/cvs/ftape-stacked/ftape/compressor/lzrw3.c,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/10/05 19:12:29 $
+ *
+ * Implementation of Ross Williams lzrw3 algorithm. Adaption for zftape.
+ *
+ */
+
+#include "../compressor/lzrw3.h"       /* Defines single exported function "compress".   */
+
+/******************************************************************************/
+/*                                                                            */
+/*                                    LZRW3.C                                 */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/* Author  : Ross Williams.                                                   */
+/* Date    : 30-Jun-1991.                                                     */
+/* Release : 1.                                                               */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/* This file contains an implementation of the LZRW3 data compression         */
+/* algorithm in C.                                                            */
+/*                                                                            */
+/* The algorithm is a general purpose compression algorithm that runs fast    */
+/* and gives reasonable compression. The algorithm is a member of the Lempel  */
+/* Ziv family of algorithms and bases its compression on the presence in the  */
+/* data of repeated substrings.                                               */
+/*                                                                            */
+/* This algorithm is unpatented and the code is public domain. As the         */
+/* algorithm is based on the LZ77 class of algorithms, it is unlikely to be   */
+/* the subject of a patent challenge.                                         */
+/*                                                                            */
+/* Unlike the LZRW1 and LZRW1-A algorithms, the LZRW3 algorithm is            */
+/* deterministic and is guaranteed to yield the same compressed               */
+/* representation for a given file each time it is run.                       */
+/*                                                                            */
+/* The LZRW3 algorithm was originally designed and implemented                */
+/* by Ross Williams on 31-Dec-1990.                                           */
+/*                                                                            */
+/* Here are the results of applying this code, compiled under THINK C 4.0     */
+/* and running on a Mac-SE (8MHz 68000), to the standard calgary corpus.      */
+/*                                                                            */
+/*    +----------------------------------------------------------------+      */
+/*    | DATA COMPRESSION TEST                                          |      */
+/*    | =====================                                          |      */
+/*    | Time of run     : Sun 30-Jun-1991 09:31PM                      |      */
+/*    | Timing accuracy : One part in 100                              |      */
+/*    | Context length  : 262144 bytes (= 256.0000K)                   |      */
+/*    | Test suite      : Calgary Corpus Suite                         |      */
+/*    | Files in suite  : 14                                           |      */
+/*    | Algorithm       : LZRW3                                        |      */
+/*    | Note: All averages are calculated from the un-rounded values.  |      */
+/*    +----------------------------------------------------------------+      */
+/*    | File Name   Length  CxB  ComLen  %Remn  Bits  Com K/s  Dec K/s |      */
+/*    | ----------  ------  ---  ------  -----  ----  -------  ------- |      */
+/*    | rpus:Bib.D  111261    1   55033   49.5  3.96    19.46    32.27 |      */
+/*    | us:Book1.D  768771    3  467962   60.9  4.87    17.03    31.07 |      */
+/*    | us:Book2.D  610856    3  317102   51.9  4.15    19.39    34.15 |      */
+/*    | rpus:Geo.D  102400    1   82424   80.5  6.44    11.65    18.18 |      */
+/*    | pus:News.D  377109    2  205670   54.5  4.36    17.14    27.47 |      */
+/*    | pus:Obj1.D   21504    1   13027   60.6  4.85    13.40    18.95 |      */
+/*    | pus:Obj2.D  246814    1  116286   47.1  3.77    19.31    30.10 |      */
+/*    | s:Paper1.D   53161    1   27522   51.8  4.14    18.60    31.15 |      */
+/*    | s:Paper2.D   82199    1   45160   54.9  4.40    18.45    32.84 |      */
+/*    | rpus:Pic.D  513216    2  122388   23.8  1.91    35.29    51.05 |      */
+/*    | us:Progc.D   39611    1   19669   49.7  3.97    18.87    30.64 |      */
+/*    | us:Progl.D   71646    1   28247   39.4  3.15    24.34    40.66 |      */
+/*    | us:Progp.D   49379    1   19377   39.2  3.14    23.91    39.23 |      */
+/*    | us:Trans.D   93695    1   33481   35.7  2.86    25.48    40.37 |      */
+/*    +----------------------------------------------------------------+      */
+/*    | Average     224401    1  110953   50.0  4.00    20.17    32.72 |      */
+/*    +----------------------------------------------------------------+      */
+/*                                                                            */
+/******************************************************************************/
+
+/******************************************************************************/
+
+/* The following structure is returned by the "compress" function below when  */
+/* the user asks the function to return identifying information.              */
+/* The most important field in the record is the working memory field which   */
+/* tells the calling program how much working memory should be passed to      */
+/* "compress" when it is called to perform a compression or decompression.    */
+/* LZRW3 uses the same amount of memory during compression and decompression. */
+/* For more information on this structure see "compress.h".                   */
+  
+#define U(X)            ((ULONG) X)
+#define SIZE_P_BYTE     (U(sizeof(UBYTE *)))
+#define SIZE_WORD       (U(sizeof(UWORD  )))
+#define ALIGNMENT_FUDGE (U(16))
+#define MEM_REQ ( U(4096)*(SIZE_P_BYTE) + ALIGNMENT_FUDGE )
+
+static struct compress_identity identity =
+{
+ U(0x032DDEA8),                           /* Algorithm identification number. */
+ MEM_REQ,                                 /* Working memory (bytes) required. */
+ "LZRW3",                                 /* Name of algorithm.               */
+ "1.0",                                   /* Version number of algorithm.     */
+ "31-Dec-1990",                           /* Date of algorithm.               */
+ "Public Domain",                         /* Copyright notice.                */
+ "Ross N. Williams",                      /* Author of algorithm.             */
+ "Renaissance Software",                  /* Affiliation of author.           */
+ "Public Domain"                          /* Vendor of algorithm.             */
+};
+LOCAL void compress_compress  (UBYTE *,UBYTE *,ULONG,UBYTE *, LONG *);
+LOCAL void compress_decompress(UBYTE *,UBYTE *,LONG, UBYTE *, ULONG *);
+
+/******************************************************************************/
+
+/* This function is the only function exported by this module.                */
+/* Depending on its first parameter, the function can be requested to         */
+/* compress a block of memory, decompress a block of memory, or to identify   */
+/* itself. For more information, see the specification file "compress.h".     */
+
+EXPORT void lzrw3_compress(action,wrk_mem,src_adr,src_len,dst_adr,p_dst_len)
+UWORD     action;      /* Action to be performed.                             */
+UBYTE   *wrk_mem;      /* Address of working memory we can use.               */
+UBYTE   *src_adr;      /* Address of input data.                              */
+LONG     src_len;      /* Length  of input data.                              */
+UBYTE   *dst_adr;      /* Address to put output data.                         */
+void  *p_dst_len;      /* Address of longword for length of output data.      */
+{
+ switch (action)
+   {
+    case COMPRESS_ACTION_IDENTITY:
+       *((struct compress_identity **)p_dst_len)= &identity;
+       break;
+    case COMPRESS_ACTION_COMPRESS:
+       compress_compress(wrk_mem,src_adr,src_len,dst_adr,(LONG *)p_dst_len);
+       break;
+    case COMPRESS_ACTION_DECOMPRESS:
+       compress_decompress(wrk_mem,src_adr,src_len,dst_adr,(LONG *)p_dst_len);
+       break;
+   }
+}
+
+/******************************************************************************/
+/*                                                                            */
+/* BRIEF DESCRIPTION OF THE LZRW3 ALGORITHM                                   */
+/* ========================================                                   */
+/* The LZRW3 algorithm is identical to the LZRW1-A algorithm except that      */
+/* instead of transmitting history offsets, it transmits hash table indexes.  */
+/* In order to decode the indexes, the decompressor must maintain an          */
+/* identical hash table. Copy items are straightforward:when the decompressor */
+/* receives a copy item, it simply looks up the hash table to translate the   */
+/* index into a pointer into the data already decompressed. To update the     */
+/* hash table, it replaces the same table entry with a pointer to the start   */
+/* of the newly decoded phrase. The tricky part is with literal items, for at */
+/* the time that the decompressor receives a literal item the decompressor    */
+/* does not have the three bytes in the Ziv (that the compressor has) to      */
+/* perform the three-byte hash. To solve this problem, in LZRW3, both the     */
+/* compressor and decompressor are wired up so that they "buffer" these       */
+/* literals and update their hash tables only when three bytes are available. */
+/* This makes the maximum buffering 2 bytes.                                  */
+/*                                                                            */
+/* Replacement of offsets by hash table indexes yields a few percent extra    */
+/* compression at the cost of some speed. LZRW3 is slower than LZRW1, LZRW1-A */
+/* and LZRW2, but yields better compression.                                  */
+/*                                                                            */
+/* Extra compression could be obtained by using a hash table of depth two.    */
+/* However, increasing the depth above one incurs a significant decrease in   */
+/* compression speed which was not considered worthwhile. Another reason for  */
+/* keeping the depth down to one was to allow easy comparison with the        */
+/* LZRW1-A and LZRW2 algorithms so as to demonstrate the exact effect of the  */
+/* use of direct hash indexes.                                                */
+/*                                                                            */
+/*                                  +---+                                     */
+/*                                  |___|4095                                 */
+/*                                  |___|                                     */
+/*              +---------------------*_|<---+   /----+---\                   */
+/*              |                   |___|    +---|Hash    |                   */
+/*              |                   |___|        |Function|                   */
+/*              |                   |___|        \--------/                   */
+/*              |                   |___|0            ^                       */
+/*              |                   +---+             |                       */
+/*              |                   Hash        +-----+                       */
+/*              |                   Table       |                             */
+/*              |                              ---                            */
+/*              v                              ^^^                            */
+/*      +-------------------------------------|----------------+              */
+/*      ||||||||||||||||||||||||||||||||||||||||||||||||||||||||              */
+/*      +-------------------------------------|----------------+              */
+/*      |                                     |1......18|      |              */
+/*      |<------- Lempel=History ------------>|<--Ziv-->|      |              */
+/*      |     (=bytes already processed)      |<-Still to go-->|              */
+/*      |<-------------------- INPUT BLOCK ------------------->|              */
+/*                                                                            */
+/* The diagram above for LZRW3 looks almost identical to the diagram for      */
+/* LZRW1. The difference is that in LZRW3, the compressor transmits hash      */
+/* table indices instead of Lempel offsets. For this to work, the             */
+/* decompressor must maintain a hash table as well as the compressor and both */
+/* compressor and decompressor must "buffer" literals, as the decompressor    */
+/* cannot hash phrases commencing with a literal until another two bytes have */
+/* arrived.                                                                   */
+/*                                                                            */
+/*  LZRW3 Algorithm Execution Summary                                         */
+/*  ---------------------------------                                         */
+/*  1. Hash the first three bytes of the Ziv to yield a hash table index h.   */
+/*  2. Look up the hash table yielding history pointer p.                     */
+/*  3. Match where p points with the Ziv. If there is a match of three or     */
+/*     more bytes, code those bytes (in the Ziv) as a copy item, otherwise    */
+/*     code the next byte in the Ziv as a literal item.                       */
+/*  4. Update the hash table as possible subject to the constraint that only  */
+/*     phrases commencing three bytes back from the Ziv can be hashed and     */
+/*     entered into the hash table. (This enables the decompressor to keep    */
+/*     pace). See the description and code for more details.                  */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/*                     DEFINITION OF COMPRESSED FILE FORMAT                   */
+/*                     ====================================                   */
+/*  * A compressed file consists of a COPY FLAG followed by a REMAINDER.      */
+/*  * The copy flag CF uses up four bytes with the first byte being the       */
+/*    least significant.                                                      */
+/*  * If CF=1, then the compressed file represents the remainder of the file  */
+/*    exactly. Otherwise CF=0 and the remainder of the file consists of zero  */
+/*    or more GROUPS, each of which represents one or more bytes.             */
+/*  * Each group consists of two bytes of CONTROL information followed by     */
+/*    sixteen ITEMs except for the last group which can contain from one      */
+/*    to sixteen items.                                                       */
+/*  * An item can be either a LITERAL item or a COPY item.                    */
+/*  * Each item corresponds to a bit in the control bytes.                    */
+/*  * The first control byte corresponds to the first 8 items in the group    */
+/*    with bit 0 corresponding to the first item in the group and bit 7 to    */
+/*    the eighth item in the group.                                           */
+/*  * The second control byte corresponds to the second 8 items in the group  */
+/*    with bit 0 corresponding to the ninth item in the group and bit 7 to    */
+/*    the sixteenth item in the group.                                        */
+/*  * A zero bit in a control word means that the corresponding item is a     */
+/*    literal item. A one bit corresponds to a copy item.                     */
+/*  * A literal item consists of a single byte which represents itself.       */
+/*  * A copy item consists of two bytes that represent from 3 to 18 bytes.    */
+/*  * The first  byte in a copy item will be denoted C1.                      */
+/*  * The second byte in a copy item will be denoted C2.                      */
+/*  * Bits will be selected using square brackets.                            */
+/*    For example: C1[0..3] is the low nibble of the first control byte.      */
+/*    of copy item C1.                                                        */
+/*  * The LENGTH of a copy item is defined to be C1[0..3]+3 which is a number */
+/*    in the range [3,18].                                                    */
+/*  * The INDEX of a copy item is defined to be C1[4..7]*256+C2[0..8] which   */
+/*    is a number in the range [0,4095].                                      */
+/*  * A copy item represents the sequence of bytes                            */
+/*       text[POS-OFFSET..POS-OFFSET+LENGTH-1] where                          */
+/*          text   is the entire text of the uncompressed string.             */
+/*          POS    is the index in the text of the character following the    */
+/*                   string represented by all the items preceeding the item  */
+/*                   being defined.                                           */
+/*          OFFSET is obtained from INDEX by looking up the hash table.       */
+/*                                                                            */
+/******************************************************************************/
+
+/* The following #define defines the length of the copy flag that appears at  */
+/* the start of the compressed file. The value of four bytes was chosen       */
+/* because the fast_copy routine on my Macintosh runs faster if the source    */
+/* and destination blocks are relatively longword aligned.                    */
+/* The actual flag data appears in the first byte. The rest are zeroed so as  */
+/* to normalize the compressed representation (i.e. not non-deterministic).   */
+#define FLAG_BYTES 4
+
+/* The following #defines define the meaning of the values of the copy        */
+/* flag at the start of the compressed file.                                  */
+#define FLAG_COMPRESS 0     /* Signals that output was result of compression. */
+#define FLAG_COPY     1     /* Signals that output was simply copied over.    */
+
+/* The 68000 microprocessor (on which this algorithm was originally developed */
+/* is fussy about non-aligned arrays of words. To avoid these problems the    */
+/* following macro can be used to "waste" from 0 to 3 bytes so as to align    */
+/* the argument pointer.                                                      */
+#define ULONG_ALIGN_UP(X) ((((ULONG)X)+sizeof(ULONG)-1)&~(sizeof(ULONG)-1))
+
+
+/* The following constant defines the maximum length of an uncompressed item. */
+/* This definition must not be changed; its value is hardwired into the code. */
+/* The longest number of bytes that can be spanned by a single item is 18     */
+/* for the longest copy item.                                                 */
+#define MAX_RAW_ITEM (18)
+
+/* The following constant defines the maximum length of an uncompressed group.*/
+/* This definition must not be changed; its value is hardwired into the code. */
+/* A group contains at most 16 items which explains this definition.          */
+#define MAX_RAW_GROUP (16*MAX_RAW_ITEM)
+
+/* The following constant defines the maximum length of a compressed group.   */
+/* This definition must not be changed; its value is hardwired into the code. */
+/* A compressed group consists of two control bytes followed by up to 16      */
+/* compressed items each of which can have a maximum length of two bytes.     */
+#define MAX_CMP_GROUP (2+16*2)
+
+/* The following constant defines the number of entries in the hash table.    */
+/* This definition must not be changed; its value is hardwired into the code. */
+#define HASH_TABLE_LENGTH (4096)
+
+/* LZRW3, unlike LZRW1(-A), must initialize its hash table so as to enable    */
+/* the compressor and decompressor to stay in step maintaining identical hash */
+/* tables. In an early version of the algorithm, the tables were simply       */
+/* initialized to zero and a check for zero was included just before the      */
+/* matching code. However, this test costs time. A better solution is to      */
+/* initialize all the entries in the hash table to point to a constant        */
+/* string. The decompressor does the same. This solution requires no extra    */
+/* test. The contents of the string do not matter so long as the string is    */
+/* the same for the compressor and decompressor and contains at least         */
+/* MAX_RAW_ITEM bytes. I chose consecutive decimal digits because they do not */
+/* have white space problems (e.g. there is no chance that the compiler will  */
+/* replace more than one space by a TAB) and because they make the length of  */
+/* the string obvious by inspection.                                          */
+#define START_STRING_18 ((UBYTE *) "123456789012345678")
+
+/* In this algorithm, hash values have to be calculated at more than one      */
+/* point. The following macro neatens the code up for this.                   */
+#define HASH(PTR) \
+   (((40543*(((*(PTR))<<8)^((*((PTR)+1))<<4)^(*((PTR)+2))))>>4) & 0xFFF)
+
+/******************************************************************************/
+                            
+LOCAL void compress_compress
+           (p_wrk_mem,p_src_first,src_len,p_dst_first,p_dst_len)
+/* Input  : Hand over the required amount of working memory in p_wrk_mem.     */
+/* Input  : Specify input block using p_src_first and src_len.                */
+/* Input  : Point p_dst_first to the start of the output zone (OZ).           */
+/* Input  : Point p_dst_len to a ULONG to receive the output length.          */
+/* Input  : Input block and output zone must not overlap.                     */
+/* Output : Length of output block written to *p_dst_len.                     */
+/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. May   */
+/* Output : write in OZ=Mem[p_dst_first..p_dst_first+src_len+MAX_CMP_GROUP-1].*/
+/* Output : Upon completion guaranteed *p_dst_len<=src_len+FLAG_BYTES.        */
+UBYTE *p_wrk_mem;
+UBYTE *p_src_first;
+ULONG  src_len;
+UBYTE *p_dst_first;
+LONG  *p_dst_len;
+{
+ /* p_src and p_dst step through the source and destination blocks.           */
+ register UBYTE *p_src = p_src_first;
+ register UBYTE *p_dst = p_dst_first;
+ /* The following variables are never modified and are used in the            */
+ /* calculations that determine when the main loop terminates.                */
+ UBYTE *p_src_post  = p_src_first+src_len;
+ UBYTE *p_dst_post  = p_dst_first+src_len;
+ UBYTE *p_src_max1  = p_src_first+src_len-MAX_RAW_ITEM;
+ UBYTE *p_src_max16 = p_src_first+src_len-MAX_RAW_ITEM*16;
+ /* The variables 'p_control' and 'control' are used to buffer control bits.  */
+ /* Before each group is processed, the next two bytes of the output block    */
+ /* are set aside for the control word for the group about to be processed.   */
+ /* 'p_control' is set to point to the first byte of that word. Meanwhile,    */
+ /* 'control' buffers the control bits being generated during the processing  */
+ /* of the group. Instead of having a counter to keep track of how many items */
+ /* have been processed (=the number of bits in the control word), at the     */
+ /* start of each group, the top word of 'control' is filled with 1 bits.     */
+ /* As 'control' is shifted for each item, the 1 bits in the top word are     */
+ /* absorbed or destroyed. When they all run out (i.e. when the top word is   */
+ /* all zero bits, we know that we are at the end of a group.                 */
+# define TOPWORD 0xFFFF0000
+ UBYTE *p_control;
+ register ULONG control=TOPWORD;
+ /* THe variable 'hash' always points to the first element of the hash table. */
+ UBYTE **hash= (UBYTE **)  ULONG_ALIGN_UP(p_wrk_mem);
+ /* The following two variables represent the literal buffer. p_h1 points to  */
+ /* the hash table entry corresponding to the youngest literal. p_h2 points   */
+ /* to the hash table entry corresponding to the second youngest literal.     */
+ /* Note: p_h1=0=>p_h2=0 because zero values denote absence of a pending      */
+ /* literal. The variables are initialized to zero meaning an empty "buffer". */
+ UBYTE **p_h1=0;
+ UBYTE **p_h2=0;
+  
+ /* To start, we write the flag bytes. Being optimistic, we set the flag to   */
+ /* FLAG_COMPRESS. The remaining flag bytes are zeroed so as to keep the      */
+ /* algorithm deterministic.                                                  */
+ *p_dst++=FLAG_COMPRESS;
+ {UWORD i; for (i=2;i<=FLAG_BYTES;i++) *p_dst++=0;}
+
+ /* Reserve the first word of output as the control word for the first group. */
+ /* Note: This is undone at the end if the input block is empty.              */
+ p_control=p_dst; p_dst+=2;
+ /* Initialize all elements of the hash table to point to a constant string.  */
+ /* Use of an unrolled loop speeds this up considerably.                      */
+ {UWORD i; UBYTE **p_h=hash;
+#  define ZH *p_h++=START_STRING_18
+  for (i=0;i<256;i++)     /* 256=HASH_TABLE_LENGTH/16. */
+    {ZH;ZH;ZH;ZH;
+     ZH;ZH;ZH;ZH;
+     ZH;ZH;ZH;ZH;
+     ZH;ZH;ZH;ZH;}
+ }
+
+ /* The main loop processes either 1 or 16 items per iteration. As its        */
+ /* termination logic is complicated, I have opted for an infinite loop       */
+ /* structure containing 'break' and 'goto' statements.                       */
+ while (TRUE)
+   {/* Begin main processing loop. */
+   
+    /* Note: All the variables here except unroll should be defined within    */
+    /*       the inner loop. Unfortunately the loop hasn't got a block.       */
+     register UBYTE *p;         /* Scans through targ phrase during matching. */
+     register UBYTE *p_ziv= NULL ;     /* Points to first byte of current Ziv.       */
+     register UWORD unroll;     /* Loop counter for unrolled inner loop.      */
+     register UWORD index;      /* Index of current hash table entry.         */
+     register UBYTE **p_h0 = NULL ;     /* Pointer to current hash table entry.       */
+     
+    /* Test for overrun and jump to overrun code if necessary.                */
+    if (p_dst>p_dst_post)
+       goto overrun;
+       
+    /* The following cascade of if statements efficiently catches and deals   */
+    /* with varying degrees of closeness to the end of the input block.       */
+    /* When we get very close to the end, we stop updating the table and      */
+    /* code the remaining bytes as literals. This makes the code simpler.     */
+    unroll=16;
+    if (p_src>p_src_max16)
+      {
+       unroll=1;
+       if (p_src>p_src_max1)
+         {
+          if (p_src==p_src_post)
+             break;
+          else
+             goto literal;
+         }
+      }
+         
+    /* This inner unrolled loop processes 'unroll' (whose value is either 1   */
+    /* or 16) items. I have chosen to implement this loop with labels and     */
+    /* gotos to heighten the ease with which the loop may be implemented with */
+    /* a single decrement and branch instruction in assembly language and     */
+    /* also because the labels act as highly readable place markers.          */
+    /* (Also because we jump into the loop for endgame literals (see above)). */
+    
+    begin_unrolled_loop:
+    
+       /* To process the next phrase, we hash the next three bytes and use    */
+       /* the resultant hash table index to look up the hash table. A pointer */
+       /* to the entry is stored in p_h0 so as to avoid an array lookup. The  */
+       /* hash table entry *p_h0 is looked up yielding a pointer p to a       */
+       /* potential match of the Ziv in the history.                          */
+       index=HASH(p_src);
+       p_h0=&hash[index];
+       p=*p_h0;
+       
+       /* Having looked up the candidate position, we are in a position to    */
+       /* attempt a match. The match loop has been unrolled using the PS      */
+       /* macro so that failure within the first three bytes automatically    */
+       /* results in the literal branch being taken. The coding is simple.    */
+       /* p_ziv saves p_src so we can let p_src wander.                       */
+#       define PS *p++!=*p_src++
+       p_ziv=p_src;
+       if (PS || PS || PS)
+         {
+          /* Literal. */
+          
+          /* Code the literal byte as itself and a zero control bit.          */
+          p_src=p_ziv; literal: *p_dst++=*p_src++; control&=0xFFFEFFFF;
+          
+          /* We have just coded a literal. If we had two pending ones, that   */
+          /* makes three and we can update the hash table.                    */
+          if (p_h2!=0)
+             {*p_h2=p_ziv-2;}
+             
+          /* In any case, rotate the hash table pointers for next time. */
+          p_h2=p_h1; p_h1=p_h0;
+          
+         }
+       else
+         {
+          /* Copy */
+          
+          /* Match up to 15 remaining bytes using an unrolled loop and code. */
+#if 0
+          PS || PS || PS || PS || PS || PS || PS || PS ||
+          PS || PS || PS || PS || PS || PS || PS || p_src++;
+#else     
+          if (
+               !( PS || PS || PS || PS || PS || PS || PS || PS ||
+                  PS || PS || PS || PS || PS || PS || PS ) 
+             ) p_src++;
+#endif
+          *p_dst++=((index&0xF00)>>4)|(--p_src-p_ziv-3);
+          *p_dst++=index&0xFF;
+          
+          /* As we have just coded three bytes, we are now in a position to   */
+          /* update the hash table with the literal bytes that were pending   */
+          /* upon the arrival of extra context bytes.                         */
+          if (p_h1!=0)
+            {
+             if (p_h2!=0)
+               {*p_h2=p_ziv-2; p_h2=0;}
+             *p_h1=p_ziv-1; p_h1=0;
+            }
+            
+          /* In any case, we can update the hash table based on the current   */
+          /* position as we just coded at least three bytes in a copy items.  */
+          *p_h0=p_ziv;
+          
+         }
+       control>>=1;
+                
+       /* This loop is all set up for a decrement and jump instruction! */
+#ifndef linux
+`    end_unrolled_loop: if (--unroll) goto begin_unrolled_loop;
+#else
+    /* end_unrolled_loop: */ if (--unroll) goto begin_unrolled_loop;
+#endif
+
+    /* At this point it will nearly always be the end of a group in which     */
+    /* case, we have to do some control-word processing. However, near the    */
+    /* end of the input block, the inner unrolled loop is only executed once. */
+    /* This necessitates the 'if' test.                                       */
+    if ((control&TOPWORD)==0)
+      {
+       /* Write the control word to the place we saved for it in the output. */
+       *p_control++=  control     &0xFF;
+       *p_control  = (control>>8) &0xFF;
+
+       /* Reserve the next word in the output block for the control word */
+       /* for the group about to be processed.                           */
+       p_control=p_dst; p_dst+=2;
+       
+       /* Reset the control bits buffer. */
+       control=TOPWORD;
+      }
+          
+   } /* End main processing loop. */
+   
+ /* After the main processing loop has executed, all the input bytes have     */
+ /* been processed. However, the control word has still to be written to the  */
+ /* word reserved for it in the output at the start of the most recent group. */
+ /* Before writing, the control word has to be shifted so that all the bits   */
+ /* are in the right place. The "empty" bit positions are filled with 1s      */
+ /* which partially fill the top word.                                        */
+ while(control&TOPWORD) control>>=1;
+ *p_control++= control     &0xFF;
+ *p_control++=(control>>8) &0xFF;
+ /* If the last group contained no items, delete the control word too.        */
+ if (p_control==p_dst) p_dst-=2;
+ /* Write the length of the output block to the dst_len parameter and return. */
+ *p_dst_len=p_dst-p_dst_first;                           
+ return;
+ /* Jump here as soon as an overrun is detected. An overrun is defined to     */
+ /* have occurred if p_dst>p_dst_first+src_len. That is, the moment the       */
+ /* length of the output written so far exceeds the length of the input block.*/
+ /* The algorithm checks for overruns at least at the end of each group       */
+ /* which means that the maximum overrun is MAX_CMP_GROUP bytes.              */
+ /* Once an overrun occurs, the only thing to do is to set the copy flag and  */
+ /* copy the input over.                                                      */
+ overrun:
+#if 0
+ *p_dst_first=FLAG_COPY;
+ fast_copy(p_src_first,p_dst_first+FLAG_BYTES,src_len);
+ *p_dst_len=src_len+FLAG_BYTES;
+#else
+ fast_copy(p_src_first,p_dst_first,src_len);
+ *p_dst_len= -src_len; /* return a negative number to indicate uncompressed data */
+#endif
+}
+
+/******************************************************************************/
+
+LOCAL void compress_decompress
+           (p_wrk_mem,p_src_first,src_len,p_dst_first,p_dst_len)
+/* Input  : Hand over the required amount of working memory in p_wrk_mem.     */
+/* Input  : Specify input block using p_src_first and src_len.                */
+/* Input  : Point p_dst_first to the start of the output zone.                */
+/* Input  : Point p_dst_len to a ULONG to receive the output length.          */
+/* Input  : Input block and output zone must not overlap. User knows          */
+/* Input  : upperbound on output block length from earlier compression.       */
+/* Input  : In any case, maximum expansion possible is nine times.            */
+/* Output : Length of output block written to *p_dst_len.                     */
+/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1].       */
+/* Output : Writes only  in Mem[p_dst_first..p_dst_first+*p_dst_len-1].       */
+UBYTE *p_wrk_mem;
+UBYTE *p_src_first;
+LONG   src_len;
+UBYTE *p_dst_first;
+ULONG *p_dst_len;
+{
+ /* Byte pointers p_src and p_dst scan through the input and output blocks.   */
+ register UBYTE *p_src = p_src_first+FLAG_BYTES;
+ register UBYTE *p_dst = p_dst_first;
+ /* we need to avoid a SEGV when trying to uncompress corrupt data */
+ register UBYTE *p_dst_post = p_dst_first + *p_dst_len;
+
+ /* The following two variables are never modified and are used to control    */
+ /* the main loop.                                                            */
+ UBYTE *p_src_post  = p_src_first+src_len;
+ UBYTE *p_src_max16 = p_src_first+src_len-(MAX_CMP_GROUP-2);
+ /* The hash table is the only resident of the working memory. The hash table */
+ /* contains HASH_TABLE_LENGTH=4096 pointers to positions in the history. To  */
+ /* keep Macintoshes happy, it is longword aligned.                           */
+ UBYTE **hash = (UBYTE **) ULONG_ALIGN_UP(p_wrk_mem);
+
+ /* The variable 'control' is used to buffer the control bits which appear in */
+ /* groups of 16 bits (control words) at the start of each compressed group.  */
+ /* When each group is read, bit 16 of the register is set to one. Whenever   */
+ /* a new bit is needed, the register is shifted right. When the value of the */
+ /* register becomes 1, we know that we have reached the end of a group.      */
+ /* Initializing the register to 1 thus instructs the code to follow that it  */
+ /* should read a new control word immediately.                               */
+ register ULONG control=1;
+ /* The value of 'literals' is always in the range 0..3. It is the number of  */
+ /* consecutive literal items just seen. We have to record this number so as  */
+ /* to know when to update the hash table. When literals gets to 3, there     */
+ /* have been three consecutive literals and we can update at the position of */
+ /* the oldest of the three.                                                  */
+ register UWORD literals=0;
+ /* Check the leading copy flag to see if the compressor chose to use a copy  */
+ /* operation instead of a compression operation. If a copy operation was     */
+ /* used, then all we need to do is copy the data over, set the output length */
+ /* and return.                                                               */
+#if 0
+ if (*p_src_first==FLAG_COPY)
+   {
+    fast_copy(p_src_first+FLAG_BYTES,p_dst_first,src_len-FLAG_BYTES);
+    *p_dst_len=src_len-FLAG_BYTES;
+    return;
+   }
+#else
+  if ( src_len < 0 )
+  {                                            
+   fast_copy(p_src_first,p_dst_first,-src_len );
+   *p_dst_len = (ULONG)-src_len;
+   return;
+  }
+#endif
+   
+ /* Initialize all elements of the hash table to point to a constant string.  */
+ /* Use of an unrolled loop speeds this up considerably.                      */
+ {UWORD i; UBYTE **p_h=hash;
+#  define ZJ *p_h++=START_STRING_18
+  for (i=0;i<256;i++)     /* 256=HASH_TABLE_LENGTH/16. */
+    {ZJ;ZJ;ZJ;ZJ;
+     ZJ;ZJ;ZJ;ZJ;
+     ZJ;ZJ;ZJ;ZJ;
+     ZJ;ZJ;ZJ;ZJ;}
+ }
+
+ /* The outer loop processes either 1 or 16 items per iteration depending on  */
+ /* how close p_src is to the end of the input block.                         */
+ while (p_src!=p_src_post)
+   {/* Start of outer loop */
+   
+    register UWORD unroll;   /* Counts unrolled loop executions.              */
+    
+    /* When 'control' has the value 1, it means that the 16 buffered control  */
+    /* bits that were read in at the start of the current group have all been */
+    /* shifted out and that all that is left is the 1 bit that was injected   */
+    /* into bit 16 at the start of the current group. When we reach the end   */
+    /* of a group, we have to load a new control word and inject a new 1 bit. */
+    if (control==1)
+      {
+       control=0x10000|*p_src++;
+       control|=(*p_src++)<<8;
+      }
+
+    /* If it is possible that we are within 16 groups from the end of the     */
+    /* input, execute the unrolled loop only once, else process a whole group */
+    /* of 16 items by looping 16 times.                                       */
+    unroll= p_src<=p_src_max16 ? 16 : 1;
+
+    /* This inner loop processes one phrase (item) per iteration. */
+    while (unroll--)
+      { /* Begin unrolled inner loop. */
+      
+       /* Process a literal or copy item depending on the next control bit. */
+       if (control&1)
+         {
+          /* Copy item. */
+          
+          register UBYTE *p;           /* Points to place from which to copy. */
+          register UWORD lenmt;        /* Length of copy item minus three.    */
+          register UBYTE **p_hte;      /* Pointer to current hash table entry.*/
+          register UBYTE *p_ziv=p_dst; /* Pointer to start of current Ziv.    */
+          
+          /* Read and dismantle the copy word. Work out from where to copy.   */
+          lenmt=*p_src++;
+          p_hte=&hash[((lenmt&0xF0)<<4)|*p_src++];
+          p=*p_hte;
+          lenmt&=0xF;
+          
+          /* Now perform the copy using a half unrolled loop. */
+          *p_dst++=*p++;
+          *p_dst++=*p++;
+          *p_dst++=*p++;
+          while (lenmt--)
+             *p_dst++=*p++;
+                 
+          /* Because we have just received 3 or more bytes in a copy item     */
+          /* (whose bytes we have just installed in the output), we are now   */
+          /* in a position to flush all the pending literal hashings that had */
+          /* been postponed for lack of bytes.                                */
+          if (literals>0)
+            {
+             register UBYTE *r=p_ziv-literals;;
+             hash[HASH(r)]=r;
+             if (literals==2)
+                {r++; hash[HASH(r)]=r;}
+             literals=0;
+            }
+            
+          /* In any case, we can immediately update the hash table with the   */
+          /* current position. We don't need to do a HASH(...) to work out    */
+          /* where to put the pointer, as the compressor just told us!!!      */
+          *p_hte=p_ziv;
+          
+         }
+       else
+         {
+          /* Literal item. */
+          
+          /* Copy over the literal byte. */
+          *p_dst++=*p_src++;
+          
+          /* If we now have three literals waiting to be hashed into the hash */
+          /* table, we can do one of them now (because there are three).      */
+          if (++literals == 3)
+             {register UBYTE *p=p_dst-3; hash[HASH(p)]=p; literals=2;}
+         }
+          
+       /* Shift the control buffer so the next control bit is in bit 0. */
+       control>>=1;
+#if 1
+       if (p_dst > p_dst_post) 
+       {
+              /* Shit: we tried to decompress corrupt data */
+              *p_dst_len = 0;
+              return;
+       }
+#endif
+      } /* End unrolled inner loop. */
+               
+   } /* End of outer loop */
+   
+ /* Write the length of the decompressed data before returning. */
+  *p_dst_len=p_dst-p_dst_first;
+}
+
+/******************************************************************************/
+/*                               End of LZRW3.C                               */
+/******************************************************************************/
diff --git a/drivers/char/ftape/compressor/lzrw3.h b/drivers/char/ftape/compressor/lzrw3.h
new file mode 100644 (file)
index 0000000..533feba
--- /dev/null
@@ -0,0 +1,253 @@
+#ifndef _LZRW3_H
+#define _LZRW3_H
+/*
+ * $Source: /homes/cvs/ftape-stacked/ftape/compressor/lzrw3.h,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/10/05 19:12:30 $
+ *
+ *  include files for lzrw3. Only slighty modified from the original
+ *  version. Assembles the three include files compress.h, port.h and
+ *  fastcopy.h from the original lzrw3 package.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+
+/******************************************************************************/
+/*                                                                            */
+/*                                 COMPRESS.H                                 */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/* Author : Ross Williams.                                                    */
+/* Date   : December 1989.                                                    */
+/*                                                                            */
+/* This header file defines the interface to a set of functions called        */
+/* 'compress', each member of which implements a particular data compression  */
+/* algorithm.                                                                 */
+/*                                                                            */
+/* Normally in C programming, for each .H file, there is a corresponding .C   */
+/* file that implements the functions promised in the .H file.                */
+/* Here, there are many .C files corresponding to this header file.           */
+/* Each comforming implementation file contains a single function             */
+/* called 'compress' that implements a single data compression                */
+/* algorithm that conforms with the interface specified in this header file.  */
+/* Only one algorithm can be linked in at a time in this organization.        */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/*                    DEFINITION OF FUNCTION COMPRESS                         */
+/*                    ===============================                         */
+/*                                                                            */
+/* Summary of Function Compress                                               */
+/* ----------------------------                                               */
+/* The action that 'compress' takes depends on its first argument called      */
+/* 'action'.  The function provides three actions:                            */
+/*                                                                            */
+/*    - Return information about the algorithm.                               */
+/*    - Compress   a block of memory.                                         */
+/*    - Decompress a block of memory.                                         */
+/*                                                                            */
+/* Parameters                                                                 */
+/* ----------                                                                 */
+/* See the formal C definition later for a description of the parameters.     */
+/*                                                                            */
+/* Constants                                                                  */
+/* ---------                                                                  */
+/* COMPRESS_OVERRUN: The constant COMPRESS_OVERRUN defines by how many bytes  */
+/* an algorithm is allowed to expand a block during a compression operation.  */
+/*                                                                            */
+/* Although compression algorithms usually compress data, there will always   */
+/* be data that a given compressor will expand (this can be proven).          */
+/* Fortunately, the degree of expansion can be limited to a single bit, by    */
+/* copying over the input data if the data gets bigger during compression.    */
+/* To allow for this possibility, the first bit of a compressed               */
+/* representation can be used as a flag indicating whether the                */
+/* input data was copied over, or truly compressed. In practice, the first    */
+/* byte would be used to store this bit so as to maintain byte alignment.     */
+/*                                                                            */
+/* Unfortunately, in general, the only way to tell if an algorithm will       */
+/* expand a particular block of data is to run the algorithm on the data.     */
+/* If the algorithm does not continuously monitor how many output bytes it    */
+/* has written, it might write an output block far larger than the input      */
+/* block before realizing that it has done so.                                */
+/* On the other hand, continuous checks on output length are inefficient.     */
+/*                                                                            */
+/* To cater for all these problems, this interface definition:                */
+/* > Allows a compression algorithm to return an output block that is up to   */
+/*   COMPRESS_OVERRUN bytes longer than the input block.                      */
+/* > Allows a compression algorithm to write up to COMPRESS_OVERRUN bytes     */
+/*   more than the length of the input block to the memory of the output      */
+/*   block regardless of the length of the output block eventually returned.  */
+/*   This allows an algorithm to overrun the length of the input block in the */
+/*   output block by up to COMPRESS_OVERRUN bytes between expansion checks.   */
+/*                                                                            */
+/* The problem does not arise for decompression.                              */
+/*                                                                            */
+/* Identity Action                                                            */
+/* ---------------                                                            */
+/* > action must be COMPRESS_ACTION_IDENTITY.                                 */
+/* > p_dst_len must point to a longword to receive a longword address.        */
+/* > The value of the other parameters does not matter.                       */
+/* > After execution, the longword that p_dst_len points to will be a pointer */
+/*   to a structure of type compress_identity.                                */
+/*   Thus, for example, after the call, (*p_dst_len)->memory will return the  */
+/*   number of bytes of working memory that the algorithm requires to run.    */
+/* > The values of the identity structure returned are fixed constant         */
+/*   attributes of the algorithm and must not vary from call to call.         */
+/*                                                                            */
+/* Common Requirements for Compression and Decompression Actions              */
+/* -------------------------------------------------------------              */
+/* > wrk_mem must point to an unused block of memory of a length specified in */
+/*   the algorithm's identity block. The identity block can be obtained by    */
+/*   making a separate call to compress, specifying the identity action.      */
+/* > The INPUT BLOCK is defined to be Memory[src_addr,src_addr+src_len-1].    */
+/* > dst_len will be used to denote *p_dst_len.                               */
+/* > dst_len is not read by compress, only written.                           */
+/* > The value of dst_len is defined only upon termination.                   */
+/* > The OUTPUT BLOCK is defined to be Memory[dst_addr,dst_addr+dst_len-1].   */
+/*                                                                            */
+/* Compression Action                                                         */
+/* ------------------                                                         */
+/* > action must be COMPRESS_ACTION_COMPRESS.                                 */
+/* > src_len must be in the range [0,COMPRESS_MAX_ORG].                       */
+/* > The OUTPUT ZONE is defined to be                                         */
+/*      Memory[dst_addr,dst_addr+src_len-1+COMPRESS_OVERRUN].                 */
+/* > The function can modify any part of the output zone regardless of the    */
+/*   final length of the output block.                                        */
+/* > The input block and the output zone must not overlap.                    */
+/* > dst_len will be in the range [0,src_len+COMPRESS_OVERRUN].               */
+/* > dst_len will be in the range [0,COMPRESS_MAX_COM] (from prev fact).      */
+/* > The output block will consist of a representation of the input block.    */
+/*                                                                            */
+/* Decompression Action                                                       */
+/* --------------------                                                       */
+/* > action must be COMPRESS_ACTION_DECOMPRESS.                               */
+/* > The input block must be the result of an earlier compression operation.  */
+/* > If the previous fact is true, the following facts must also be true:     */
+/*   > src_len will be in the range [0,COMPRESS_MAX_COM].                     */
+/*   > dst_len will be in the range [0,COMPRESS_MAX_ORG].                     */
+/* > The input and output blocks must not overlap.                            */
+/* > Only the output block is modified.                                       */
+/* > Upon termination, the output block will consist of the bytes contained   */
+/*   in the input block passed to the earlier compression operation.          */
+/*                                                                            */
+/******************************************************************************/
+
+/******************************************************************************/
+/*                                                                            */
+/*                                    PORT.H                                  */
+/*                                                                            */
+/******************************************************************************/
+/*                                                                            */
+/* This module contains macro definitions and types that are likely to        */
+/* change between computers.                                                  */
+/*                                                                            */
+/******************************************************************************/
+
+#ifndef DONE_PORT       /* Only do this if not previously done.               */
+
+   #ifdef THINK_C
+      #define UBYTE unsigned char      /* Unsigned byte                       */
+      #define UWORD unsigned int       /* Unsigned word (2 bytes)             */
+      #define ULONG unsigned long      /* Unsigned word (4 bytes)             */
+      #define BOOL  unsigned char      /* Boolean                             */
+      #define FOPEN_BINARY_READ  "rb"  /* Mode string for binary reading.     */
+      #define FOPEN_BINARY_WRITE "wb"  /* Mode string for binary writing.     */
+      #define FOPEN_TEXT_APPEND  "a"   /* Mode string for text appending.     */
+      #define REAL double              /* USed for floating point stuff.      */
+   #endif
+   #if defined(LINUX) || defined(linux)
+      #define UBYTE __u8               /* Unsigned byte                       */
+      #define UWORD __u16              /* Unsigned word (2 bytes)             */
+      #define ULONG __u32              /* Unsigned word (4 bytes)             */
+      #define LONG  __s32              /* Signed   word (4 bytes)             */
+      #define BOOL  is not used here   /* Boolean                             */
+      #define FOPEN_BINARY_READ  not used  /* Mode string for binary reading. */
+      #define FOPEN_BINARY_WRITE not used  /* Mode string for binary writing. */
+      #define FOPEN_TEXT_APPEND  not used  /* Mode string for text appending. */
+      #define REAL not used                /* USed for floating point stuff.  */
+      #ifndef TRUE
+      #define TRUE 1
+      #endif
+   #endif
+
+   #define DONE_PORT                   /* Don't do all this again.            */
+   #define MALLOC_FAIL NULL            /* Failure status from malloc()        */
+   #define LOCAL static                /* For non-exported routines.          */
+   #define EXPORT                      /* Signals exported function.          */
+   #define then                        /* Useful for aligning ifs.            */
+
+#endif
+
+/******************************************************************************/
+/*                              End of PORT.H                                 */
+/******************************************************************************/
+
+#define COMPRESS_ACTION_IDENTITY   0
+#define COMPRESS_ACTION_COMPRESS   1
+#define COMPRESS_ACTION_DECOMPRESS 2
+
+#define COMPRESS_OVERRUN 1024
+#define COMPRESS_MAX_COM 0x70000000
+#define COMPRESS_MAX_ORG (COMPRESS_MAX_COM-COMPRESS_OVERRUN)
+
+#define COMPRESS_MAX_STRLEN 255
+
+/* The following structure provides information about the algorithm.         */
+/* > The top bit of id must be zero. The remaining bits must be chosen by    */
+/*   the author of the algorithm by tossing a coin 31 times.                 */
+/* > The amount of memory requested by the algorithm is specified in bytes   */
+/*   and must be in the range [0,0x70000000].                                */
+/* > All strings s must be such that strlen(s)<=COMPRESS_MAX_STRLEN.         */
+struct compress_identity
+  {
+   ULONG id;           /* Identifying number of algorithm.            */
+   ULONG memory;       /* Number of bytes of working memory required. */
+
+   char  *name;        /* Name of algorithm.                          */
+   char  *version;     /* Version number.                             */
+   char  *date;        /* Date of release of this version.            */
+   char  *copyright;   /* Copyright message.                          */
+
+   char  *author;      /* Author of algorithm.                        */
+   char  *affiliation; /* Affiliation of author.                      */
+   char  *vendor;      /* Where the algorithm can be obtained.        */
+  };
+
+void  lzrw3_compress(        /* Single function interface to compression algorithm. */
+UWORD     action,      /* Action to be performed.                             */
+UBYTE   *wrk_mem,      /* Working memory temporarily given to routine to use. */
+UBYTE   *src_adr,      /* Address of input  data.                             */
+LONG     src_len,      /* Length  of input  data.                             */
+UBYTE   *dst_adr,      /* Address of output data.                             */
+void  *p_dst_len       /* Pointer to a longword where routine will write:     */
+                       /*    If action=..IDENTITY   => Adr of id structure.   */
+                       /*    If action=..COMPRESS   => Length of output data. */
+                       /*    If action=..DECOMPRESS => Length of output data. */
+);
+
+/******************************************************************************/
+/*                             End of COMPRESS.H                              */
+/******************************************************************************/
+
+
+/******************************************************************************/
+/*                                  fast_copy.h                               */
+/******************************************************************************/
+
+/* This function copies a block of memory very quickly.                       */
+/* The exact speed depends on the relative alignment of the blocks of memory. */
+/* PRE  : 0<=src_len<=(2^32)-1 .                                              */
+/* PRE  : Source and destination blocks must not overlap.                     */
+/* POST : MEM[dst_adr,dst_adr+src_len-1]=MEM[src_adr,src_adr+src_len-1].      */
+/* POST : MEM[dst_adr,dst_adr+src_len-1] is the only memory changed.          */
+
+#define fast_copy(src,dst,len) memcpy(dst,src,len)
+
+/******************************************************************************/
+/*                               End of fast_copy.h                           */
+/******************************************************************************/
+
+#endif
diff --git a/drivers/char/ftape/compressor/zftape-compress.c b/drivers/char/ftape/compressor/zftape-compress.c
new file mode 100644 (file)
index 0000000..382b58a
--- /dev/null
@@ -0,0 +1,1317 @@
+/*
+ *      Copyright (C) 1994-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+ *
+ *     This file implements a "generic" interface between the *
+ *     zftape-driver and a compression-algorithm. The *
+ *     compression-algorithm currently used is a LZ77. I use the *
+ *     implementation lzrw3 by Ross N. Williams (Renaissance *
+ *     Software). The compression program itself is in the file
+ *     lzrw3.c * and lzrw3.h.  To adopt another compression algorithm
+ *     the functions * zft_compress() and zft_uncompress() must be
+ *     changed * appropriately. See below.
+ */
+
+ char zftc_src[] ="$Source: /homes/cvs/ftape-stacked/ftape/compressor/zftape-compress.c,v $";
+ char zftc_rev[] = "$Revision: 1.1.6.1 $";
+ char zftc_dat[] = "$Date: 1997/11/16 15:15:56 $";
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../compressor/zftape-compress.h"
+#include "../zftape/zftape-vtbl.h"
+#include "../compressor/lzrw3.h"
+
+/*
+ *   global variables
+ */
+
+/* I handle the allocation of this buffer as a special case, because
+ * it's size varies depending on the tape length inserted.
+ */
+
+/* local variables 
+ */
+static int keep_module_locked = 1;
+
+static void *zftc_wrk_mem = NULL;
+static __u8 *zftc_buf     = NULL;
+static void *zftc_scratch_buf  = NULL;
+
+/* compression statistics 
+ */
+static unsigned int zftc_wr_uncompressed = 0;
+static unsigned int zftc_wr_compressed   = 0;
+static unsigned int zftc_rd_uncompressed = 0;
+static unsigned int zftc_rd_compressed   = 0;
+
+/* forward */
+static int  zftc_write(int *write_cnt,
+                      __u8 *dst_buf, const int seg_sz,
+                      const __u8 *src_buf, const int req_len,
+                      const zft_position *pos, const zft_volinfo *volume);
+static int  zftc_read(int *read_cnt,
+                     __u8  *dst_buf, const int to_do,
+                     const __u8 *src_buf, const int seg_sz,
+                     const zft_position *pos, const zft_volinfo *volume);
+static int  zftc_seek(unsigned int new_block_pos, 
+                     zft_position *pos, const zft_volinfo *volume,
+                     __u8 *buffer);
+static void zftc_lock   (void);
+static void zftc_reset  (void);
+static void zftc_cleanup(void);
+static void zftc_stats      (void);
+
+/* compressed segment. This conforms to QIC-80-MC, Revision K.
+ * 
+ * Rev. K applies to tapes with `fixed length format' which is
+ * indicated by format code 2,3 and 5. See below for format code 4 and 6
+ *
+ * 2 bytes: offset of compression segment structure
+ *          29k > offset >= 29k-18: data from previous segment ens in this
+ *                                  segment and no compressed block starts
+ *                                  in this segment
+ *                     offset == 0: data from previous segment occupies entire
+ *                                  segment and continues in next segment
+ * n bytes: remainder from previous segment
+ * 
+ * Rev. K:  
+ * 4 bytes: 4 bytes: files set byte offset
+ * Post Rev. K and QIC-3020/3020:
+ * 8 bytes: 8 bytes: files set byte offset
+ * 2 bytes: byte count N (amount of data following)
+ *          bit 15 is set if data is compressed, bit 15 is not
+ *          set if data is uncompressed
+ * N bytes: data (as much as specified in the byte count)
+ * 2 bytes: byte count N_1 of next cluster
+ * N_1 bytes: data of next cluset
+ * 2 bytes: byte count N_2 of next cluster
+ * N_2 bytes: ...  
+ *
+ * Note that the `N' byte count accounts only for the bytes that in the
+ * current segment if the cluster spans to the next segment.
+ */
+
+typedef struct
+{
+       int cmpr_pos;             /* actual position in compression buffer */
+       int cmpr_sz;              /* what is left in the compression buffer
+                                  * when copying the compressed data to the
+                                  * deblock buffer
+                                  */
+       unsigned int first_block; /* location of header information in
+                                  * this segment
+                                  */
+       unsigned int count;       /* amount of data of current block
+                                  * contained in current segment 
+                                  */
+       unsigned int offset;      /* offset in current segment */
+       unsigned int spans:1;     /* might continue in next segment */
+       unsigned int uncmpr;      /* 0x8000 if this block contains
+                                  * uncompressed data 
+                                  */
+       __s64 foffs;              /* file set byte offset, same as in 
+                                  * compression map segment
+                                  */
+} cmpr_info;
+
+static cmpr_info cseg; /* static data. Must be kept uptodate and shared by 
+                       * read, write and seek functions
+                       */
+
+#define DUMP_CMPR_INFO(level, msg, info)                               \
+       TRACE(level, msg "\n"                                           \
+             KERN_INFO "cmpr_pos   : %d\n"                             \
+             KERN_INFO "cmpr_sz    : %d\n"                             \
+             KERN_INFO "first_block: %d\n"                             \
+             KERN_INFO "count      : %d\n"                             \
+             KERN_INFO "offset     : %d\n"                             \
+             KERN_INFO "spans      : %d\n"                             \
+             KERN_INFO "uncmpr     : 0x%04x\n"                         \
+             KERN_INFO "foffs      : " LL_X,                           \
+             (info)->cmpr_pos, (info)->cmpr_sz, (info)->first_block,   \
+             (info)->count, (info)->offset, (info)->spans == 1,        \
+             (info)->uncmpr, LL((info)->foffs))
+
+/*   dispatch compression segment info, return error code
+ *  
+ *   afterwards, cseg->offset points to start of data of the NEXT
+ *   compressed block, and cseg->count contains the amount of data
+ *   left in the actual compressed block. cseg->spans is set to 1 if
+ *   the block is continued in the following segment. Otherwise it is
+ *   set to 0. 
+ */
+static int get_cseg (cmpr_info *cinfo, const __u8 *buff, 
+                    const unsigned int seg_sz,
+                    const zft_volinfo *volume)
+{
+       TRACE_FUN(ft_t_flow);
+
+       cinfo->first_block = GET2(buff, 0);
+       if (cinfo->first_block == 0) { /* data spans to next segment */
+               cinfo->count  = seg_sz - sizeof(__u16);
+               cinfo->offset = seg_sz;
+               cinfo->spans = 1;
+       } else { /* cluster definetely ends in this segment */
+               if (cinfo->first_block > seg_sz) {
+                       /* data corrupted */
+                       TRACE_ABORT(-EIO, ft_t_err, "corrupted data:\n"
+                                   KERN_INFO "segment size: %d\n"
+                                   KERN_INFO "first block : %d",
+                                   seg_sz, cinfo->first_block);
+               }
+               cinfo->count  = cinfo->first_block - sizeof(__u16);
+               cinfo->offset = cinfo->first_block;
+               cinfo->spans = 0;
+       }
+       /* now get the offset the first block should have in the
+        * uncompressed data stream.
+        *
+        * For this magic `18' refer to CRF-3 standard or QIC-80MC,
+        * Rev. K.  
+        */
+       if ((seg_sz - cinfo->offset) > 18) {
+               if (volume->qic113) { /* > revision K */
+                       TRACE(ft_t_data_flow, "New QIC-113 compliance");
+                       cinfo->foffs = GET8(buff, cinfo->offset);
+                       cinfo->offset += sizeof(__s64); 
+               } else {
+                       TRACE(/* ft_t_data_flow */ ft_t_noise, "pre QIC-113 version");
+                       cinfo->foffs   = (__s64)GET4(buff, cinfo->offset);
+                       cinfo->offset += sizeof(__u32); 
+               }
+       }
+       if (cinfo->foffs > volume->size) {
+               TRACE_ABORT(-EIO, ft_t_err, "Inconsistency:\n"
+                           KERN_INFO "offset in current volume: %d\n"
+                           KERN_INFO "size of current volume  : %d",
+                           (int)(cinfo->foffs>>10), (int)(volume->size>>10));
+       }
+       if (cinfo->cmpr_pos + cinfo->count > volume->blk_sz) {
+               TRACE_ABORT(-EIO, ft_t_err, "Inconsistency:\n"
+                           KERN_INFO "block size : %d\n"
+                           KERN_INFO "data record: %d",
+                           volume->blk_sz, cinfo->cmpr_pos + cinfo->count);
+       }
+       DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */, "", cinfo);
+       TRACE_EXIT 0;
+}
+
+/*  This one is called, when a new cluster starts in same segment.
+ *  
+ *  Note: if this is the first cluster in the current segment, we must
+ *  not check whether there are more than 18 bytes available because
+ *  this have already been done in get_cseg() and there may be less
+ *  than 18 bytes available due to header information.
+ * 
+ */
+static void get_next_cluster(cmpr_info *cluster, const __u8 *buff, 
+                            const int seg_sz, const int finish)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (seg_sz - cluster->offset > 18 || cluster->foffs != 0) {
+               cluster->count   = GET2(buff, cluster->offset);
+               cluster->uncmpr  = cluster->count & 0x8000;
+               cluster->count  -= cluster->uncmpr;
+               cluster->offset += sizeof(__u16);
+               cluster->foffs   = 0;
+               if ((cluster->offset + cluster->count) < seg_sz) {
+                       cluster->spans = 0;
+               } else if (cluster->offset + cluster->count == seg_sz) {
+                       cluster->spans = !finish;
+               } else {
+                       /* either an error or a volume written by an 
+                        * old version. If this is a data error, then we'll
+                        * catch it later.
+                        */
+                       TRACE(ft_t_data_flow, "Either error or old volume");
+                       cluster->spans = 1;
+                       cluster->count = seg_sz - cluster->offset;
+               }
+       } else {
+               cluster->count = 0;
+               cluster->spans = 0;
+               cluster->foffs = 0;
+       }
+       DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */ , "", cluster);
+       TRACE_EXIT;
+}
+
+static void zftc_lock(void)
+{
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+       if (!MOD_IN_USE) {
+               MOD_INC_USE_COUNT;
+       }
+#else
+       MOD_INC_USE_COUNT; /*  sets MOD_VISITED and MOD_USED_ONCE,
+                           *  locking is done with can_unload()
+                           */
+#endif
+       keep_module_locked = 1;
+}
+
+/*  this function is needed for zftape_reset_position in zftape-io.c 
+ */
+static void zftc_reset(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       memset((void *)&cseg, '\0', sizeof(cseg));
+       zftc_stats();
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+       if (MOD_IN_USE) {
+               MOD_DEC_USE_COUNT;
+       }
+#endif
+       keep_module_locked = 0;
+       TRACE_EXIT;
+}
+
+static int cmpr_mem_initialized = 0;
+static unsigned int alloc_blksz = 0;
+
+static int zft_allocate_cmpr_mem(unsigned int blksz)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (cmpr_mem_initialized && blksz == alloc_blksz) {
+               TRACE_EXIT 0;
+       }
+       TRACE_CATCH(zft_vmalloc_once(&zftc_wrk_mem, CMPR_WRK_MEM_SIZE),
+                   zftc_cleanup());
+       TRACE_CATCH(zft_vmalloc_always(&zftc_buf, blksz + CMPR_OVERRUN),
+                   zftc_cleanup());
+       alloc_blksz = blksz;
+       TRACE_CATCH(zft_vmalloc_always(&zftc_scratch_buf, blksz+CMPR_OVERRUN),
+                   zftc_cleanup());
+       cmpr_mem_initialized = 1;
+       TRACE_EXIT 0;
+}
+
+static void zftc_cleanup(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       zft_vfree(&zftc_wrk_mem, CMPR_WRK_MEM_SIZE);
+       zft_vfree(&zftc_buf, alloc_blksz + CMPR_OVERRUN);
+       zft_vfree(&zftc_scratch_buf, alloc_blksz + CMPR_OVERRUN);
+       cmpr_mem_initialized = alloc_blksz = 0;
+       TRACE_EXIT;
+}
+
+/*****************************************************************************
+ *                                                                           *
+ *  The following two functions "ftape_compress()" and                       *
+ *  "ftape_uncompress()" are the interface to the actual compression         *
+ *  algorithm (i.e. they are calling the "compress()" function from          *
+ *  the lzrw3 package for now). These routines could quite easily be         *
+ *  changed to adopt another compression algorithm instead of lzrw3,         *
+ *  which currently is used.                                                 *
+ *                                                                           *
+ *****************************************************************************/
+
+/* called by zft_compress_write() to perform the compression. Must
+ * return the size of the compressed data.
+ *
+ * NOTE: The size of the compressed data should not exceed the size of
+ *       the uncompressed data. Most compression algorithms have means
+ *       to store data unchanged if the "compressed" data amount would
+ *       exceed the original one. Mostly this is done by storing some
+ *       flag-bytes in front of the compressed data to indicate if it
+ *       is compressed or not. Thus the worst compression result
+ *       length is the original length plus those flag-bytes.
+ *
+ *       We don't want that, as the QIC-80 standard provides a means
+ *       of marking uncompressed blocks by simply setting bit 15 of
+ *       the compressed block's length. Thus a compessed block can
+ *       have at most a length of 2^15-1 bytes. The QIC-80 standard
+ *       restricts the block-length even further, allowing only 29k -
+ *       6 bytes.
+ *
+ *       Currently, the maximum blocksize used by zftape is 28k.
+ *
+ *       In short: don't exceed the length of the input-package, set
+ *       bit 15 of the compressed size to 1 if you have copied data
+ *       instead of compressing it.
+ */
+static int zft_compress(__u8 *in_buffer, unsigned int in_sz, __u8 *out_buffer)
+{ 
+       __s32 compressed_sz;
+       TRACE_FUN(ft_t_flow);
+       
+
+       lzrw3_compress(COMPRESS_ACTION_COMPRESS, zftc_wrk_mem,
+                      in_buffer, in_sz, out_buffer, &compressed_sz);
+       if (TRACE_LEVEL >= ft_t_info) {
+               /*  the compiler will optimize this away when
+                *  compiled with NO_TRACE_AT_ALL option
+                */
+               TRACE(ft_t_data_flow, "\n"
+                     KERN_INFO "before compression: %d bytes\n"
+                     KERN_INFO "after compresison : %d bytes", 
+                     in_sz, 
+                     (int)(compressed_sz < 0 
+                     ? -compressed_sz : compressed_sz));
+               /*  for statistical purposes
+                */
+               zftc_wr_compressed   += (compressed_sz < 0 
+                                          ? -compressed_sz : compressed_sz);
+               zftc_wr_uncompressed += in_sz;
+       }
+       TRACE_EXIT (int)compressed_sz;
+}
+
+/* called by zft_compress_read() to decompress the data. Must
+ * return the size of the decompressed data for sanity checks
+ * (compared with zft_blk_sz)
+ *
+ * NOTE: Read the note for zft_compress() above!  If bit 15 of the
+ *       parameter in_sz is set, then the data in in_buffer isn't
+ *       compressed, which must be handled by the un-compression
+ *       algorithm. (I changed lzrw3 to handle this.)
+ *
+ *  The parameter max_out_sz is needed to prevent buffer overruns when 
+ *  uncompressing corrupt data.
+ */
+static unsigned int zft_uncompress(__u8 *in_buffer, 
+                                  int in_sz, 
+                                  __u8 *out_buffer,
+                                  unsigned int max_out_sz)
+{ 
+       TRACE_FUN(ft_t_flow);
+       
+       lzrw3_compress(COMPRESS_ACTION_DECOMPRESS, zftc_wrk_mem,
+                      in_buffer, (__s32)in_sz,
+                      out_buffer, (__u32 *)&max_out_sz);
+       
+       if (TRACE_LEVEL >= ft_t_info) {
+               TRACE(ft_t_data_flow, "\n"
+                     KERN_INFO "before decompression: %d bytes\n"
+                     KERN_INFO "after decompression : %d bytes", 
+                     in_sz < 0 ? -in_sz : in_sz,(int)max_out_sz);
+               /*  for statistical purposes
+                */
+               zftc_rd_compressed   += in_sz < 0 ? -in_sz : in_sz;
+               zftc_rd_uncompressed += max_out_sz;
+       }
+       TRACE_EXIT (unsigned int)max_out_sz;
+}
+
+/* print some statistics about the efficiency of the compression to
+ * the kernel log 
+ */
+static void zftc_stats(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (TRACE_LEVEL < ft_t_info) {
+               TRACE_EXIT;
+       }
+       if (zftc_wr_uncompressed != 0) {
+               if (zftc_wr_compressed > (1<<14)) {
+                       TRACE(ft_t_info, "compression statistics (writing):\n"
+                             KERN_INFO " compr./uncmpr.   : %3d %%",
+                             (((zftc_wr_compressed>>10) * 100)
+                              / (zftc_wr_uncompressed>>10)));
+               } else {
+                       TRACE(ft_t_info, "compression statistics (writing):\n"
+                             KERN_INFO " compr./uncmpr.   : %3d %%",
+                             ((zftc_wr_compressed * 100)
+                              / zftc_wr_uncompressed));
+               }
+       }
+       if (zftc_rd_uncompressed != 0) {
+               if (zftc_rd_compressed > (1<<14)) {
+                       TRACE(ft_t_info, "compression statistics (reading):\n"
+                             KERN_INFO " compr./uncmpr.   : %3d %%",
+                             (((zftc_rd_compressed>>10) * 100)
+                              / (zftc_rd_uncompressed>>10)));
+               } else {
+                       TRACE(ft_t_info, "compression statistics (reading):\n"
+                             KERN_INFO " compr./uncmpr.   : %3d %%",
+                             ((zftc_rd_compressed * 100)
+                              / zftc_rd_uncompressed));
+               }
+       }
+       /* only print it once: */
+       zftc_wr_uncompressed = 
+               zftc_wr_compressed  =
+               zftc_rd_uncompressed =
+               zftc_rd_compressed   = 0;
+       TRACE_EXIT;
+}
+
+/* start new compressed block 
+ */
+static int start_new_cseg(cmpr_info *cluster, 
+                         char *dst_buf, 
+                         const zft_position *pos,
+                         const unsigned int blk_sz,
+                         const char *src_buf,
+                         const int this_segs_sz,
+                         const int qic113)
+{
+       int size_left;
+       int cp_cnt;
+       int buf_pos;
+       TRACE_FUN(ft_t_flow);
+
+       size_left = this_segs_sz - sizeof(__u16) - cluster->cmpr_sz;
+       TRACE(ft_t_data_flow,"\n" 
+             KERN_INFO "segment size   : %d\n"
+             KERN_INFO "compressed_sz: %d\n"
+             KERN_INFO "size_left      : %d",
+             this_segs_sz, cluster->cmpr_sz, size_left);
+       if (size_left > 18) { /* start a new cluseter */
+               cp_cnt = cluster->cmpr_sz;
+               cluster->cmpr_sz = 0;
+               buf_pos = cp_cnt + sizeof(__u16);
+               PUT2(dst_buf, 0, buf_pos);
+
+               if (qic113) {
+                       __s64 foffs = pos->volume_pos;
+                       if (cp_cnt) foffs += (__s64)blk_sz;
+
+                       TRACE(ft_t_data_flow, "new style QIC-113 header");
+                       PUT8(dst_buf, buf_pos, foffs);
+                       buf_pos += sizeof(__s64);
+               } else {
+                       __u32 foffs = (__u32)pos->volume_pos;
+                       if (cp_cnt) foffs += (__u32)blk_sz;
+                       
+                       TRACE(ft_t_data_flow, "old style QIC-80MC header");
+                       PUT4(dst_buf, buf_pos, foffs);
+                       buf_pos += sizeof(__u32);
+               }
+       } else if (size_left >= 0) {
+               cp_cnt = cluster->cmpr_sz;
+               cluster->cmpr_sz = 0;
+               buf_pos = cp_cnt + sizeof(__u16);
+               PUT2(dst_buf, 0, buf_pos);  
+               /* zero unused part of segment. */
+               memset(dst_buf + buf_pos, '\0', size_left);
+               buf_pos = this_segs_sz;
+       } else { /* need entire segment and more space */
+               PUT2(dst_buf, 0, 0); 
+               cp_cnt = this_segs_sz - sizeof(__u16);
+               cluster->cmpr_sz  -= cp_cnt;
+               buf_pos = this_segs_sz;
+       }
+       memcpy(dst_buf + sizeof(__u16), src_buf + cluster->cmpr_pos, cp_cnt);
+       cluster->cmpr_pos += cp_cnt;
+       TRACE_EXIT buf_pos;
+}
+
+/* return-value: the number of bytes removed from the user-buffer
+ *               `src_buf' or error code
+ *
+ *  int *write_cnt           : how much actually has been moved to the
+ *                             dst_buf. Need not be initialized when
+ *                             function returns with an error code
+ *                             (negativ return value) 
+ *  __u8 *dst_buf            : kernel space buffer where the has to be
+ *                             copied to. The contents of this buffers
+ *                             goes to a specific segment.
+ *  const int seg_sz         : the size of the segment dst_buf will be
+ *                             copied to.
+ *  const zft_position *pos  : struct containing the coordinates in
+ *                             the current volume (byte position,
+ *                             segment id of current segment etc)
+ *  const zft_volinfo *volume: information about the current volume,
+ *                             size etc.
+ *  const __u8 *src_buf      : user space buffer that contains the
+ *                             data the user wants to be written to
+ *                             tape.
+ *  const int req_len        : the amount of data the user wants to be
+ *                             written to tape.
+ */
+static int zftc_write(int *write_cnt,
+                     __u8 *dst_buf, const int seg_sz,
+                     const __u8 *src_buf, const int req_len,
+                     const zft_position *pos, const zft_volinfo *volume)
+{
+       int req_len_left = req_len;
+       int result;
+       int len_left;
+       int buf_pos_write = pos->seg_byte_pos;
+       TRACE_FUN(ft_t_flow);
+       
+       keep_module_locked = 1;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+       MOD_INC_USE_COUNT; /*  sets MOD_VISITED and MOD_USED_ONCE,
+                           *  locking is done with can_unload()
+                           */
+#else
+       if (!MOD_IN_USE) {
+               MOD_INC_USE_COUNT;
+       }
+#endif
+       /* Note: we do not unlock the module because
+        * there are some values cached in that `cseg' variable.  We
+        * don't don't want to use this information when being
+        * unloaded by kerneld even when the tape is full or when we
+        * cannot allocate enough memory.
+        */
+       if (pos->tape_pos > (volume->size-volume->blk_sz-ZFT_CMPR_OVERHEAD)) {
+               TRACE_EXIT -ENOSPC;
+       }    
+       if (zft_allocate_cmpr_mem(volume->blk_sz) < 0) {
+               /* should we unlock the module? But it shouldn't 
+                * be locked anyway ...
+                */
+               TRACE_EXIT -ENOMEM;
+       }
+       if (buf_pos_write == 0) { /* fill a new segment */
+               *write_cnt = buf_pos_write = start_new_cseg(&cseg,
+                                                           dst_buf,
+                                                           pos,
+                                                           volume->blk_sz,
+                                                           zftc_buf, 
+                                                           seg_sz,
+                                                           volume->qic113);
+               if (cseg.cmpr_sz == 0 && cseg.cmpr_pos != 0) {
+                       req_len_left -= result = volume->blk_sz;
+                       cseg.cmpr_pos  = 0;
+               } else {
+                       result = 0;
+               }
+       } else {
+               *write_cnt = result = 0;
+       }
+       
+       len_left = seg_sz - buf_pos_write;
+       while ((req_len_left > 0) && (len_left > 18)) {
+               /* now we have some size left for a new compressed
+                * block.  We know, that the compression buffer is
+                * empty (else there wouldn't be any space left).  
+                */
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+               if (copy_from_user(zftc_scratch_buf, src_buf + result, 
+                                  volume->blk_sz) != 0) {
+                       TRACE_EXIT -EFAULT;
+               }
+#else
+               TRACE_CATCH(verify_area(VERIFY_READ, src_buf + result, 
+                                       volume->blk_sz),);
+               memcpy_fromfs(zftc_scratch_buf, src_buf + result, 
+                             volume->blk_sz);
+#endif
+               req_len_left -= volume->blk_sz;
+               cseg.cmpr_sz = zft_compress(zftc_scratch_buf, volume->blk_sz, 
+                                           zftc_buf);
+               if (cseg.cmpr_sz < 0) {
+                       cseg.uncmpr = 0x8000;
+                       cseg.cmpr_sz = -cseg.cmpr_sz;
+               } else {
+                       cseg.uncmpr = 0;
+               }
+               /* increment "result" iff we copied the entire
+                * compressed block to the zft_deblock_buf 
+                */
+               len_left -= sizeof(__u16);
+               if (len_left >= cseg.cmpr_sz) {
+                       len_left -= cseg.count = cseg.cmpr_sz;
+                       cseg.cmpr_pos = cseg.cmpr_sz = 0;
+                       result += volume->blk_sz;
+               } else {
+                       cseg.cmpr_sz       -= 
+                               cseg.cmpr_pos =
+                               cseg.count    = len_left;
+                       len_left = 0;
+               }
+               PUT2(dst_buf, buf_pos_write, cseg.uncmpr | cseg.count);
+               buf_pos_write += sizeof(__u16);
+               memcpy(dst_buf + buf_pos_write, zftc_buf, cseg.count);
+               buf_pos_write += cseg.count;
+               *write_cnt    += cseg.count + sizeof(__u16);
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+       }
+       /* erase the remainder of the segment if less than 18 bytes
+        * left (18 bytes is due to the QIC-80 standard) 
+        */
+       if (len_left <= 18) {
+               memset(dst_buf + buf_pos_write, '\0', len_left);
+               (*write_cnt) += len_left;
+       }
+       TRACE(ft_t_data_flow, "returning %d", result);
+       TRACE_EXIT result;
+}   
+
+/* out:
+ *
+ * int *read_cnt: the number of bytes we removed from the zft_deblock_buf
+ *                (result)
+ * int *to_do   : the remaining size of the read-request.
+ *
+ * in:
+ *
+ * char *buff          : buff is the address of the upper part of the user
+ *                       buffer, that hasn't been filled with data yet.
+
+ * int buf_pos_read    : copy of from _ftape_read()
+ * int buf_len_read    : copy of buf_len_rd from _ftape_read()
+ * char *zft_deblock_buf: zft_deblock_buf
+ * unsigned short blk_sz: the block size valid for this volume, may differ
+ *                            from zft_blk_sz.
+ * int finish: if != 0 means that this is the last segment belonging
+ *  to this volume
+ * returns the amount of data actually copied to the user-buffer
+ *
+ * to_do MUST NOT SHRINK except to indicate an EOF. In this case *to_do has to
+ * be set to 0 
+ */
+static int zftc_read (int *read_cnt, 
+                     __u8  *dst_buf, const int to_do, 
+                     const __u8 *src_buf, const int seg_sz, 
+                     const zft_position *pos, const zft_volinfo *volume)
+{          
+       int uncompressed_sz;         
+       int result = 0;
+       int remaining = to_do;
+       TRACE_FUN(ft_t_flow);
+
+       keep_module_locked = 1;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+       MOD_INC_USE_COUNT; /*  sets MOD_VISITED and MOD_USED_ONCE,
+                           *  locking is done with can_unload()
+                           */
+#else
+       if (!MOD_IN_USE) {
+               MOD_INC_USE_COUNT;
+       }
+#endif
+       TRACE_CATCH(zft_allocate_cmpr_mem(volume->blk_sz),);
+       if (pos->seg_byte_pos == 0) {
+               /* new segment just read
+                */
+               TRACE_CATCH(get_cseg(&cseg, src_buf, seg_sz, volume),
+                           *read_cnt = 0);
+               memcpy(zftc_buf + cseg.cmpr_pos, src_buf + sizeof(__u16), 
+                      cseg.count);
+               cseg.cmpr_pos += cseg.count;
+               *read_cnt      = cseg.offset;
+               DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */, "", &cseg);
+       } else {
+               *read_cnt = 0;
+       }
+       /* loop and uncompress until user buffer full or
+        * deblock-buffer empty 
+        */
+       TRACE(ft_t_data_flow, "compressed_sz: %d, compos : %d, *read_cnt: %d",
+             cseg.cmpr_sz, cseg.cmpr_pos, *read_cnt);
+       while ((cseg.spans == 0) && (remaining > 0)) {
+               if (cseg.cmpr_pos  != 0) { /* cmpr buf is not empty */
+                       uncompressed_sz = 
+                               zft_uncompress(zftc_buf,
+                                              cseg.uncmpr == 0x8000 ?
+                                              -cseg.cmpr_pos : cseg.cmpr_pos,
+                                              zftc_scratch_buf,
+                                              volume->blk_sz);
+                       if (uncompressed_sz != volume->blk_sz) {
+                               *read_cnt = 0;
+                               TRACE_ABORT(-EIO, ft_t_warn,
+                                     "Uncompressed blk (%d) != blk size (%d)",
+                                     uncompressed_sz, volume->blk_sz);
+                       }       
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+                       if (copy_to_user(dst_buf + result, 
+                                        zftc_scratch_buf, 
+                                        uncompressed_sz) != 0 ) {
+                               TRACE_EXIT -EFAULT;
+                       }
+#else
+                       memcpy_tofs(dst_buf + result, zftc_scratch_buf, 
+                                   uncompressed_sz);
+#endif
+                       remaining      -= uncompressed_sz;
+                       result     += uncompressed_sz;
+                       cseg.cmpr_pos  = 0;
+               }                                              
+               if (remaining > 0) {
+                       get_next_cluster(&cseg, src_buf, seg_sz, 
+                                        volume->end_seg == pos->seg_pos);
+                       if (cseg.count != 0) {
+                               memcpy(zftc_buf, src_buf + cseg.offset,
+                                      cseg.count);
+                               cseg.cmpr_pos = cseg.count;
+                               cseg.offset  += cseg.count;
+                               *read_cnt += cseg.count + sizeof(__u16);
+                       } else {
+                               remaining = 0;
+                       }
+               }
+               TRACE(ft_t_data_flow, "\n" 
+                     KERN_INFO "compressed_sz: %d\n"
+                     KERN_INFO "compos       : %d\n"
+                     KERN_INFO "*read_cnt    : %d",
+                     cseg.cmpr_sz, cseg.cmpr_pos, *read_cnt);
+       }
+       if (seg_sz - cseg.offset <= 18) {
+               *read_cnt += seg_sz - cseg.offset;
+               TRACE(ft_t_data_flow, "expanding read cnt to: %d", *read_cnt);
+       }
+       TRACE(ft_t_data_flow, "\n"
+             KERN_INFO "segment size   : %d\n"
+             KERN_INFO "read count     : %d\n"
+             KERN_INFO "buf_pos_read   : %d\n"
+             KERN_INFO "remaining      : %d",
+               seg_sz, *read_cnt, pos->seg_byte_pos, 
+               seg_sz - *read_cnt - pos->seg_byte_pos);
+       TRACE(ft_t_data_flow, "returning: %d", result);
+       TRACE_EXIT result;
+}                
+
+/* seeks to the new data-position. Reads sometimes a segment.
+ *  
+ * start_seg and end_seg give the boundaries of the current volume
+ * blk_sz is the blk_sz of the current volume as stored in the
+ * volume label
+ *
+ * We don't allow blocksizes less than 1024 bytes, therefore we don't need
+ * a 64 bit argument for new_block_pos.
+ */
+
+static int seek_in_segment(const unsigned int to_do, cmpr_info  *c_info,
+                          const char *src_buf, const int seg_sz, 
+                          const int seg_pos, const zft_volinfo *volume);
+static int slow_seek_forward_until_error(const unsigned int distance,
+                                        cmpr_info *c_info, zft_position *pos, 
+                                        const zft_volinfo *volume, __u8 *buf);
+static int search_valid_segment(unsigned int segment,
+                               const unsigned int end_seg,
+                               const unsigned int max_foffs,
+                               zft_position *pos, cmpr_info *c_info,
+                               const zft_volinfo *volume, __u8 *buf);
+static int slow_seek_forward(unsigned int dest, cmpr_info *c_info,
+                            zft_position *pos, const zft_volinfo *volume,
+                            __u8 *buf);
+static int compute_seg_pos(unsigned int dest, zft_position *pos,
+                          const zft_volinfo *volume);
+
+#define ZFT_SLOW_SEEK_THRESHOLD  10 /* segments */
+#define ZFT_FAST_SEEK_MAX_TRIALS 10 /* times */
+#define ZFT_FAST_SEEK_BACKUP     10 /* segments */
+
+static int zftc_seek(unsigned int new_block_pos,
+                    zft_position *pos, const zft_volinfo *volume, __u8 *buf)
+{
+       unsigned int dest;
+       int limit;
+       int distance;
+       int result = 0;
+       int seg_dist;
+       int new_seg;
+       int old_seg = 0;
+       int fast_seek_trials = 0;
+       TRACE_FUN(ft_t_flow);
+
+       keep_module_locked = 1;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+       MOD_INC_USE_COUNT; /*  sets MOD_VISITED and MOD_USED_ONCE,
+                           *  locking is done with can_unload()
+                           */
+#else
+       if (!MOD_IN_USE) {
+               MOD_INC_USE_COUNT;
+       }
+#endif
+       if (new_block_pos == 0) {
+               pos->seg_pos      = volume->start_seg;
+               pos->seg_byte_pos = 0;
+               pos->volume_pos   = 0;
+               zftc_reset();
+               TRACE_EXIT 0;
+       }
+       dest = new_block_pos * (volume->blk_sz >> 10);
+       distance = dest - (pos->volume_pos >> 10);
+       while (distance != 0) {
+               seg_dist = compute_seg_pos(dest, pos, volume);
+               TRACE(ft_t_noise, "\n"
+                     KERN_INFO "seg_dist: %d\n"
+                     KERN_INFO "distance: %d\n"
+                     KERN_INFO "dest    : %d\n"
+                     KERN_INFO "vpos    : %d\n"
+                     KERN_INFO "seg_pos : %d\n"
+                     KERN_INFO "trials  : %d",
+                     seg_dist, distance, dest,
+                     (unsigned int)(pos->volume_pos>>10), pos->seg_pos,
+                     fast_seek_trials);
+               if (distance > 0) {
+                       if (seg_dist < 0) {
+                               TRACE(ft_t_bug, "BUG: distance %d > 0, "
+                                     "segment difference %d < 0",
+                                     distance, seg_dist);
+                               result = -EIO;
+                               break;
+                       }
+                       new_seg = pos->seg_pos + seg_dist;
+                       if (new_seg > volume->end_seg) {
+                               new_seg = volume->end_seg;
+                       }
+                       if (old_seg == new_seg || /* loop */
+                           seg_dist <= ZFT_SLOW_SEEK_THRESHOLD ||
+                           fast_seek_trials >= ZFT_FAST_SEEK_MAX_TRIALS) {
+                               TRACE(ft_t_noise, "starting slow seek:\n"
+                                  KERN_INFO "fast seek failed too often: %s\n"
+                                  KERN_INFO "near target position      : %s\n"
+                                  KERN_INFO "looping between two segs  : %s",
+                                     (fast_seek_trials >= 
+                                      ZFT_FAST_SEEK_MAX_TRIALS)
+                                     ? "yes" : "no",
+                                     (seg_dist <= ZFT_SLOW_SEEK_THRESHOLD) 
+                                     ? "yes" : "no",
+                                     (old_seg == new_seg)
+                                     ? "yes" : "no");
+                               result = slow_seek_forward(dest, &cseg, 
+                                                          pos, volume, buf);
+                               break;
+                       }
+                       old_seg = new_seg;
+                       limit = volume->end_seg;
+                       fast_seek_trials ++;
+                       for (;;) {
+                               result = search_valid_segment(new_seg, limit,
+                                                             volume->size,
+                                                             pos, &cseg,
+                                                             volume, buf);
+                               if (result == 0 || result == -EINTR) {
+                                       break;
+                               }
+                               if (new_seg == volume->start_seg) {
+                                       result = -EIO; /* set errror 
+                                                       * condition
+                                                       */
+                                       break;
+                               }
+                               limit    = new_seg;
+                               new_seg -= ZFT_FAST_SEEK_BACKUP;
+                               if (new_seg < volume->start_seg) {
+                                       new_seg = volume->start_seg;
+                               }
+                       }
+                       if (result < 0) {
+                               TRACE(ft_t_warn,
+                                     "Couldn't find a readable segment");
+                               break;
+                       }
+               } else /* if (distance < 0) */ {
+                       if (seg_dist > 0) {
+                               TRACE(ft_t_bug, "BUG: distance %d < 0, "
+                                     "segment difference %d >0",
+                                     distance, seg_dist);
+                               result = -EIO;
+                               break;
+                       }
+                       new_seg = pos->seg_pos + seg_dist;
+                       if (fast_seek_trials > 0 && seg_dist == 0) {
+                               /* this avoids sticking to the same
+                                * segment all the time. On the other hand:
+                                * if we got here for the first time, and the
+                                * deblock_buffer still contains a valid
+                                * segment, then there is no need to skip to 
+                                * the previous segment if the desired position
+                                * is inside this segment.
+                                */
+                               new_seg --;
+                       }
+                       if (new_seg < volume->start_seg) {
+                               new_seg = volume->start_seg;
+                       }
+                       limit   = pos->seg_pos;
+                       fast_seek_trials ++;
+                       for (;;) {
+                               result = search_valid_segment(new_seg, limit,
+                                                             pos->volume_pos,
+                                                             pos, &cseg,
+                                                             volume, buf);
+                               if (result == 0 || result == -EINTR) {
+                                       break;
+                               }
+                               if (new_seg == volume->start_seg) {
+                                       result = -EIO; /* set errror 
+                                                       * condition
+                                                       */
+                                       break;
+                               }
+                               limit    = new_seg;
+                               new_seg -= ZFT_FAST_SEEK_BACKUP;
+                               if (new_seg < volume->start_seg) {
+                                       new_seg = volume->start_seg;
+                               }
+                       }
+                       if (result < 0) {
+                               TRACE(ft_t_warn,
+                                     "Couldn't find a readable segment");
+                               break;
+                       }
+               }
+               distance = dest - (pos->volume_pos >> 10);
+       }
+       TRACE_EXIT result;
+}
+
+
+/*  advance inside the given segment at most to_do bytes.
+ *  of kilobytes moved
+ */
+
+static int seek_in_segment(const unsigned int to_do,
+                          cmpr_info  *c_info,
+                          const char *src_buf, 
+                          const int seg_sz, 
+                          const int seg_pos,
+                          const zft_volinfo *volume)
+{
+       int result = 0;
+       int blk_sz = volume->blk_sz >> 10;
+       int remaining = to_do;
+       TRACE_FUN(ft_t_flow);
+
+       if (c_info->offset == 0) {
+               /* new segment just read
+                */
+               TRACE_CATCH(get_cseg(c_info, src_buf, seg_sz, volume),);
+               c_info->cmpr_pos += c_info->count;
+               DUMP_CMPR_INFO(ft_t_noise, "", c_info);
+       }
+       /* loop and uncompress until user buffer full or
+        * deblock-buffer empty 
+        */
+       TRACE(ft_t_noise, "compressed_sz: %d, compos : %d",
+             c_info->cmpr_sz, c_info->cmpr_pos);
+       while (c_info->spans == 0 && remaining > 0) {
+               if (c_info->cmpr_pos  != 0) { /* cmpr buf is not empty */
+                       result       += blk_sz;
+                       remaining    -= blk_sz;
+                       c_info->cmpr_pos = 0;
+               }
+               if (remaining > 0) {
+                       get_next_cluster(c_info, src_buf, seg_sz, 
+                                        volume->end_seg == seg_pos);
+                       if (c_info->count != 0) {
+                               c_info->cmpr_pos = c_info->count;
+                               c_info->offset  += c_info->count;
+                       } else {
+                               break;
+                       }
+               }
+               /*  Allow escape from this loop on signal!
+                */
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+               DUMP_CMPR_INFO(ft_t_noise, "", c_info);
+               TRACE(ft_t_noise, "to_do: %d", remaining);
+       }
+       if (seg_sz - c_info->offset <= 18) {
+               c_info->offset = seg_sz;
+       }
+       TRACE(ft_t_noise, "\n"
+             KERN_INFO "segment size   : %d\n"
+             KERN_INFO "buf_pos_read   : %d\n"
+             KERN_INFO "remaining      : %d",
+             seg_sz, c_info->offset,
+             seg_sz - c_info->offset);
+       TRACE_EXIT result;
+}                
+
+static int slow_seek_forward_until_error(const unsigned int distance,
+                                        cmpr_info *c_info,
+                                        zft_position *pos, 
+                                        const zft_volinfo *volume,
+                                        __u8 *buf)
+{
+       unsigned int remaining = distance;
+       int seg_sz;
+       int seg_pos;
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       seg_pos = pos->seg_pos;
+       do {
+               TRACE_CATCH(seg_sz = zft_fetch_segment(seg_pos, buf, 
+                                                      FT_RD_AHEAD),);
+               /* now we have the contents of the actual segment in
+                * the deblock buffer
+                */
+               TRACE_CATCH(result = seek_in_segment(remaining, c_info, buf,
+                                                    seg_sz, seg_pos,volume),);
+               remaining        -= result;
+               pos->volume_pos  += result<<10;
+               pos->seg_pos      = seg_pos;
+               pos->seg_byte_pos = c_info->offset;
+               seg_pos ++;
+               if (seg_pos <= volume->end_seg && c_info->offset == seg_sz) {
+                       pos->seg_pos ++;
+                       pos->seg_byte_pos = 0;
+                       c_info->offset = 0;
+               }
+               /*  Allow escape from this loop on signal!
+                */
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+               TRACE(ft_t_noise, "\n"
+                     KERN_INFO "remaining:  %d\n"
+                     KERN_INFO "seg_pos:    %d\n"
+                     KERN_INFO "end_seg:    %d\n"
+                     KERN_INFO "result:     %d",
+                     remaining, seg_pos, volume->end_seg, result);  
+       } while (remaining > 0 && seg_pos <= volume->end_seg);
+       TRACE_EXIT 0;
+}
+
+/* return segment id of next segment containing valid data, -EIO otherwise
+ */
+static int search_valid_segment(unsigned int segment,
+                               const unsigned int end_seg,
+                               const unsigned int max_foffs,
+                               zft_position *pos,
+                               cmpr_info *c_info,
+                               const zft_volinfo *volume,
+                               __u8 *buf)
+{
+       cmpr_info tmp_info;
+       int seg_sz;
+       TRACE_FUN(ft_t_flow);
+       
+       memset(&tmp_info, 0, sizeof(cmpr_info));
+       while (segment <= end_seg) {
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+               TRACE(ft_t_noise,
+                     "Searching readable segment between %d and %d",
+                     segment, end_seg);
+               seg_sz = zft_fetch_segment(segment, buf, FT_RD_AHEAD);
+               if ((seg_sz > 0) &&
+                   (get_cseg (&tmp_info, buf, seg_sz, volume) >= 0) &&
+                   (tmp_info.foffs != 0 || segment == volume->start_seg)) {
+                       if ((tmp_info.foffs>>10) > max_foffs) {
+                               TRACE_ABORT(-EIO, ft_t_noise, "\n"
+                                           KERN_INFO "cseg.foff: %d\n"
+                                           KERN_INFO "dest     : %d",
+                                           (int)(tmp_info.foffs >> 10),
+                                           max_foffs);
+                       }
+                       DUMP_CMPR_INFO(ft_t_noise, "", &tmp_info);
+                       *c_info           = tmp_info;
+                       pos->seg_pos      = segment;
+                       pos->volume_pos   = c_info->foffs;
+                       pos->seg_byte_pos = c_info->offset;
+                       TRACE(ft_t_noise, "found segment at %d", segment);
+                       TRACE_EXIT 0;
+               }
+               segment++;
+       }
+       TRACE_EXIT -EIO;
+}
+
+static int slow_seek_forward(unsigned int dest,
+                            cmpr_info *c_info,
+                            zft_position *pos,
+                            const zft_volinfo *volume,
+                            __u8 *buf)
+{
+       unsigned int distance;
+       int result = 0;
+       TRACE_FUN(ft_t_flow);
+               
+       distance = dest - (pos->volume_pos >> 10);
+       while ((distance > 0) &&
+              (result = slow_seek_forward_until_error(distance,
+                                                      c_info,
+                                                      pos,
+                                                      volume,
+                                                      buf)) < 0) {
+               if (result == -EINTR) {
+                       break;
+               }
+               TRACE(ft_t_noise, "seg_pos: %d", pos->seg_pos);
+               /* the failing segment is either pos->seg_pos or
+                * pos->seg_pos + 1. There is no need to further try
+                * that segment, because ftape_read_segment() already
+                * has tried very much to read it. So we start with
+                * following segment, which is pos->seg_pos + 1
+                */
+               if(search_valid_segment(pos->seg_pos+1, volume->end_seg, dest,
+                                       pos, c_info,
+                                       volume, buf) < 0) {
+                       TRACE(ft_t_noise, "search_valid_segment() failed");
+                       result = -EIO;
+                       break;
+               }
+               distance = dest - (pos->volume_pos >> 10);
+               result = 0;
+               TRACE(ft_t_noise, "segment: %d", pos->seg_pos);
+               /* found valid segment, retry the seek */
+       }
+       TRACE_EXIT result;
+}
+
+static int compute_seg_pos(const unsigned int dest,
+                          zft_position *pos,
+                          const zft_volinfo *volume)
+{
+       int segment;
+       int distance = dest - (pos->volume_pos >> 10);
+       unsigned int raw_size;
+       unsigned int virt_size;
+       unsigned int factor;
+       TRACE_FUN(ft_t_flow);
+
+       if (distance >= 0) {
+               raw_size  = volume->end_seg - pos->seg_pos + 1;
+               virt_size = ((unsigned int)(volume->size>>10) 
+                            - (unsigned int)(pos->volume_pos>>10)
+                            + FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS - 1);
+               virt_size /= FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS;
+               if (virt_size == 0 || raw_size == 0) {
+                       TRACE_EXIT 0;
+               }
+               if (raw_size >= (1<<25)) {
+                       factor = raw_size/(virt_size>>7);
+               } else {
+                       factor = (raw_size<<7)/virt_size;
+               }
+               segment = distance/(FT_SECTORS_PER_SEGMENT-FT_ECC_SECTORS);
+               segment = (segment * factor)>>7;
+       } else {
+               raw_size  = pos->seg_pos - volume->start_seg + 1;
+               virt_size = ((unsigned int)(pos->volume_pos>>10)
+                            + FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS - 1);
+               virt_size /= FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS;
+               if (virt_size == 0 || raw_size == 0) {
+                       TRACE_EXIT 0;
+               }
+               if (raw_size >= (1<<25)) {
+                       factor = raw_size/(virt_size>>7);
+               } else {
+                       factor = (raw_size<<7)/virt_size;
+               }
+               segment = distance/(FT_SECTORS_PER_SEGMENT-FT_ECC_SECTORS);
+       }
+       TRACE(ft_t_noise, "factor: %d/%d", factor, 1<<7);
+       TRACE_EXIT segment;
+}
+
+static struct zft_cmpr_ops cmpr_ops = {
+       zftc_write,
+       zftc_read,
+       zftc_seek,
+       zftc_lock,
+       zftc_reset,
+       zftc_cleanup
+};
+
+int zft_compressor_init(void)
+{
+       TRACE_FUN(ft_t_flow);
+       
+#ifdef MODULE
+       printk(KERN_INFO "zftape compressor v1.00a 970514 for " FTAPE_VERSION "\n");
+        if (TRACE_LEVEL >= ft_t_info) {
+               printk(
+KERN_INFO "(c) 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n"
+KERN_INFO "Compressor for zftape (lzrw3 algorithm)\n"
+KERN_INFO "Compiled for kernel version %s"
+#ifdef MODVERSIONS
+               " with versioned symbols"
+#endif
+               "\n", UTS_RELEASE);
+        }
+#else /* !MODULE */
+       /* print a short no-nonsense boot message */
+       printk("zftape compressor v1.00a 970514 for Linux " UTS_RELEASE "\n");
+       printk("For use with " FTAPE_VERSION "\n");
+#endif /* MODULE */
+       TRACE(ft_t_info, "zft_compressor_init @ 0x%p", zft_compressor_init);
+       TRACE(ft_t_info, "installing compressor for zftape ...");
+       TRACE_CATCH(zft_cmpr_register(&cmpr_ops),);
+       TRACE_EXIT 0;
+}
+
+
+#ifdef MODULE
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+MODULE_AUTHOR(
+       "(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de");
+MODULE_DESCRIPTION(
+"Compression routines for zftape. Uses the lzrw3 algorithm by Ross Williams");
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+static int can_unload(void)
+{
+       return keep_module_locked ? -EBUSY : 0;
+}
+#endif
+
+/* Called by modules package when installing the driver
+ */
+int init_module(void)
+{
+       int result;
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,1,85)
+# if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+       register_symtab(0); /* remove global ftape symbols */
+# else
+       if (!mod_member_present(&__this_module, can_unload))
+               return -EBUSY;
+       __this_module.can_unload = can_unload;
+       EXPORT_NO_SYMBOLS;
+# endif
+#endif
+       result = zft_compressor_init();
+       keep_module_locked = 0;
+       return result;
+}
+
+/* Called by modules package when removing the driver 
+ */
+void cleanup_module(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (zft_cmpr_unregister() != &cmpr_ops) {
+               TRACE(ft_t_info, "failed");
+       } else {
+               TRACE(ft_t_info, "successful");
+       }
+       zftc_cleanup();
+        printk(KERN_INFO "zft-compressor successfully unloaded.\n");
+       TRACE_EXIT;
+}
+#endif /* MODULE */
diff --git a/drivers/char/ftape/compressor/zftape-compress.h b/drivers/char/ftape/compressor/zftape-compress.h
new file mode 100644 (file)
index 0000000..f200741
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef _ZFTAPE_COMPRESS_H
+#define _ZFTAPE_COMPRESS_H
+/*
+ *      Copyright (c) 1994-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/compressor/zftape-compress.h,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/10/05 19:12:32 $
+ *
+ * This file contains macros and definitions for zftape's
+ * builtin compression code.
+ *
+ */
+
+#include "../zftape/zftape-buffers.h"
+#include "../zftape/zftape-vtbl.h"
+#include "../compressor/lzrw3.h"
+
+/* CMPR_WRK_MEM_SIZE gives the size of the compression wrk_mem */
+/* I got these out of lzrw3.c */
+#define U(X)            ((__u32) X)
+#define SIZE_P_BYTE     (U(sizeof(__u8 *)))
+#define ALIGNMENT_FUDGE (U(16))
+
+#define CMPR_WRK_MEM_SIZE (U(4096)*(SIZE_P_BYTE) + ALIGNMENT_FUDGE)
+
+/* the maximum number of bytes the size of the "compressed" data can
+ * exceed the uncompressed data. As it is quite useless to compress
+ * data twice it is sometimes the case that it is more efficient to
+ * copy a block of data but to feed it to the "compression"
+ * algorithm. In this case there are some flag bytes or the like
+ * proceding the "compressed" data.  THAT MUST NOT BE THE CASE for the
+ * algorithm we use for this driver. Instead, the high bit 15 of
+ * compressed_size:
+ *
+ * compressed_size = ftape_compress()
+ *
+ * must be set in such a case.
+ *
+ * Nevertheless, it might also be as for lzrw3 that there is an
+ * "intermediate" overrun that exceeds the amount of the compressed
+ * data that is actually produced. During the algorithm we need in the
+ * worst case MAX_CMP_GROUP bytes more than the input-size.
+ */
+#define MAX_CMP_GROUP (2+16*2) /* from lzrw3.c */
+
+#define CMPR_OVERRUN      MAX_CMP_GROUP /* during compression */
+
+/****************************************************/
+
+#define     CMPR_BUFFER_SIZE (MAX_BLOCK_SIZE + CMPR_OVERRUN)
+
+/* the compression map stores the byte offset compressed blocks within
+ * the current volume for catridges with format code 2,3 and 5
+ * (and old versions of zftape) and the offset measured in kilobytes for
+ * format code 4 and 6. This gives us a possible max. size of a 
+ * compressed volume of 1024*4GIG which should be enough.
+ */
+typedef __u32 CmprMap;
+
+/* globals 
+ */
+
+/* exported functions
+ */
+
+#endif /* _ZFTAPE_COMPRESS_H */
diff --git a/drivers/char/ftape/ecc.c b/drivers/char/ftape/ecc.c
deleted file mode 100644 (file)
index 5c7d86b..0000000
+++ /dev/null
@@ -1,893 +0,0 @@
-/* Yo, Emacs! we're -*- Linux-C -*-
- *
- *      Copyright (c) 1993 Ning and David Mosberger.
- *
- * This is based on code originally written by Bas Laarhoven (bas@vimec.nl)
- * and David L. Brown, Jr., and incorporates improvements suggested by
- * Kai Harrekilde-Petersen.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
- * USA.
- *
- * $Source: /home/bas/distr/ftape-2.03b/RCS/ecc.c,v $
- * $Author: bas $
- *
- * $Revision: 1.32 $
- * $Date: 1995/04/22 07:30:15 $
- * $State: Beta $
- *
- *      This file contains the Reed-Solomon error correction code 
- *      for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/ftape.h>
-#include <stdio.h>
-#include <sys/errno.h>
-
-#include "tracing.h"
-#include "ecc.h"
-
-/*
- * Machines that are big-endian should define macro BIG_ENDIAN.
- * Unfortunately, there doesn't appear to be a standard include
- * file that works for all OSs.
- */
-
-#if defined(__sparc__) || defined(__hppa)
-#define BIG_ENDIAN
-#endif                         /* __sparc__ || __hppa */
-
-#if defined(__mips__)
-#error Find a smart way to determine the Endianness of the MIPS CPU
-#endif
-
-#ifdef TEST
-
-#undef TRACE()
-#undef TRACE_()
-#undef TRACE()
-#undef TRACEi()
-#undef TRACElx()
-#undef TRACE_FUN()
-#undef TRACE_EXIT
-#define printk  printf
-#define TRACE_FUN( level, name) char __fun[] = name
-#define TRACE_EXIT
-#define TRACE_(l,m) { if (ftape_ecc_tracing >= (l) && (l) <= TOP_LEVEL) { \
-    printk( "[%03d] " __FILE__ " (%s) - ", (int)ftape_trace_id++, __fun); \
-    m;  } }
-#define TRACE(l,m) TRACE_(l,printk(m".\n"))
-#define TRACEi(l,m,i) TRACE_(l,printk(m" %d.\n",i))
-#define TRACElx(l,m,i) TRACE_(l,printk(m" 0x%08lx.\n",i))
-
-int ftape_ecc_tracing = 1;
-unsigned char ftape_trace_id = 0;
-
-#endif                         /* TEST */
-
-/*
- * Notice: to minimize the potential for confusion, we use r to
- *         denote the independent variable of the polynomials
- *         in the Galois Field GF(2^8).  We reserve x for polynomials
- *         that that have coefficients in GF(2^8).
- *         
- * The Galois Field in which coefficient arithmetic is performed are
- * the polynomials over Z_2 (i.e., 0 and 1) modulo the irreducible
- * polynomial f(r), where f(r)=r^8 + r^7 + r^2 + r + 1.  A polynomial
- * is represented as a byte with the MSB as the coefficient of r^7 and
- * the LSB as the coefficient of r^0.  For example, the binary
- * representation of f(x) is 0x187 (of course, this doesn't fit into 8
- * bits).  In this field, the polynomial r is a primitive element.
- * That is, r^i with i in 0,...,255 enumerates all elements in the
- * field.
- *
- * The generator polynomial for the QIC-80 ECC is
- *
- *      g(x) = x^3 + r^105*x^2 + r^105*x + 1
- *
- * which can be factored into:
- *
- *      g(x) = (x-r^-1)(x-r^0)(x-r^1)
- *
- * the byte representation of the coefficients are:
- *
- *      r^105 = 0xc0
- *      r^-1  = 0xc3
- *      r^0   = 0x01
- *      r^1   = 0x02
- *
- * Notice that r^-1 = r^254 as exponent arithmetic is performed
- * modulo 2^8-1 = 255.
- *
- * For more information on Galois Fields and Reed-Solomon codes,
- * refer to any good book.  I found _An Introduction to Error
- * Correcting Codes with Applications_ by S. A. Vanstone and
- * P. C. van Oorschot to be a good introduction into the former.
- * _CODING THEORY: The Essentials_ I found very useful for its
- * concise description of Reed-Solomon encoding/decoding.
- *
- */
-
-typedef unsigned char Matrix[3][3];
-
-/*
- * gfpow[] is defined such that gfpow[i] returns r^i if
- * i is in the range [0..255].
- */
-static const unsigned char gfpow[] =
-{
-       0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
-       0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4,
-       0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb,
-       0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd,
-       0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31,
-       0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67,
-       0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc,
-       0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b,
-       0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4,
-       0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26,
-       0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21,
-       0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba,
-       0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30,
-       0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0,
-       0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3,
-       0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a,
-       0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9,
-       0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44,
-       0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef,
-       0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85,
-       0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6,
-       0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf,
-       0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff,
-       0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58,
-       0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a,
-       0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24,
-       0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8,
-       0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64,
-       0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2,
-       0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda,
-       0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77,
-       0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3, 0x01
-};
-
-/*
- * This is a log table.  That is, gflog[r^i] returns i (modulo f(r)).
- * gflog[0] is undefined and the first element is therefore not valid.
- */
-static const unsigned char gflog[256] =
-{
-       0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a,
-       0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a,
-       0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1,
-       0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3,
-       0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83,
-       0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4,
-       0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35,
-       0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38,
-       0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70,
-       0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48,
-       0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24,
-       0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15,
-       0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f,
-       0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10,
-       0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7,
-       0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b,
-       0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08,
-       0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a,
-       0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91,
-       0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb,
-       0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2,
-       0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf,
-       0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52,
-       0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86,
-       0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc,
-       0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc,
-       0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8,
-       0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44,
-       0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1,
-       0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97,
-       0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5,
-       0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7
-};
-
-/*
- * This is a multiplication table for the factor
- * 0xc0 (i.e., r^105 (modulo f(r)).
- * gfmul_c0[f] returns r^105 * f(r) (modulo f(r)).
- */
-static const unsigned char gfmul_c0[256] =
-{
-       0x00, 0xc0, 0x07, 0xc7, 0x0e, 0xce, 0x09, 0xc9,
-       0x1c, 0xdc, 0x1b, 0xdb, 0x12, 0xd2, 0x15, 0xd5,
-       0x38, 0xf8, 0x3f, 0xff, 0x36, 0xf6, 0x31, 0xf1,
-       0x24, 0xe4, 0x23, 0xe3, 0x2a, 0xea, 0x2d, 0xed,
-       0x70, 0xb0, 0x77, 0xb7, 0x7e, 0xbe, 0x79, 0xb9,
-       0x6c, 0xac, 0x6b, 0xab, 0x62, 0xa2, 0x65, 0xa5,
-       0x48, 0x88, 0x4f, 0x8f, 0x46, 0x86, 0x41, 0x81,
-       0x54, 0x94, 0x53, 0x93, 0x5a, 0x9a, 0x5d, 0x9d,
-       0xe0, 0x20, 0xe7, 0x27, 0xee, 0x2e, 0xe9, 0x29,
-       0xfc, 0x3c, 0xfb, 0x3b, 0xf2, 0x32, 0xf5, 0x35,
-       0xd8, 0x18, 0xdf, 0x1f, 0xd6, 0x16, 0xd1, 0x11,
-       0xc4, 0x04, 0xc3, 0x03, 0xca, 0x0a, 0xcd, 0x0d,
-       0x90, 0x50, 0x97, 0x57, 0x9e, 0x5e, 0x99, 0x59,
-       0x8c, 0x4c, 0x8b, 0x4b, 0x82, 0x42, 0x85, 0x45,
-       0xa8, 0x68, 0xaf, 0x6f, 0xa6, 0x66, 0xa1, 0x61,
-       0xb4, 0x74, 0xb3, 0x73, 0xba, 0x7a, 0xbd, 0x7d,
-       0x47, 0x87, 0x40, 0x80, 0x49, 0x89, 0x4e, 0x8e,
-       0x5b, 0x9b, 0x5c, 0x9c, 0x55, 0x95, 0x52, 0x92,
-       0x7f, 0xbf, 0x78, 0xb8, 0x71, 0xb1, 0x76, 0xb6,
-       0x63, 0xa3, 0x64, 0xa4, 0x6d, 0xad, 0x6a, 0xaa,
-       0x37, 0xf7, 0x30, 0xf0, 0x39, 0xf9, 0x3e, 0xfe,
-       0x2b, 0xeb, 0x2c, 0xec, 0x25, 0xe5, 0x22, 0xe2,
-       0x0f, 0xcf, 0x08, 0xc8, 0x01, 0xc1, 0x06, 0xc6,
-       0x13, 0xd3, 0x14, 0xd4, 0x1d, 0xdd, 0x1a, 0xda,
-       0xa7, 0x67, 0xa0, 0x60, 0xa9, 0x69, 0xae, 0x6e,
-       0xbb, 0x7b, 0xbc, 0x7c, 0xb5, 0x75, 0xb2, 0x72,
-       0x9f, 0x5f, 0x98, 0x58, 0x91, 0x51, 0x96, 0x56,
-       0x83, 0x43, 0x84, 0x44, 0x8d, 0x4d, 0x8a, 0x4a,
-       0xd7, 0x17, 0xd0, 0x10, 0xd9, 0x19, 0xde, 0x1e,
-       0xcb, 0x0b, 0xcc, 0x0c, 0xc5, 0x05, 0xc2, 0x02,
-       0xef, 0x2f, 0xe8, 0x28, 0xe1, 0x21, 0xe6, 0x26,
-       0xf3, 0x33, 0xf4, 0x34, 0xfd, 0x3d, 0xfa, 0x3a
-};
-
-
-/*
- * Returns V modulo 255 provided V is in the range -255,-254,...,509.
- */
-static inline unsigned char mod255(int v)
-{
-       if (v > 0) {
-               if (v < 255) {
-                       return v;
-               } else {
-                       return v - 255;
-               }
-       } else {
-               return v + 255;
-       }
-}
-
-
-/*
- * Add two numbers in the field.  Addition in this field is
- * equivalent to a bit-wise exclusive OR operation---subtraction
- * is therefore identical to addition.
- */
-static inline unsigned char gfadd(unsigned char a, unsigned char b)
-{
-       return a ^ b;
-}
-
-
-/*
- * Add two vectors of numbers in the field.  Each byte in A and B get
- * added individually.
- */
-static inline unsigned long gfadd_long(unsigned long a, unsigned long b)
-{
-       return a ^ b;
-}
-
-
-/*
- * Multiply two numbers in the field:
- */
-static inline unsigned char gfmul(unsigned char a, unsigned char b)
-{
-       if (a && b) {
-               return gfpow[mod255(gflog[a] + gflog[b])];
-       } else {
-               return 0;
-       }
-}
-
-
-/*
- * Just like gfmul, except we have already looked up the log
- * of the second number.
- */
-static inline unsigned char gfmul_exp(unsigned char a, int b)
-{
-       if (a) {
-               return gfpow[mod255(gflog[a] + b)];
-       } else {
-               return 0;
-       }
-}
-
-
-/*
- * Just like gfmul_exp, except that A is a vector of numbers.  That is,
- * each byte in A gets multiplied by gfpow[mod255(B)].
- */
-static inline unsigned long gfmul_exp_long(unsigned long a, int b)
-{
-       TRACE_FUN(8, "gfmul_exp_long");
-       unsigned char t;
-
-       if (sizeof(long) == 4) {
-               TRACE_EXIT;
-               return
-                   ((t = a >> 24 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 24) : 0) |
-                   ((t = a >> 16 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 16) : 0) |
-                   ((t = a >> 8 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 8) : 0) |
-                   ((t = a >> 0 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 0) : 0);
-#if !defined(linux)
-       } else if (sizeof(long) == 8) {
-               TRACE_EXIT;
-               return
-                   ((t = a >> 56 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 56) : 0) |
-                   ((t = a >> 48 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 48) : 0) |
-                   ((t = a >> 40 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 40) : 0) |
-                   ((t = a >> 32 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 32) : 0) |
-                   ((t = a >> 24 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 24) : 0) |
-                   ((t = a >> 16 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 16) : 0) |
-                   ((t = a >> 8 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 8) : 0) |
-                   ((t = a >> 0 & 0xff) ? (((unsigned long) gfpow[mod255(gflog[t] + b)]) << 0) : 0);
-#endif
-       } else {
-               TRACEx1(1, "Error: size of long is %d bytes", (int) sizeof(long));
-       }
-       TRACE_EXIT;
-       return -1;
-}
-
-
-/*
- * Divide two numbers in the field.  Returns a/b (modulo f(x)).
- */
-static inline unsigned char gfdiv(unsigned char a, unsigned char b)
-{
-       TRACE_FUN(8, "gfdiv");
-       if (!b) {
-               TRACE(-1, "Error: division by zero");
-               return 0xff;
-       } else if (a == 0) {
-               return 0;
-       } else {
-               return gfpow[mod255(gflog[a] - gflog[b])];
-       }
-       TRACE_EXIT;
-}
-
-
-/*
- * The following functions return the inverse of the matrix of the
- * linear system that needs to be solved to determine the error
- * magnitudes.  The first deals with matrices of rank 3, while the
- * second deals with matrices of rank 2.  The error indices are passed
- * in arguments L0,..,L2 (0=first sector, 31=last sector).  The
- * error indices must be sorted in ascending order, i.e., L0<L1<L2.
- *
- * The linear system that needs to be solved for the error
- * magnitudes is A * b = s, where s is the known vector of
- * syndromes, b is the vector of error magnitudes and A in
- * the ORDER=3 case:
- *
- *    A_3 = {{1/r^L[0], 1/r^L[1], 1/r^L[2]},
- *          {        1,        1,        1},
- *          {   r^L[0],   r^L[1],   r^L[2]}}
- */
-static inline int gfinv3(unsigned char l0, unsigned char l1, unsigned char l2, Matrix Ainv)
-{
-       TRACE_FUN(8, "gfinv3");
-       unsigned char det;
-       unsigned char t20, t10, t21, t12, t01, t02;
-       int log_det;
-
-       /* compute some intermediate results: */
-       t20 = gfpow[l2 - l0];   /* t20 = r^l2/r^l0 */
-       t10 = gfpow[l1 - l0];   /* t10 = r^l1/r^l0 */
-       t21 = gfpow[l2 - l1];   /* t21 = r^l2/r^l1 */
-       t12 = gfpow[l1 - l2 + 255];     /* t12 = r^l1/r^l2 */
-       t01 = gfpow[l0 - l1 + 255];     /* t01 = r^l0/r^l1 */
-       t02 = gfpow[l0 - l2 + 255];     /* t02 = r^l0/r^l2 */
-       /*
-        * Calculate the determinant of matrix A_3^-1 (sometimes called
-        * the Vandermonde determinant):
-        */
-       det = gfadd(t20, gfadd(t10, gfadd(t21, gfadd(t12, gfadd(t01, t02)))));
-       if (!det) {
-               TRACE(1, "Inversion failed (3 CRC errors, >0 CRC failures)");
-               TRACE_EXIT;
-               return 0;
-       }
-       log_det = 255 - gflog[det];
-
-       /*
-        * Now, calculate all of the coefficients:
-        */
-       Ainv[0][0] = gfmul_exp(gfadd(gfpow[l1], gfpow[l2]), log_det);
-       Ainv[0][1] = gfmul_exp(gfadd(t21, t12), log_det);
-       Ainv[0][2] = gfmul_exp(gfadd(gfpow[255 - l1], gfpow[255 - l2]), log_det);
-
-       Ainv[1][0] = gfmul_exp(gfadd(gfpow[l0], gfpow[l2]), log_det);
-       Ainv[1][1] = gfmul_exp(gfadd(t20, t02), log_det);
-       Ainv[1][2] = gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l2]), log_det);
-
-       Ainv[2][0] = gfmul_exp(gfadd(gfpow[l0], gfpow[l1]), log_det);
-       Ainv[2][1] = gfmul_exp(gfadd(t10, t01), log_det);
-       Ainv[2][2] = gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l1]), log_det);
-
-       TRACE_EXIT;
-       return 1;
-}
-
-
-static inline int gfinv2(unsigned char l0, unsigned char l1, Matrix Ainv)
-{
-       TRACE_FUN(8, "gfinv2");
-       unsigned char det;
-       unsigned char t1, t2;
-       int log_det;
-
-       t1 = gfpow[255 - l0];
-       t2 = gfpow[255 - l1];
-       det = gfadd(t1, t2);
-       if (!det) {
-               TRACE(1, "Inversion failed (2 CRC errors, >0 CRC failures)");
-               TRACE_EXIT;
-               return 0;
-       }
-       log_det = 255 - gflog[det];
-
-       /*
-        * Now, calculate all of the coefficients:
-        */
-       Ainv[0][0] = Ainv[1][0] = gfpow[log_det];
-
-       Ainv[0][1] = gfmul_exp(t2, log_det);
-       Ainv[1][1] = gfmul_exp(t1, log_det);
-
-       TRACE_EXIT;
-       return 1;
-}
-
-
-/*
- * Multiply matrix A by vector S and return result in vector B.
- * M is assumed to be of order NxN, S and B of order Nx1.
- */
-static inline void gfmat_mul(int n, Matrix A, unsigned char *s, unsigned char *b)
-{
-       int i, j;
-       unsigned char dot_prod;
-
-       for (i = 0; i < n; ++i) {
-               dot_prod = 0;
-               for (j = 0; j < n; ++j) {
-                       dot_prod = gfadd(dot_prod, gfmul(A[i][j], s[j]));
-               }
-               b[i] = dot_prod;
-       }
-}
-\f
-
-
-/*
- * The Reed Solomon ECC codes are computed over the N-th byte of each
- * block, where N=SECTOR_SIZE.  There are up to 29 blocks of data, and
- * 3 blocks of ECC.  The blocks are stored contiguously in memory.
- * A segment, consequently, is assumed to have at least 4 blocks:
- * one or more data blocks plus three ECC blocks.
- *
- * Notice: In QIC-80 speak, a CRC error is a sector with an incorrect
- *         CRC.  A CRC failure is a sector with incorrect data, but
- *         a valid CRC.  In the error control literature, the former
- *         is usually called "erasure", the latter "error."
- */
-/*
- * Compute the parity bytes for C columns of data, where C is the
- * number of bytes that fit into a long integer.  We use a linear
- * feed-back register to do this.  The parity bytes P[0], P[STRIDE],
- * P[2*STRIDE] are computed such that:
- *
- *              x^k * p(x) + m(x) = 0 (modulo g(x))
- *
- * where k = NBLOCKS,
- *       p(x) = P[0] + P[STRIDE]*x + P[2*STRIDE]*x^2, and
- *       m(x) = sum_{i=0}^k m_i*x^i.
- *       m_i  = DATA[i*SECTOR_SIZE]
- */
-static inline void set_parity(unsigned long *data, int nblocks, unsigned long *p, int stride)
-{
-       TRACE_FUN(8, "set_parity");
-       unsigned long p0, p1, p2, t1, t2, *end;
-
-       end = data + nblocks * (SECTOR_SIZE / sizeof(long));
-       p0 = p1 = p2 = 0;
-       while (data < end) {
-               /*
-                * The new parity bytes p0_i, p1_i, p2_i are computed from the old
-                * values p0_{i-1}, p1_{i-1}, p2_{i-1} recursively as:
-                *
-                *        p0_i = p1_{i-1} + r^105 * (m_{i-1} - p0_{i-1})
-                *        p1_i = p2_{i-1} + r^105 * (m_{i-1} - p0_{i-1})
-                *        p2_i =                    (m_{i-1} - p0_{i-1})
-                *
-                * With the initial condition: p0_0 = p1_0 = p2_0 = 0.
-                */
-               t1 = gfadd_long(*data, p0);
-               /*
-                * Multiply each byte in t1 by 0xc0:
-                */
-               if (sizeof(long) == 4) {
-                       t2 = ((unsigned long) gfmul_c0[t1 >> 24 & 0xff]) << 24 |
-                           ((unsigned long) gfmul_c0[t1 >> 16 & 0xff]) << 16 |
-                           ((unsigned long) gfmul_c0[t1 >> 8 & 0xff]) << 8 |
-                           ((unsigned long) gfmul_c0[t1 >> 0 & 0xff]) << 0;
-#if !defined(linux)
-               } else if (sizeof(long) == 8) {
-                       t2 = ((unsigned long) gfmul_c0[t1 >> 56 & 0xff]) << 56 |
-                           ((unsigned long) gfmul_c0[t1 >> 48 & 0xff]) << 48 |
-                           ((unsigned long) gfmul_c0[t1 >> 40 & 0xff]) << 40 |
-                           ((unsigned long) gfmul_c0[t1 >> 32 & 0xff]) << 32 |
-                           ((unsigned long) gfmul_c0[t1 >> 24 & 0xff]) << 24 |
-                           ((unsigned long) gfmul_c0[t1 >> 16 & 0xff]) << 16 |
-                           ((unsigned long) gfmul_c0[t1 >> 8 & 0xff]) << 8 |
-                           ((unsigned long) gfmul_c0[t1 >> 0 & 0xff]) << 0;
-#endif
-               } else {
-                       TRACEx1(1, "Error: long is of size %d", (int) sizeof(long));
-               }
-               p0 = gfadd_long(t2, p1);
-               p1 = gfadd_long(t2, p2);
-               p2 = t1;
-               data += SECTOR_SIZE / sizeof(long);
-       }
-       *p = p0;
-       p += stride;
-       *p = p1;
-       p += stride;
-       *p = p2;
-       TRACE_EXIT;
-}
-
-
-/*
- * Compute the 3 syndrome values.  DATA should point to the first byte
- * of the column for which the syndromes are desired.  The syndromes
- * are computed over the first NBLOCKS of rows.  The three bytes will be
- * placed in S[0], S[1], and S[2].
- *
- * S[i] is the value of the "message" polynomial m(x) evaluated at the
- * i-th root of the generator polynomial g(x).
- *
- * As g(x)=(x-r^-1)(x-1)(x-r^1) we evaluate the message polynomial at
- * x=r^-1 to get S[0], at x=r^0=1 to get S[1], and at x=r to get S[2].
- * This could be done directly and efficiently via the Horner scheme.
- * However, it would require multiplication tables for the factors
- * r^-1 (0xc3) and r (0x02).  The following scheme does not require
- * any multiplication tables beyond what's needed for set_parity()
- * anyway and is slightly faster if there are no errors and slightly
- * slower if there are errors.  The latter is hopefully the infrequent
- * case.
- *
- * To understand the alternative algorithm, notice that
- * set_parity(m, k, p) computes parity bytes such that:
- *
- *      x^k * p(x) = m(x) (modulo g(x)).
- *
- * That is, to evaluate m(r^m), where r^m is a root of g(x), we can
- * simply evaluate (r^m)^k*p(r^m).  Also, notice that p is 0 if and
- * only if s is zero.  That is, if all parity bytes are 0, we know
- * there is no error in the data and consequently there is no need to
- * compute s(x) at all!  In all other cases, we compute s(x) from p(x)
- * by evaluating (r^m)^k*p(r^m) for m=-1, m=0, and m=1.  The p(x)
- * polynomial is evaluated via the Horner scheme.
- */
-static int compute_syndromes(unsigned long *data, int nblocks, unsigned long *s)
-{
-       unsigned long p[3];
-
-       set_parity(data, nblocks, p, 1);
-       if (p[0] | p[1] | p[2]) {
-               /*
-                * Some of the checked columns do not have a zero syndrome.  For
-                * simplicity, we compute the syndromes for all columns that we
-                * have computed the remainders for.
-                */
-               s[0] = gfmul_exp_long(gfadd_long(p[0], gfmul_exp_long(gfadd_long(p[1],
-                             gfmul_exp_long(p[2], -1)), -1)), -nblocks);
-               s[1] = gfadd_long(gfadd_long(p[2], p[1]), p[0]);
-               s[2] = gfmul_exp_long(gfadd_long(p[0], gfmul_exp_long(gfadd_long(p[1],
-                                gfmul_exp_long(p[2], 1)), 1)), nblocks);
-               return 0;
-       } else {
-               return 1;
-       }
-}
-
-
-/*
- * Correct the block in the column pointed to by DATA.  There are NBAD
- * CRC errors and their indices are in BAD_LOC[0], up to
- * BAD_LOC[NBAD-1].  If NBAD>1, Ainv holds the inverse of the matrix
- * of the linear system that needs to be solved to determine the error
- * magnitudes.  S[0], S[1], and S[2] are the syndrome values.  If row
- * j gets corrected, then bit j will be set in CORRECTION_MAP.
- */
-static inline int correct_block(unsigned char *data, int nblocks,
-                               int nbad, int *bad_loc, Matrix Ainv,
-                               unsigned char *s,
-                               BAD_SECTOR * correction_map)
-{
-       TRACE_FUN(8, "correct_block");
-       int ncorrected = 0;
-       int i;
-       unsigned char t1, t2;
-       unsigned char c0, c1, c2;       /* check bytes */
-       unsigned char error_mag[3], log_error_mag;
-       unsigned char *dp, l, e;
-
-       switch (nbad) {
-       case 0:
-               /* might have a CRC failure: */
-               if (s[0] == 0) {
-                       /* more than one error */
-                       TRACE(1, "ECC failed (0 CRC errors, >1 CRC failures)");
-                       TRACE_EXIT;
-                       return -1;
-               }               /* if */
-               t1 = gfdiv(s[1], s[0]);
-               if ((bad_loc[nbad++] = gflog[t1]) >= nblocks) {
-                       TRACE(1, "ECC failed (0 CRC errors, >1 CRC failures): ");
-                       TRACEi(1, "attempt to correct data at ", bad_loc[0]);
-                       TRACE_EXIT;
-                       return -1;
-               }
-               error_mag[0] = s[1];
-               break;
-       case 1:
-               t1 = gfadd(gfmul_exp(s[1], bad_loc[0]), s[2]);
-               t2 = gfadd(gfmul_exp(s[0], bad_loc[0]), s[1]);
-               if (t1 == 0 && t2 == 0) {
-                       /* one erasure, no error: */
-                       Ainv[0][0] = gfpow[bad_loc[0]];
-               } else if (t1 == 0 || t2 == 0) {
-                       /* one erasure and more than one error: */
-                       TRACE(1, "ECC failed (1 erasure, >1 error)");
-                       TRACE_EXIT;
-                       return -1;
-               } else {
-                       /* one erasure, one error: */
-                       if ((bad_loc[nbad++] = gflog[gfdiv(t1, t2)]) >= nblocks) {
-                               TRACE(1, "ECC failed (1 CRC errors, >1 CRC failures): ");
-                               TRACEi(1, "attempt to correct data at ", bad_loc[1]);
-                               TRACE_EXIT;
-                               return -1;
-                       }       /* if */
-                       if (!gfinv2(bad_loc[0], bad_loc[1], Ainv)) {
-                               /* inversion failed---must have more than one error */
-                               TRACE_EXIT;
-                               return -1;
-                       }
-               }
-               /*
-                *  FALL THROUGH TO ERROR MAGNITUDE COMPUTATION:
-                */
-       case 2:
-       case 3:
-               /* compute error magnitudes: */
-               gfmat_mul(nbad, Ainv, s, error_mag);
-               break;
-
-       default:
-               TRACE(1, "Internal Error: number of CRC errors > 3");
-               TRACE_EXIT;
-               return -1;
-       }
-
-       /*
-        * Perform correction by adding ERROR_MAG[i] to the byte at offset
-        * BAD_LOC[i].  Also add the value of the computed error polynomial
-        * to the syndrome values.  If the correction was successful, the
-        * resulting check bytes should be zero (i.e., the corrected data
-        * is a valid code word).
-        */
-       c0 = s[0];
-       c1 = s[1];
-       c2 = s[2];
-       for (i = 0; i < nbad; ++i) {
-               e = error_mag[i];
-               if (e) {
-                       /* correct the byte at offset L by magnitude E: */
-                       l = bad_loc[i];
-                       dp = &data[l * SECTOR_SIZE];
-                       *dp = gfadd(*dp, e);
-                       *correction_map |= 1 << l;
-                       ++ncorrected;
-
-                       log_error_mag = gflog[e];
-                       c0 = gfadd(c0, gfpow[mod255(log_error_mag - l)]);
-                       c1 = gfadd(c1, e);
-                       c2 = gfadd(c2, gfpow[mod255(log_error_mag + l)]);
-               }
-       }
-       if (c0 || c1 || c2) {
-               TRACE(1, "ECC self-check failed, too many errors");
-               TRACE_EXIT;
-               return -1;
-       }
-       TRACE_EXIT;
-       return ncorrected;
-}
-
-
-#if defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID)
-
-/*
- * Perform a sanity check on the computed parity bytes:
- */
-static int sanity_check(unsigned long *data, int nblocks)
-{
-       TRACE_FUN(8, "sanity_check");
-       unsigned long s[3];
-
-       if (!compute_syndromes(data, nblocks, s)) {
-               TRACE(-1, "Internal Error: syndrome self-check failed");
-               TRACE_EXIT;
-               return 0;
-       }
-       TRACE_EXIT;
-       return 1;
-}
-
-#endif                         /* defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) */
-\f
-
-
-/*
- * Compute the parity for an entire segment of data.
- */
-int ecc_set_segment_parity(struct memory_segment *mseg)
-{
-       int i;
-       unsigned char *parity_bytes;
-
-       parity_bytes = &mseg->data[(mseg->blocks - 3) * SECTOR_SIZE];
-       for (i = 0; i < SECTOR_SIZE; i += sizeof(long)) {
-               set_parity((unsigned long *) &mseg->data[i], mseg->blocks - 3,
-                          (unsigned long *) &parity_bytes[i],
-                          SECTOR_SIZE / sizeof(long));
-#ifdef ECC_PARANOID
-               if (!sanity_check((unsigned long *) &mseg->data[i], mseg->blocks)) {
-                       return -1;
-               }
-#endif                         /* ECC_PARANOID */
-       }
-       return 0;
-}
-
-
-/*
- * Checks and corrects (if possible) the segment MSEG.  Returns one of
- * ECC_OK, ECC_CORRECTED, and ECC_FAILED.
- */
-int ecc_correct_data(struct memory_segment *mseg)
-{
-       TRACE_FUN(5, "ecc_correct_data");
-       int col, i, result;
-       int ncorrected = 0;
-       int nerasures = 0;      /* # of erasures (CRC errors) */
-       int erasure_loc[3];     /* erasure locations */
-       unsigned long ss[3];
-       unsigned char s[3];
-       Matrix Ainv;
-
-       mseg->corrected = 0;
-
-       /* find first column that has non-zero syndromes: */
-       for (col = 0; col < SECTOR_SIZE; col += sizeof(long)) {
-               if (!compute_syndromes((unsigned long *) &mseg->data[col],
-                                      mseg->blocks, ss)) {
-                       /* something is wrong---have to fix things */
-                       break;
-               }
-       }
-       if (col >= SECTOR_SIZE) {
-               /* all syndromes are ok, therefore nothing to correct */
-               TRACE_EXIT;
-               return ECC_OK;
-       }
-       /* count the number of CRC errors if there were any: */
-       if (mseg->read_bad) {
-               for (i = 0; i < mseg->blocks; i++) {
-                       if (BAD_CHECK(mseg->read_bad, i)) {
-                               if (nerasures >= 3) {
-                                       /* this is too much for ECC */
-                                       TRACE(1, "ECC failed (>3 CRC errors)");
-                                       TRACE_EXIT;
-                                       return ECC_FAILED;
-                               }       /* if */
-                               erasure_loc[nerasures++] = i;
-                       }
-               }
-       }
-       /*
-          * If there are at least 2 CRC errors, determine inverse of matrix
-          * of linear system to be solved:
-        */
-       switch (nerasures) {
-       case 2:
-               if (!gfinv2(erasure_loc[0], erasure_loc[1], Ainv)) {
-                       TRACE_EXIT;
-                       return ECC_FAILED;
-               }
-               break;
-       case 3:
-               if (!gfinv3(erasure_loc[0], erasure_loc[1], erasure_loc[2], Ainv)) {
-                       TRACE_EXIT;
-                       return ECC_FAILED;
-               }
-               break;
-       default:
-               /* this is not an error condition... */
-               break;
-       }
-
-       do {
-               for (i = 0; i < sizeof(long); ++i) {
-                       s[0] = ss[0];
-                       s[1] = ss[1];
-                       s[2] = ss[2];
-                       if (s[0] | s[1] | s[2]) {
-#ifdef BIG_ENDIAN
-                               result = correct_block(&mseg->data[col + sizeof(long) - 1 - i],
-                                                      mseg->blocks,
-                                        nerasures, erasure_loc, Ainv, s,
-                                                      &mseg->corrected);
-#else
-                               result = correct_block(&mseg->data[col + i], mseg->blocks,
-                                        nerasures, erasure_loc, Ainv, s,
-                                                      &mseg->corrected);
-#endif
-                               if (result < 0) {
-                                       TRACE_EXIT;
-                                       return ECC_FAILED;
-                               }
-                               ncorrected += result;
-                       }
-                       ss[0] >>= 8;
-                       ss[1] >>= 8;
-                       ss[2] >>= 8;
-               }
-
-#ifdef ECC_SANITY_CHECK
-               if (!sanity_check((unsigned long *) &mseg->data[col], mseg->blocks)) {
-                       TRACE_EXIT;
-                       return ECC_FAILED;
-               }
-#endif                         /* ECC_SANITY_CHECK */
-
-               /* find next column with non-zero syndromes: */
-               while ((col += sizeof(long)) < SECTOR_SIZE) {
-                       if (!compute_syndromes((unsigned long *) &mseg->data[col],
-                                              mseg->blocks, ss)) {
-                               /* something is wrong---have to fix things */
-                               break;
-                       }
-               }
-       } while (col < SECTOR_SIZE);
-       if (ncorrected && nerasures == 0) {
-               TRACE(2, "block contained error not caught by CRC");
-       }
-       TRACEi((ncorrected > 0) ? 4 : 8, "number of corrections:", ncorrected);
-       TRACE_EXIT;
-       return ncorrected ? ECC_CORRECTED : ECC_OK;
-}
-
-/*** end of ecc.c ***/
diff --git a/drivers/char/ftape/ecc.h b/drivers/char/ftape/ecc.h
deleted file mode 100644 (file)
index 890543c..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- *      Copyright (C) 1993 Ning and David Mosberger.
- *      Original:
- *      Copyright (C) 1993 Bas Laarhoven.
- *      Copyright (C) 1992 David L. Brown, Jr.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
- * USA.
- *
- *
- * $Source: /home/bas/distr/ftape-2.03b/RCS/ecc.h,v $
- * $Author: bas $
- *
- * $Revision: 1.20 $
- * $Date: 1995/01/08 14:16:21 $
- * $State: Beta $
- *
- *      This file contains the definitions for the
- *      Reed-Solomon error correction code 
- *      for the QIC-40/80 tape streamer device driver.
- */
-#ifndef _ecc_h_
-#define _ecc_h_
-
-typedef unsigned long BAD_SECTOR;
-#define BAD_CLEAR(entry) ((entry)=0)
-#define BAD_SET(entry,sector) ((entry)|=(1<<(sector)))
-#define BAD_CHECK(entry,sector) ((entry)&(1<<(sector)))
-
-/*
- * Return values for ecc_correct_data:
- */
-enum {
-       ECC_OK,                 /* Data was correct. */
-       ECC_CORRECTED,          /* Correctable error in data. */
-       ECC_FAILED,             /* Could not correct data. */
-};
-
-/*
- * Representation of an in memory segment.  MARKED_BAD lists the
- * sectors that were marked bad during formatting.  If the N-th sector
- * in a segment is marked bad, bit 1<<N will be set in MARKED_BAD.
- * The sectors should be read in from the disk and packed, as if the
- * bad sectors were not there, and the segment just contained fewer
- * sectors.  READ_SECTORS is a bitmap of errors encountered while
- * reading the data.  These offsets are relative to the packed data.
- * BLOCKS is a count of the sectors not marked bad.  This is just to
- * prevent having to count the zero bits in MARKED_BAD each time this
- * is needed.  DATA is the actual sector packed data from (or to) the
- * tape.
- */
-struct memory_segment {
-       BAD_SECTOR marked_bad;
-       BAD_SECTOR read_bad;
-       int blocks;
-       unsigned char *data;
-       BAD_SECTOR corrected;
-};
-
-/*
- * ecc.c defined global variables:
- */
-#ifdef TEST
-extern int ftape_ecc_tracing;
-#endif
-
-/*
- * ecc.c defined global functions:
- */
-extern int ecc_correct_data(struct memory_segment *data);
-extern int ecc_set_segment_parity(struct memory_segment *data);
-
-#endif /* _ecc_h_ */
diff --git a/drivers/char/ftape/fc-10.c b/drivers/char/ftape/fc-10.c
deleted file mode 100644 (file)
index d18d650..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/* Yo, Emacs! we're -*- Linux-C -*-
- *
-   Copyright (C) 1993,1994 Jon Tombs.
-
-   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.
-
-   The entire guts of this program was written by dosemu, modified to
-   record reads and writes to the ports in the 0x180-0x188 address space,
-   while running the CMS program TAPE.EXE V2.0.5 supplied with the drive.
-
-   Modified to use an array of addresses and generally cleaned up (made
-   much shorter) 4 June 94, dosemu isn't that good at writing short code it
-   would seem :-). Made independent of 0x180, but I doubt it will work
-   at any other address.
-
-   Modified for distribution with ftape source. 21 June 94, SJL.
-
-   Modifications on 20 October 95, by Daniel Cohen (catman@wpi.edu):
-   Modified to support different DMA, IRQ, and IO Ports.  Borland's
-   Turbo Debugger in virtual 8086 mode (TD386.EXE with hardware breakpoints
-   provided by the TDH386.SYS Device Driver) was used on the CMS program
-   TAPE V4.0.5.  I set breakpoints on I/O to ports 0x180-0x187.  Note that
-   CMS's program will not successfully configure the tape drive if you set
-   breakpoints on IO Reads, but you can set them on IO Writes without problems.
-   Known problems:
-   - You can not use DMA Channels 5 or 7.
-
-   Modification on 29 January 96, by Daniel Cohen (catman@wpi.edu):
-   Modified to only accept IRQs 3 - 7, or 9.  Since we can only send a 3 bit
-   number representing the IRQ to the card, special handling is required when
-   IRQ 9 is selected.  IRQ 2 and 9 are the same, and we should request IRQ 9
-   from the kernel while telling the card to use IRQ 2.  Thanks to Greg
-   Crider (gcrider@iclnet.org) for finding and locating this bug, as well as
-   testing the patch.
-
- *
- *      This file contains code for the CMS FC-10/FC-20 card.
- */
-
-#include <linux/ftape.h>
-#include <asm/io.h>
-
-#include "tracing.h"
-#include "fdc-io.h"
-#include "fc-10.h"
-
-#ifdef PROBE_FC10
-
-/*  This code will only work if the FC-10 (or FC-20) is set to
- *  use DMA channels 1, 2, or 3.  DMA channels 5 and 7 seem to be 
- *  initialized by the same command as channels 1 and 3, respectively.
- */
-#if (FDC_DMA > 3)
-#error : The FC-10/20 must be set to use DMA channels 1, 2, or 3!
-#endif
-
-/*  Only allow the FC-10/20 to use IRQ 3-7, or 9.  Note that CMS's program
- *  only accepts IRQ's 2-7, but in linux, IRQ 2 is the same as IRQ 9.
- */
-#if (FDC_IRQ < 3 || FDC_IRQ == 8 || FDC_IRQ > 9)
-#error : The FC-10/20 must be set to use IRQ levels 3 - 7, or 9!
-#error :              Note IRQ 9 is the same as IRQ 2
-#endif
-
-unsigned short inbs_magic[] = {
-       0x3, 0x3, 0x0, 0x4, 0x7, 0x2, 0x5, 0x3, 0x1, 0x4,
-       0x3, 0x5, 0x2, 0x0, 0x3, 0x7, 0x4, 0x2,
-       0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7
-};
-
-unsigned short fc10_ports[] = {
-       0x180, 0x210, 0x2A0, 0x300, 0x330, 0x340, 0x370
-};
-
-int fc10_enable(void)
-{
-       int i;
-       byte cardConfig = 0x00;
-       byte x;
-
-       /*  Clear state machine ???
-        */
-       for (i = 0; i < NR_ITEMS(inbs_magic); i++) {
-               inb(FDC_BASE + inbs_magic[i]);
-       }
-       outb(0x0, FDC_BASE);
-
-       x = inb(FDC_BASE);
-       if (x == 0x13 || x == 0x93) {
-               for (i = 1; i < 8; i++) {
-                       if (inb(FDC_BASE + i) != x) {
-                               return 0;
-                       }
-               }
-       } else {
-               return 0;
-       }
-
-       outb(0x8, FDC_BASE);
-
-       for (i = 0; i < 8; i++) {
-               if (inb(FDC_BASE + i) != 0x0) {
-                       return 0;
-               }
-       }
-       outb(0x10, FDC_BASE);
-
-       for (i = 0; i < 8; i++) {
-               if (inb(FDC_BASE + i) != 0xff) {
-                       return 0;
-               }
-       }
-
-       /*  Okay, we found a FC-10 card ! ???
-        */
-       outb(0x0, fdc.ccr);
-
-       /*  Clear state machine again ???
-        */
-       for (i = 0; i < NR_ITEMS(inbs_magic); i++) {
-               inb(FDC_BASE + inbs_magic[i]);
-       }
-       /* Send io port */
-       for (i = 0; i < NR_ITEMS(fc10_ports); i++)
-               if (FDC_BASE == fc10_ports[i])
-                       cardConfig = i + 1;
-       if (cardConfig == 0)
-               return 0;       /* Invalid I/O Port */
-       /* and IRQ - If using IRQ 9, tell the FC card it is actually IRQ 2 */
-       if (FDC_IRQ != 9)
-               cardConfig |= FDC_IRQ << 3;
-       else
-               cardConfig |= 2 << 3;
-
-       /* and finally DMA Channel */
-       cardConfig |= FDC_DMA << 6;
-       outb(cardConfig, FDC_BASE);     /* DMA [2 bits]/IRQ [3 bits]/BASE [3 bits] */
-
-       /*  Enable FC-10 ???
-        */
-       outb(0, fdc.ccr);
-       outb(0, FDC_BASE + 0x6);
-       outb(8, fdc.dor);
-       outb(8, fdc.dor);
-       outb(1, FDC_BASE + 0x6);
-
-       /*  Initialize fdc, select drive B:
-        */
-       outb(0x08, fdc.dor);    /* assert reset, dma & irq enabled */
-       outb(0x0c, fdc.dor);    /* release reset */
-       outb(0x2d, fdc.dor);    /* select drive 1 */
-
-       return (x == 0x93) ? 2 : 1;
-}
-
-#endif /* CMS_FC10_CONTROLLER */
diff --git a/drivers/char/ftape/fc-10.h b/drivers/char/ftape/fc-10.h
deleted file mode 100644 (file)
index f38d468..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef _FC_10_H
-#define _FC_10_H
-
-/*
- * Copyright (C) 1994 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/fc-10.h,v $
- $Author: bas $
- *
- $Revision: 1.3 $
- $Date: 1995/01/08 14:16:21 $
- $State: Beta $
- *
- *      This file contains definitions for the FC-10 code
- *      of the QIC-40/80 floppy-tape driver for Linux.
- */
-
-/*
- *      fc-10.c defined global vars.
- */
-
-/*
- *      fc-10.c defined global functions.
- */
-extern int fc10_enable(void);
-
-#endif
diff --git a/drivers/char/ftape/fdc-io.c b/drivers/char/ftape/fdc-io.c
deleted file mode 100644 (file)
index 3ebc5a8..0000000
+++ /dev/null
@@ -1,1300 +0,0 @@
-/* Yo, Emacs! we're -*- Linux-C -*-
- *
- *      Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- *      This file contains the low-level floppy disk interface code
- *      for the QIC-40/80 tape streamer device driver.
- */
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/ioport.h>
-#include <linux/ftape.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <asm/irq.h>
-
-#include "tracing.h"
-#include "fdc-io.h"
-#include "fdc-isr.h"
-#include "ftape-io.h"
-#include "ftape-rw.h"
-#include "calibr.h"
-#include "fc-10.h"
-#include "qic117.h"
-
-
-/*      Global vars.
- */
-int ftape_unit = -1;
-int ftape_motor = 0;
-int current_cylinder = -1;
-fdc_mode_enum fdc_mode = fdc_idle;
-fdc_config_info fdc = {0};
-
-/*      Local vars.
- */
-static int fdc_calibr_count;
-static int fdc_calibr_time;
-static int fdc_confused = 0;
-static int fdc_status;
-volatile byte fdc_head;                /* FDC head */
-volatile byte fdc_cyl;         /* FDC track */
-volatile byte fdc_sect;                /* FDC sector */
-static int fdc_data_rate = 0;  /* default rate = 500 Kbps */
-static int fdc_seek_rate = 14; /* default rate = 2 msec @ 500 Kbps */
-static void (*do_ftape) (void);
-static int fdc_fifo_state;     /* original fifo setting - fifo enabled */
-static int fdc_fifo_thr;       /* original fifo setting - threshold */
-static int fdc_lock_state;     /* original lock setting - locked */
-static int fdc_fifo_locked = 0;        /* has fifo && lock set ? */
-static byte fdc_precomp = 0;   /* sets fdc to default precomp. value */
-static byte fdc_drv_spec[4];   /* drive specification bytes for i82078 */
-static int perpend_mode;       /* true if fdc is in perpendicular mode */
-
-static char ftape_id[] = "ftape"; /* used by request irq and free irq */
-
-void fdc_catch_stray_interrupts(unsigned count)
-{
-       unsigned long flags;
-
-       save_flags(flags);
-       cli();
-       if (count == 0) {
-               expected_stray_interrupts = 0;
-       } else {
-               expected_stray_interrupts += count;
-       }
-       restore_flags(flags);
-}
-
-/*  Wait during a timeout period for a given FDC status.
- *  If usecs == 0 then just test status, else wait at least for usecs.
- *  Returns -ETIME on timeout. Function must be calibrated first !
- */
-int fdc_wait(int usecs, byte mask, byte state)
-{
-       int count_1 = (fdc_calibr_count * usecs - 1) / fdc_calibr_time;
-
-       do {
-               fdc_status = inb_p(fdc.msr);
-               if ((fdc_status & mask) == state) {
-                       return 0;
-               }
-       } while (count_1-- >= 0);
-       return -ETIME;
-}
-
-int fdc_ready_wait(int usecs)
-{
-       return fdc_wait(usecs, FDC_DATA_READY, FDC_DATA_READY);
-}
-
-static void fdc_usec_wait(int usecs)
-{
-       fdc_wait(usecs, 0, 1);  /* will always timeout ! */
-}
-
-int fdc_ready_out_wait(int usecs)
-{
-       fdc_usec_wait(RQM_DELAY);       /* wait for valid RQM status */
-       return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_OUT_READY);
-}
-
-int fdc_ready_in_wait(int usecs)
-{
-       fdc_usec_wait(RQM_DELAY);       /* wait for valid RQM status */
-       return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_IN_READY);
-}
-
-int fdc_wait_calibrate(void)
-{
-       return calibrate("fdc_wait",
-                    fdc_usec_wait, &fdc_calibr_count, &fdc_calibr_time);
-}
-
-/*  Wait for a (short) while for the FDC to become ready
- *  and transfer the next command byte.
- *  Return -ETIME on timeout on getting ready (depends on hardware!).
- */
-int fdc_write(byte data)
-{
-       fdc_usec_wait(RQM_DELAY);       /* wait for valid RQM status */
-       if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_IN_READY) < 0) {
-               return -ETIME;
-       } else {
-               outb(data, fdc.fifo);
-               return 0;
-       }
-}
-
-/*  Wait for a (short) while for the FDC to become ready
- *  and transfer the next result byte.
- *  Return -ETIME if timeout on getting ready (depends on hardware!).
- */
-int fdc_read(byte * data)
-{
-       fdc_usec_wait(RQM_DELAY);       /* wait for valid RQM status */
-       if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_OUT_READY) < 0) {
-               return -ETIME;
-       } else {
-               *data = inb(fdc.fifo);
-               return 0;
-       }
-}
-
-/*  Output a cmd_len long command string to the FDC.
- *  The FDC should be ready to receive a new command or
- *  an error (EBUSY) will occur.
- */
-int fdc_command(byte * cmd_data, int cmd_len)
-{
-       TRACE_FUN(8, "fdc_command");
-       int result = 0;
-       unsigned long flags;
-       int count = cmd_len;
-
-       fdc_usec_wait(RQM_DELAY);       /* wait for valid RQM status */
-       save_flags(flags);
-       cli();
-       fdc_status = inb(fdc.msr);
-       if ((fdc_status & FDC_DATA_READY_MASK) == FDC_DATA_IN_READY) {
-               int retry = 0;
-               fdc_mode = *cmd_data;   /* used by isr */
-               interrupt_seen = 0;
-               while (count) {
-                       result = fdc_write(*cmd_data);
-                       if (result < 0) {
-                               TRACEx3(6, "fdc_mode = %02x, status = %02x at index %d",
-                                       (int) fdc_mode, (int) fdc_status, cmd_len - count);
-                               if (++retry <= 3) {
-                                       TRACE(2, "fdc_write timeout, retry");
-                               } else {
-                                       TRACE(1, "fdc_write timeout, fatal");
-                                       fdc_confused = 1;
-                                       /* recover ??? */
-                                       break;
-                               }
-                       } else {
-                               --count;
-                               ++cmd_data;
-                       }
-               }
-       } else {
-               TRACE(1, "fdc not ready");
-               result = -EBUSY;
-       }
-       restore_flags(flags);
-       TRACE_EXIT;
-       return result;
-}
-
-/*  Input a res_len long result string from the FDC.
- *  The FDC should be ready to send the result or an error
- *  (EBUSY) will occur.
- */
-int fdc_result(byte * res_data, int res_len)
-{
-       TRACE_FUN(8, "fdc_result");
-       int result = 0;
-       unsigned long flags;
-       int count = res_len;
-
-       save_flags(flags);
-       cli();
-       fdc_status = inb(fdc.msr);
-       if ((fdc_status & FDC_DATA_READY_MASK) == FDC_DATA_OUT_READY) {
-               int retry = 0;
-               while (count) {
-                       if (!(fdc_status & FDC_BUSY)) {
-                               TRACE(1, "premature end of result phase");
-                       }
-                       result = fdc_read(res_data);
-                       if (result < 0) {
-                               TRACEx3(6, "fdc_mode = %02x, status = %02x at index %d",
-                                       (int) fdc_mode, (int) fdc_status, res_len - count);
-                               if (++retry <= 3) {
-                                       TRACE(2, "fdc_read timeout, retry");
-                               } else {
-                                       TRACE(1, "fdc_read timeout, fatal");
-                                       fdc_confused = 1;
-                                       /* recover ??? */
-                                       break;
-                               }
-                       } else {
-                               --count;
-                               ++res_data;
-                       }
-               }
-       } else {
-               TRACE(1, "fdc not ready");
-               result = -EBUSY;
-       }
-       restore_flags(flags);
-       fdc_usec_wait(RQM_DELAY);       /* allow FDC to negate BSY */
-       TRACE_EXIT;
-       return result;
-}
-
-/*      Handle command and result phases for
- *      commands without data phase.
- */
-int fdc_issue_command(byte * out_data, int out_count,
-                     byte * in_data, int in_count)
-{
-       TRACE_FUN(8, "fdc_issue_command");
-       int result;
-       int t0, t1;
-
-       if (out_count > 0) {
-               result = fdc_command(out_data, out_count);
-               if (result < 0) {
-                       TRACE(1, "fdc_command failed");
-                       TRACE_EXIT;
-                       return result;
-               }
-       }
-       /* will take 24 - 30 usec for fdc_sense_drive_status and
-        * fdc_sense_interrupt_status commands.
-        *    35 fails sometimes (5/9/93 SJL)
-        * On a loaded system it incidentally takes longer than
-        * this for the fdc to get ready ! ?????? WHY ??????
-        * So until we know what's going on use a very long timeout.
-        */
-       t0 = timestamp();
-       result = fdc_ready_out_wait(500 /* usec */ );
-       t1 = timestamp();
-       if (result < 0) {
-               TRACEi(1, "fdc_ready_out_wait failed after:", timediff(t0, t1));
-               TRACE_EXIT;
-               return result;
-       }
-       if (in_count > 0) {
-               result = fdc_result(in_data, in_count);
-               if (result < 0) {
-                       TRACE(1, "result phase aborted");
-                       TRACE_EXIT;
-                       return result;
-               }
-       }
-       TRACE_EXIT;
-       return 0;
-}
-
-/*      Wait for FDC interrupt with timeout.
- *      Signals are blocked so the wait will not be aborted.
- *      Note: interrupts must be enabled ! (23/05/93 SJL)
- */
-int fdc_interrupt_wait(int time)
-{
-       TRACE_FUN(8, "fdc_interrupt_wait");
-       struct wait_queue wait =
-       {current, NULL};
-       int result = -ETIME;
-       int need_cleanup = 0;
-       int current_blocked = current->blocked;
-       static int resetting = 0;
-
-       if (waitqueue_active(&wait_intr)) {
-               TRACE(1, "error: nested call");
-               return -EIO;    /* return error... */
-       }
-       if (interrupt_seen == 0) {
-               /* timeout time will be between 0 and MSPT milliseconds too long !
-                */
-               current->timeout = jiffies + 1 + (time + MSPT - 1) / MSPT;
-               current->state = TASK_INTERRUPTIBLE;
-               current->blocked = _BLOCK_ALL;
-               add_wait_queue(&wait_intr, &wait);
-               do {
-                       schedule();     /* sets TASK_RUNNING on timeout */
-               } while (!interrupt_seen && current->state != TASK_RUNNING);
-               current->blocked = current_blocked;     /* restore */
-               remove_wait_queue(&wait_intr, &wait);
-               if (interrupt_seen) {
-                       current->timeout = 0;   /* interrupt hasn't cleared this */
-                       result = 0;
-               } else {
-#if 1
-/*** remove me when sure this doesn't happen ***/
-                       if (current->timeout > 0) {
-                               TRACE(-1, "*** BUG: unexpected schedule exit ***");
-                               if (signal_pending(current)) {
-                                       TRACE(4, "caused by signal ?");
-                               }
-                       }
-#endif
-                       if (signal_pending(current)) {
-                               result = -EINTR;
-                       } else {
-                               result = -ETIME;
-                       }
-                       need_cleanup = 1;       /* missing interrupt, reset fdc. */
-               }
-       } else {
-               result = 0;
-       }
-       /*  In first instance, next statement seems unnecessary since
-        *  it will be cleared in fdc_command. However, a small part of
-        *  the software seems to rely on this being cleared here
-        *  (ftape_close might fail) so stick to it until things get fixed !
-        */
-       interrupt_seen = 0;     /* clear for next call */
-
-       if (need_cleanup & !resetting) {
-               resetting = 1;  /* break infinite recursion if reset fails */
-               TRACE(8, "cleanup reset");
-               fdc_reset();
-               resetting = 0;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-/*      Start/stop drive motor. Enable DMA mode.
- */
-void fdc_motor(int motor)
-{
-       TRACE_FUN(8, "fdc_motor");
-       int unit = FTAPE_UNIT;
-       int data = unit | FDC_RESET_NOT | FDC_DMA_MODE;
-
-       ftape_motor = motor;
-       if (ftape_motor) {
-               data |= FDC_MOTOR_0 << unit;
-               TRACEx1(4, "turning motor %d on", unit);
-       } else {
-               TRACEx1(4, "turning motor %d off", unit);
-       }
-#ifdef MACH2
-       outb_p(data, fdc.dor2);
-#else
-       outb_p(data, fdc.dor);
-#endif
-       ftape_sleep(10 * MILLISECOND);
-       TRACE_EXIT;
-}
-
-static void fdc_update_dsr(void)
-{
-       TRACE_FUN(8, "fdc_update_dsr");
-
-       TRACEx2(5, "rate = %d, precomp = %d", fdc_data_rate, fdc_precomp);
-       if (fdc.type >= i82077) {
-               outb_p((fdc_data_rate & 0x03) | fdc_precomp, fdc.dsr);
-       } else {
-               outb_p(fdc_data_rate, fdc.ccr);
-       }
-       TRACE_EXIT;
-}
-
-void fdc_set_write_precomp(int precomp)
-{
-       /*  write precompensation can be set in multiples of 41.67 nsec.
-        *  round the parameter to the nearest multiple and convert it
-        *  into a fdc setting. Note that 0 means default to the fdc,
-        *  7 is used instead of that.
-        */
-       fdc_precomp = ((precomp + 21) / 42) << 2;
-       if (fdc_precomp == 0) {
-               fdc_precomp = 7 << 2;
-       }
-       fdc_update_dsr();
-}
-
-/* Read back the Drive Specification regs on an i82078, so that we
- * are able to restore them later
- */
-void fdc_save_drive_specs(void)
-{
-       byte cmd1[] =
-       {FDC_DRIVE_SPEC, 0x80};
-       byte cmd2[] =
-       {FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0};
-       int result;
-
-       TRACE_FUN(8, "fdc_save_drive_specs");
-       if (fdc.type >= i82078_1) {
-               result = fdc_issue_command(cmd1, NR_ITEMS(cmd1), fdc_drv_spec, 4);
-               if (result >= 0) {
-                       cmd2[1] = (fdc_drv_spec[0] & 0x03) | 0x04;
-                       cmd2[2] = (fdc_drv_spec[1] & 0x03) | 0x24;
-                       cmd2[3] = (fdc_drv_spec[2] & 0x03) | 0x44;
-                       cmd2[4] = (fdc_drv_spec[3] & 0x03) | 0x64;
-                       fdc_command(cmd2, NR_ITEMS(cmd2));
-                       if (result < 0) {
-                               TRACE(1, "Setting of drive specs failed");
-                               return;
-                       }
-               } else {
-                       TRACE(2, "Save of drive specs failed");
-               }
-       }
-       TRACE_EXIT;
-}
-
-/* Restore the previously saved Drive Specification values */
-void fdc_restore_drive_specs(void)
-{
-       byte cmd[] =
-       {FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0};
-       int result;
-
-       TRACE_FUN(8, "fdc_restore_drive_specs");
-       if (fdc.type > i82078_1) {
-               cmd[1] = (fdc_drv_spec[0] & 0x1f) | 0x00;
-               cmd[2] = (fdc_drv_spec[1] & 0x1f) | 0x20;
-               cmd[3] = (fdc_drv_spec[2] & 0x1f) | 0x40;
-               cmd[4] = (fdc_drv_spec[3] & 0x1f) | 0x60;
-               result = fdc_command(cmd, NR_ITEMS(cmd));
-               if (result < 0) {
-                       TRACE(2, "Restoration of drive specs failed");
-               }
-       }
-       TRACE_EXIT;
-}
-
-/* Select clock for fdc, must correspond with tape drive setting !
- * This also influences the fdc timing so we must adjust some values.
- */
-void fdc_set_data_rate(int rate)
-{
-       /* Select clock for fdc, must correspond with tape drive setting !
-        * This also influences the fdc timing so we must adjust some values.
-        */
-       fdc_data_rate = rate;
-       fdc_update_dsr();
-       fdc_set_seek_rate(fdc_seek_rate);       /* re-adjust for changed clock */
-}
-
-/*      Reset the floppy disk controller. Leave the ftape_unit selected.
- */
-void fdc_reset(void)
-{
-       TRACE_FUN(8, "fdc_reset");
-       int unit = FTAPE_UNIT;
-       byte fdc_ctl = unit | FDC_DMA_MODE;
-       int st0;
-       int i;
-       int result;
-       int dummy;
-
-       if (ftape_motor) {
-               fdc_ctl |= FDC_MOTOR_0 << unit;
-       }
-#ifdef MACH2
-       outb_p(fdc_ctl & 0x0f, fdc.dor);
-       outb_p(fdc_ctl, fdc.dor2);
-#else
-       outb_p(fdc_ctl, fdc.dor);       /* assert reset, keep unit selected */
-#endif
-       fdc_usec_wait(10 /* usec */ );  /* delay >= 14 fdc clocks */
-       fdc_ctl |= FDC_RESET_NOT;
-       fdc_mode = fdc_idle;
-#ifdef MACH2
-       outb_p(fdc_ctl & 0x0f, fdc.dor);
-       outb_p(fdc_ctl, fdc.dor2);
-#else
-       outb_p(fdc_ctl, fdc.dor);       /* release reset */
-#endif
-       result = fdc_interrupt_wait(1 * SECOND);
-       if (result < 0) {
-               TRACE(1, "missing interrupt after reset");
-       }
-       fdc_set_data_rate(fdc_data_rate);       /* keep original setting */
-       fdc_usec_wait(1000 /* usec */ );        /* don't know why, but needed */
-       for (i = 0; i < 4; ++i) {       /* clear disk-change status */
-               fdc_sense_interrupt_status(&st0, &dummy);
-               if (i == unit) {
-                       current_cylinder = dummy;
-               }
-       }
-       fdc_set_seek_rate(2);
-       TRACE_EXIT;
-}
-
-/* When we're done, put the fdc into reset mode so that the regular
-   floppy disk driver will figure out that something is wrong and
-   initialize the controller the way it wants. */
-void fdc_disable(void)
-{
-       TRACE_FUN(8, "fdc_disable");
-       int result;
-       byte cmd1[] = {FDC_CONFIGURE, 0x00, 0x00, 0x00};
-       byte cmd2[] = {FDC_LOCK};
-       byte cmd3[] = {FDC_UNLOCK};
-       byte stat[1];
-
-       if (CLK_48MHZ && fdc.type >= i82078)
-               cmd1[0] |= FDC_CLK48_BIT;
-       if (fdc_fifo_locked) {
-               result = fdc_issue_command(cmd3, 1, stat, 1);
-               if (result < 0 || stat[0] != 0x00) {
-                       TRACE(-1, "couldn't unlock fifo, configuration remains changed");
-               } else {
-                       cmd1[2] = ((fdc_fifo_state) ? 0 : 0x20) + (fdc_fifo_thr - 1);
-                       result = fdc_command(cmd1, NR_ITEMS(cmd1));
-                       if (result < 0) {
-                               TRACE(-1, "couldn't reconfigure fifo to old state");
-                       } else if (fdc_lock_state) {
-                               result = fdc_issue_command(cmd2, 1, stat, 1);
-                               if (result < 0) {
-                                       TRACE(-1, "couldn't lock old state again");
-                               }
-                       }
-                       TRACEx3(5, "fifo restored: %sabled, thr. %d, %slocked",
-                               fdc_fifo_state ? "en" : "dis",
-                          fdc_fifo_thr, (fdc_lock_state) ? "" : "not ");
-               }
-               fdc_fifo_locked = 0;
-       }
-#ifdef MACH2
-       outb_p(FTAPE_UNIT & 0x0f, fdc.dor);
-       outb_p(FTAPE_UNIT, fdc.dor2);
-       udelay(10);
-       outb_p(FDC_RESET_NOT & 0x0f, fdc.dor);
-       outb_p(FDC_RESET_NOT, fdc.dor2);
-#else
-       outb_p(FTAPE_UNIT, fdc.dor);
-       udelay(10);
-       outb_p(FDC_RESET_NOT, fdc.dor);
-#endif
-       TRACE_EXIT;
-}
-
-/*      Specify FDC seek-rate
- */
-int fdc_set_seek_rate(int seek_rate)
-{
-       byte in[3];
-       const int hut = 1;      /* minimize head unload time */
-       const int hlt = 1;      /* minimize head load time */
-       const int rates[] = {250, 2000, 500, 1000};
-
-       in[0] = FDC_SPECIFY;
-       in[1] = (((16 - (rates[fdc_data_rate & 0x03] * seek_rate) / 500) << 4) |
-                hut);
-       in[2] = (hlt << 1) | 0;
-       fdc_seek_rate = seek_rate;
-
-       return fdc_command(in, 3);
-}
-
-/*      Sense drive status: get unit's drive status (ST3)
- */
-int fdc_sense_drive_status(int *st3)
-{
-       TRACE_FUN(8, "fdc_sense_drive_status");
-       int result;
-       byte out[2];
-       byte in[1];
-
-       out[0] = FDC_SENSED;
-       out[1] = FTAPE_UNIT;
-       result = fdc_issue_command(out, 2, in, 1);
-       if (result < 0) {
-               TRACE(1, "issue_command failed");
-       } else {
-               *st3 = in[0];
-               result = 0;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-/*      Sense Interrupt Status command:
- *      should be issued at the end of each seek.
- *      get ST0 and current cylinder.
- */
-int fdc_sense_interrupt_status(int *st0, int *current_cylinder)
-{
-       TRACE_FUN(8, "fdc_sense_interrupt_status");
-       int result;
-       byte out[1];
-       byte in[2];
-
-       out[0] = FDC_SENSEI;
-       result = fdc_issue_command(out, 1, in, 2);
-       if (result) {
-               TRACE(1, "issue_command failed");
-       } else {
-               *st0 = in[0];
-               *current_cylinder = in[1];
-               result = 0;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-/*      step to track
- */
-int fdc_seek(int track)
-{
-       TRACE_FUN(8, "fdc_seek");
-       int result;
-       byte out[3];
-       int st0, pcn;
-
-       out[0] = FDC_SEEK;
-       out[1] = FTAPE_UNIT;
-       out[2] = track;
-       seek_completed = 0;
-       result = fdc_command(out, 3);
-       if (result != 0) {
-               TRACEi(1, "failed, status =", result);
-               TRACEx1(4, "destination was: %d, resetting FDC...", track);
-               /*  We really need this command to work !
-                */
-               fdc_reset();
-               TRACE_EXIT;
-               return result;
-       }
-       /*    Handle interrupts until seek_completed or timeout.
-        */
-       for (;;) {
-               result = fdc_interrupt_wait(2 * SECOND);
-               if (result < 0) {
-                       TRACEi(2, "fdc_interrupt_wait timeout, status =", result);
-                       TRACE_EXIT;
-                       return result;
-               } else if (seek_completed) {
-                       result = fdc_sense_interrupt_status(&st0, &pcn);
-                       if (result != 0) {
-                               TRACEi(1, "fdc_sense_interrupt_status failed, status =", result);
-                               TRACE_EXIT;
-                               return result;
-                       }
-                       if ((st0 & ST0_SEEK_END) == 0) {
-                               TRACE(1, "no seek-end after seek completion !??");
-                               TRACE_EXIT;
-                               return -EIO;
-                       }
-                       break;
-               }
-       }
-       /*    Verify whether we issued the right tape command.
-        */
-       /* Verify that we seek to the proper track. */
-       if (pcn != track) {
-               TRACE(1, "bad seek..");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       current_cylinder = pcn;
-       TRACE_EXIT;
-       return 0;
-}
-
-/*      Recalibrate and wait until home.
- */
-int fdc_recalibrate(void)
-{
-       TRACE_FUN(8, "fdc_recalibrate");
-       int result;
-       byte out[2];
-       int st0;
-       int pcn;
-       int retry;
-
-       result = fdc_set_seek_rate(6);
-       if (result) {
-               TRACEi(1, "fdc_set_seek_rate failed, status =", result);
-               TRACE_EXIT;
-               return result;
-       }
-       out[0] = FDC_RECAL;
-       out[1] = FTAPE_UNIT;
-       seek_completed = 0;
-       result = fdc_command(out, 2);
-       if (result) {
-               TRACEi(1, "fdc_command failed, status =", result);
-               TRACE_EXIT;
-               return result;
-       }
-       /*    Handle interrupts until seek_completed or timeout.
-        */
-       for (retry = 0;; ++retry) {
-               result = fdc_interrupt_wait(2 * SECOND);
-               if (result < 0) {
-                       TRACE(1, "fdc_interrupt_wait failed");
-                       TRACE_EXIT;
-                       return result;
-               } else if (result == 0 && seek_completed) {
-                       result = fdc_sense_interrupt_status(&st0, &pcn);
-                       if (result != 0) {
-                               TRACEi(1, "fdc_sense_interrupt_status failed, status =", result);
-                               TRACE_EXIT;
-                               return result;
-                       }
-                       if ((st0 & ST0_SEEK_END) == 0) {
-                               if (retry < 1) {
-                                       continue;       /* some drives/fdc's give an extra interrupt */
-                               } else {
-                                       TRACE(1, "no seek-end after seek completion !??");
-                                       TRACE_EXIT;
-                                       return -EIO;
-                               }
-                       }
-                       break;
-               }
-       }
-       current_cylinder = pcn;
-       if (pcn != 0) {
-               TRACEi(1, "failed: resulting track =", pcn);
-       }
-       result = fdc_set_seek_rate(2);
-       if (result != 0) {
-               TRACEi(1, "fdc_set_seek_rate failed, status =", result);
-               TRACE_EXIT;
-               return result;
-       }
-       TRACE_EXIT;
-       return 0;
-}
-
-/*      Setup Floppy Disk Controller and DMA to read or write the next cluster
- *      of good sectors from or to the current segment.
- */
-int setup_fdc_and_dma(buffer_struct * buff, unsigned char operation)
-{
-       TRACE_FUN(8, "setup_fdc_and_dma");
-       unsigned long flags;
-       byte perpend[] = {FDC_PERPEND, 0x00};
-       unsigned char out[9];
-       int result;
-       int dma_mode;
-
-       if (operation == FDC_READ || operation == FDC_READ_DELETED) {
-               dma_mode = DMA_MODE_READ;
-               if (qic_std == QIC_TAPE_QIC3020) {
-                       if (fdc.type < i82077AA) {
-                               /* fdc does not support perpendicular mode. complain */
-                               TRACE(0, "Your FDC does not support QIC-3020.");
-                               return -EIO;
-                       }
-                       /* enable perpendicular mode */
-                       perpend[1] = 0x83 + (0x04 << FTAPE_UNIT);
-                       result = fdc_command(perpend, 2);
-                       if (result < 0) {
-                               TRACE(1, "Perpendicular mode entry failed!");
-                       } else {
-                               TRACE(4, "Perpendicular mode entered");
-                               perpend_mode = 1;
-                       }
-               } else if (perpend_mode) {
-                       /* Turn off perpendicular mode */
-                       perpend[1] = 0x80;
-                       result = fdc_command(perpend, 2);
-                       if (result < 0) {
-                               TRACE(1, "Perpendicular mode exit failed!");
-                       } else {
-                               TRACE(4, "Perpendicular mode exited");
-                               perpend_mode = 0;
-                       }
-               }
-               TRACEx2(5, "xfer %d sectors to 0x%p", buff->sector_count, buff->ptr);
-       } else if (operation == FDC_WRITE || operation == FDC_WRITE_DELETED) {
-               dma_mode = DMA_MODE_WRITE;
-               /* When writing QIC-3020 tapes, turn on perpendicular mode.
-                */
-               if (qic_std == QIC_TAPE_QIC3020) {
-                       if (fdc.type < i82077AA) {
-                               /* fdc does not support perpendicular mode: complain */
-                               TRACE(0, "Your FDC does not support QIC-3020.");
-                               return -EIO;
-                       }
-                       perpend[1] = 0x83 + (0x4 << FTAPE_UNIT);
-                       result = fdc_command(perpend, 2);
-                       if (result < 0) {
-                               TRACE(1, "Perpendicular mode entry failed!");
-                       } else {
-                               TRACE(4, "Perpendicular mode entered");
-                               perpend_mode = 1;
-                       }
-               } else if (perpend_mode) {
-                       perpend[1] = 0x80;
-                       result = fdc_command(perpend, 2);
-                       if (result < 0) {
-                               TRACE(1, "Perpendicular mode exit failed!");
-                       } else {
-                               TRACE(4, "Perpendicular mode exited");
-                               perpend_mode = 0;
-                       }
-               }
-               TRACEx2(5, "xfer %d sectors from 0x%p", buff->sector_count, buff->ptr);
-       } else {
-               TRACE(-1, "bug: illegal operation parameter");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       /* Program the DMA controller.
-        */
-       save_flags(flags);
-       cli();                  /* could be called from ISR ! */
-       disable_dma(fdc.dma);
-       clear_dma_ff(fdc.dma);
-       set_dma_mode(fdc.dma, dma_mode);
-       set_dma_addr(fdc.dma, (unsigned) buff->ptr);
-       set_dma_count(fdc.dma, SECTOR_SIZE * buff->sector_count);
-#ifdef GCC_2_4_5_BUG
-       /*  This seemingly stupid construction confuses the gcc-2.4.5
-        *  code generator enough to create correct code.
-        */
-       if (1) {
-               int i;
-
-               for (i = 0; i < 1; ++i) {
-                       udelay(1);
-               }
-       }
-#endif
-       enable_dma(fdc.dma);
-       /* Issue FDC command to start reading/writing.
-        */
-       out[0] = operation;
-       out[1] = FTAPE_UNIT;
-       out[2] = buff->cyl;
-       out[3] = buff->head;
-       out[4] = buff->sect + buff->sector_offset;
-       out[5] = 3;             /* Sector size of 1K. */
-       out[6] = out[4] + buff->sector_count - 1;       /* last sector */
-       out[7] = 109;           /* Gap length. */
-       out[8] = 0xff;          /* No limit to transfer size. */
-       restore_flags(flags);
-       TRACEx4(6, "C: 0x%02x, H: 0x%02x, R: 0x%02x, cnt: 0x%02x",
-               out[2], out[3], out[4], out[6] - out[4] + 1);
-       result = fdc_command(out, 9);
-       if (result != 0) {
-               fdc_mode = fdc_idle;
-               TRACE(1, "fdc_command failed");
-       }
-       fdc_setup_error = result;
-       TRACE_EXIT;
-       return result;
-}
-
-int fdc_fifo_enable(void)
-{
-       TRACE_FUN(8, "fdc_fifo_enable");
-       int result = 0;
-       byte cmd0[] = {FDC_DUMPREGS};
-       byte cmd1[] = {FDC_CONFIGURE, 0, 0x07, 0}; /* enable fifo, thr = 8 */
-       byte cmd2[] = {FDC_LOCK};
-       byte cmd3[] = {FDC_UNLOCK};
-       byte stat;
-       byte reg[10];
-       int i;
-
-       if (CLK_48MHZ && fdc.type >= i82078)
-               cmd1[0] |= FDC_CLK48_BIT;
-       if (!fdc_fifo_locked) {
-               /*  Dump fdc internal registers for examination
-                */
-               result = fdc_command(cmd0, NR_ITEMS(cmd0));
-               if (result < 0) {
-                       TRACE(2, "FDC dumpreg command failed, fifo unchanged");
-                       result = -EIO;
-               } else {
-                       /*  Now read fdc internal registers from fifo
-                        */
-                       for (i = 0; i < NR_ITEMS(reg); ++i) {
-                               fdc_read(&reg[i]);
-                               TRACEx2(6, "Register %d = 0x%02x", i, reg[i]);
-                       }
-                       fdc_fifo_state = (reg[8] & 0x20) == 0;
-                       fdc_lock_state = reg[7] & 0x80;
-                       fdc_fifo_thr = 1 + (reg[8] & 0x0f);
-                       TRACEx3(5, "original fifo state: %sabled, threshold %d, %slocked",
-                               (fdc_fifo_state) ? "en" : "dis",
-                          fdc_fifo_thr, (fdc_lock_state) ? "" : "not ");
-                       /*  If fdc is already locked, unlock it first !
-                        */
-                       if (fdc_lock_state) {
-                               fdc_ready_wait(100);
-                               result = fdc_command(cmd3, NR_ITEMS(cmd3));
-                               if (result < 0) {
-                                       TRACE(-1, "FDC unlock command failed, configuration unchanged");
-                                       result = -EIO;
-                               }
-                       }
-                       /*  Enable fifo and set threshold at xx bytes to allow a
-                        *  reasonably large latency and reduce number of dma bursts.
-                        */
-                       fdc_ready_wait(100);
-                       result = fdc_command(cmd1, NR_ITEMS(cmd1));
-                       if (result < 0) {
-                               TRACE(-1, "FDC configure command failed, fifo unchanged");
-                               result = -EIO;
-                       } else {
-                               /*  Now lock configuration so reset will not change it
-                                */
-                               result = fdc_issue_command(cmd2, NR_ITEMS(cmd2), &stat, 1);
-                               if (result < 0 || stat != 0x10) {
-                                       TRACEx1(-1, "FDC lock command failed, stat = 0x%02x", stat);
-                                       result = -EIO;
-                               } else {
-                                       fdc_fifo_locked = 1;
-                                       result = 0;
-                               }
-                       }
-               }
-       } else {
-               TRACE(2, "Fifo not enabled because locked");
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-/*   Determine fd controller type 
- */
-static byte fdc_save_state[2] = {0, 0};
-
-int fdc_probe(void)
-{
-       TRACE_FUN(8, "fdc_probe");
-       byte cmd[1];
-       byte stat[16];          /* must be able to hold dumpregs & save results */
-       int result;
-
-       /*  Try to find out what kind of fd controller we have to deal with
-        *  Scheme borrowed from floppy driver:
-        *  first try if FDC_DUMPREGS command works
-        *  (this indicates that we have a 82072 or better)
-        *  then try the FDC_VERSION command (82072 doesn't support this)
-        *  then try the FDC_UNLOCK command (some older 82077's don't support this)
-        *  then try the FDC_PARTID command (82078's support this)
-        */
-       cmd[0] = FDC_DUMPREGS;
-       result = fdc_issue_command(cmd, 1, stat, 1);
-       if (result == 0) {
-               if (stat[0] == 0x80) {
-                       /* invalid command: must be pre 82072
-                        */
-                       TRACE(2, "Type 8272A/765A compatible FDC found");
-                       result = i8272;
-               } else {
-                       fdc_result(&stat[1], 9);
-                       fdc_save_state[0] = stat[7];
-                       fdc_save_state[1] = stat[8];
-                       cmd[0] = FDC_VERSION;
-                       result = fdc_issue_command(cmd, 1, stat, 1);
-                       if (result < 0 || stat[0] == 0x80) {
-                               TRACE(2, "Type 82072 FDC found");
-                               result = i8272;
-                       } else if (*stat == 0x90) {
-                               cmd[0] = FDC_UNLOCK;
-                               result = fdc_issue_command(cmd, 1, stat, 1);
-                               if (result < 0 || stat[0] != 0x00) {
-                                       TRACE(2, "Type pre-1991 82077 FDC found, treating it like a 82072");
-                                       result = i8272;
-                               } else {
-                                       int i;
-
-                                       if (fdc_save_state[0] & 0x80) { /* was locked */
-                                               cmd[0] = FDC_LOCK; /* restore lock */
-                                               result = fdc_issue_command(cmd, 1, stat, 1);
-                                               TRACE(2, "FDC is already locked");
-                                       }
-                                       /* Test for an i82078 FDC */
-                                       cmd[0] = FDC_PARTID;
-                                       result = fdc_issue_command(cmd, 1, stat, 1);
-                                       if (result < 0 || stat[0] == 0x80) {
-                                               /* invalid command: not an i82078xx type FDC */
-                                               result = no_fdc;
-                                               for (i = 0; i < 4; ++i) {
-                                                       outb_p(i, fdc.tdr);
-                                                       if ((inb_p(fdc.tdr) & 0x03) != i) {
-                                                               result = i82077;
-                                                               break;
-                                                       }
-                                               }
-                                               if (result == no_fdc) {
-                                                       result = i82077AA;
-                                                       TRACE(2, "Type 82077AA FDC found");
-                                               } else {
-                                                       TRACE(2, "Type 82077 FDC found");
-                                               }
-                                       } else {
-                                               /* FDC_PARTID cmd succeeded */
-                                               switch (stat[0] >> 5) {
-                                               case 0x0:
-                                                       /* i82078SL or i82078-1.  The SL part cannot run at 2Mbps (the
-                                                        * SL and -1 dies are identical; they are speed graded after
-                                                        * production, according to Intel).  Some SL's can be detected
-                                                        * by doing a SAVE cmd and look at bit 7 of the first byte (the
-                                                        * SEL3V# bit).  If it is 0, the part runs off 3Volts, and hence
-                                                        * it is a SL.
-                                                        */
-                                                       cmd[0] = FDC_SAVE;
-                                                       result = fdc_issue_command(cmd, 1, stat, 16);
-                                                       if (result < 0) {
-                                                               TRACE(1, "FDC_SAVE failed. Dunno why");
-                                                               /* guess we better claim the fdc to be an i82078 */
-                                                               result = i82078;
-                                                               TRACE(2, "Type i82078 FDC (i suppose) found");
-                                                       } else {
-                                                               if ((stat[0] & FDC_SEL3V_BIT)) {
-                                                                       /* fdc running off 5Volts; Pray that it's an i82078-1
-                                                                        */
-                                                                       TRACE(2, "Type i82078-1 or 5Volt i82078SL FDC found");
-                                                                       TRACE(2, "Treating it as an i82078-1 (2Mbps) FDC");
-                                                                       result = i82078_1;
-                                                               } else {
-                                                                       TRACE(2, "Type 3Volt i82078SL FDC (1Mbps) found");
-                                                                       result = i82078;
-                                                               }
-                                                       }
-                                                       break;
-                                               case 0x1:
-                                               case 0x2: /* S82078B (?!) */
-                                                       /* 44pin i82078 found */
-                                                       result = i82078;
-                                                       TRACE(2, "Type i82078 FDC found");
-                                                       break;
-                                               case 0x3: /* NSC PC8744 core; used in several super-IO chips */
-                                                       result = i82077AA;
-                                                       TRACE(2, "Type 82077AA compatible FDC found");
-                                                       break;
-                                               default:
-                                                       TRACE(2, "A previously undetected FDC found");
-                                                       TRACEi(2, "Treating it as a 82077AA. Please report partid=",
-                                                              stat[0]);
-                                                       result = i82077AA;
-                                               } /* switch(stat[ 0] >> 5) */
-                                       } /* if (result < 0 || stat[ 0] == 0x80) */
-                               }
-                       } else {
-                               TRACE(2, "Unknown FDC found");
-                               result = i8272;
-                       }
-               }
-       } else {
-               TRACE(-1, "No FDC found");
-               result = no_fdc;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-void fdc_config_regs(unsigned fdc_base, unsigned fdc_irq, unsigned fdc_dma)
-{
-       fdc.irq = fdc_irq;
-       fdc.dma = fdc_dma;
-       fdc.sra = fdc_base;
-       fdc.srb = fdc_base + 1;
-       fdc.dor = fdc_base + 2;
-       fdc.tdr = fdc_base + 3;
-       fdc.msr = fdc.dsr = fdc_base + 4;
-       fdc.fifo = fdc_base + 5;
-#if defined MACH2 || defined PROBE_FC10
-       fdc.dor2 = fdc_base + 6;
-#endif
-       fdc.dir = fdc.ccr = fdc_base + 7;
-}
-
-/*  If probing for a FC-10/20 controller the fdc base address, interrupt
- *  and dma channel must be specified.
- *  If using an alternate fdc controller, base address, interrupt and
- *  dma channel must be specified.
- */
-#if defined PROBE_FC10 && !defined FDC_BASE
-#error No FDC base address (FDC_BASE) specified in Makefile!
-#endif
-#if defined FDC_BASE && !defined FDC_IRQ
-#error No interrupt (FDC_IRQ) specified in Makefile!
-#endif
-#if defined FDC_BASE && !defined FDC_DMA
-#error No dma channel (FDC_DMA) specified in Makefile!
-#endif
-
-void fdc_config(void)
-{
-       TRACE_FUN(8, "fdc_config");
-       static int already_done = 0;
-
-       if (!already_done) {
-#ifdef PROBE_FC10
-               int fc_type;
-
-               fdc_config_regs(FDC_BASE, FDC_IRQ, FDC_DMA);
-               fc_type = fc10_enable();
-               if (fc_type != 0) {
-                       TRACEx1(2, "FC-%c0 controller found", '0' + fc_type);
-                       fdc.type = fc10;
-                       fdc.hook = &do_ftape;
-               } else {
-                       TRACE(2, "FC-10/20 controller not found");
-                       fdc.type = no_fdc;
-                       fdc.dor2 = 0;   /* not used with std fdc */
-                       fdc_config_regs(0x3f0, 6, 2);   /* back to std fdc again */
-                       fdc.hook = &do_ftape;
-               }
-#else
-#ifdef FDC_BASE
-               TRACE(2, "Using fdc controller at alternate address");
-               fdc_config_regs(FDC_BASE, FDC_IRQ, FDC_DMA);
-               fdc.hook = &do_ftape;
-#else
-               TRACE(2, "Using the standard fdc controller");
-               fdc_config_regs(0x3f0, 6, 2);   /* std fdc */
-               fdc.hook = &do_ftape;
-#endif /* !FDC_BASE */
-#endif /* !PROBE_FC10 */
-       }
-       *(fdc.hook) = fdc_isr;  /* hook our handler in */
-       already_done = 1;
-       TRACE_EXIT;
-}
-
-static void ftape_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-       TRACE_FUN(8, "ftape_interrupt");
-       void (*handler) (void) = *fdc.hook;
-
-       *fdc.hook = NULL;
-       if (handler) {
-               handler();
-       } else {
-               TRACE(-1, "Unexpected ftape interrupt");
-       }
-       TRACE_EXIT;
-}
-
-int fdc_grab_irq_and_dma(void)
-{
-       TRACE_FUN(8, "fdc_grab_irq_and_dma");
-       int result = 0;
-
-       if (fdc.hook == &do_ftape) {
-               /*  Get fast interrupt handler.
-                */
-               result = request_irq(fdc.irq, ftape_interrupt, SA_INTERRUPT,
-                                    "ftape", ftape_id);
-               if (result) {
-                       TRACEx1(-1, "Unable to grab IRQ%d for ftape driver", fdc.irq);
-                       result = -EIO;
-               } else {
-                       result = request_dma(fdc.dma, ftape_id);
-                       if (result) {
-                               TRACEx1(-1, "Unable to grab DMA%d for ftape driver", fdc.dma);
-                               free_irq(fdc.irq, ftape_id);
-                               result = -EIO;
-                       } else {
-                               enable_irq(fdc.irq);
-                       }
-               }
-       }
-#ifdef FDC_DMA
-       if (result == 0 && FDC_DMA == 2) {
-               /*  Using same dma channel as standard fdc, need to disable the
-                *  dma-gate on the std fdc. This couldn't be done in the floppy
-                *  driver as some laptops are using the dma-gate to enter a
-                *  low power or even suspended state :-(
-                */
-               outb_p(FDC_RESET_NOT, 0x3f2);
-               TRACE(2, "DMA-gate on standard fdc disabled");
-       }
-#endif
-       TRACE_EXIT;
-       return result;
-}
-
-int fdc_release_irq_and_dma(void)
-{
-       TRACE_FUN(8, "fdc_grab_irq_and_dma");
-       int result = 0;
-
-       if (fdc.hook == &do_ftape) {
-               disable_dma(fdc.dma);   /* just in case... */
-               free_dma(fdc.dma);
-               disable_irq(fdc.irq);
-               free_irq(fdc.irq, ftape_id);
-       }
-#ifdef FDC_DMA
-       if (result == 0 && FDC_DMA == 2) {
-               /*  Using same dma channel as standard fdc, need to disable the
-                *  dma-gate on the std fdc. This couldn't be done in the floppy
-                *  driver as some laptops are using the dma-gate to enter a
-                *  low power or even suspended state :-(
-                */
-               outb_p(FDC_RESET_NOT | FDC_DMA_MODE, 0x3f2);
-               TRACE(2, "DMA-gate on standard fdc enabled again");
-       }
-#endif
-       TRACE_EXIT;
-       return result;
-}
-
-int fdc_uninit(void)
-{
-       TRACE_FUN(8, "fdc_uninit");
-       int result = 0;
-
-       if (fdc.sra != 0) {
-               if (fdc.dor2 == 0) {
-                       release_region(fdc.sra, 6);
-                       release_region(fdc.sra + 7, 1);
-               } else {
-                       release_region(fdc.sra, 8);
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int fdc_init(void)
-{
-       TRACE_FUN(8, "fdc_init");
-       int result = 0;
-
-       fdc_config();
-       if (fdc_grab_irq_and_dma() < 0) {
-               result = -EBUSY;
-       } else {
-               ftape_motor = 0;
-               fdc_catch_stray_interrupts(1);  /* one always comes */
-               TRACE(5, "resetting fdc");
-               fdc_reset();    /* init fdc & clear track counters */
-               if (fdc.type == no_fdc) {       /* default, means no FC-10 or 20 found */
-                       fdc.type = fdc_probe();
-               }
-               if (fdc.type != no_fdc) {
-                       if (fdc.type >= i82077) {
-                               if (fdc_fifo_enable() < 0) {
-                                       TRACE(2, "couldn't enable fdc fifo !");
-                               } else {
-                                       TRACE(5, "fdc fifo enabled and locked");
-                               }
-                       }
-               } else {
-                       fdc_release_irq_and_dma();
-                       result = -EIO;
-               }
-       }
-       if (result >= 0) {
-               if (fdc.dor2 == 0) {
-                       request_region(fdc.sra, 6, "fdc (ftape)");
-                       request_region(fdc.sra + 7, 1, "fdc (ftape)");
-               } else {
-                       request_region(fdc.sra, 8, "fdc (ftape)");
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
diff --git a/drivers/char/ftape/fdc-io.h b/drivers/char/ftape/fdc-io.h
deleted file mode 100644 (file)
index 083eab7..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-#ifndef _FDC_IO_H
-#define _FDC_IO_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/fdc-io.h,v $
- $Author: bas $
- *
- $Revision: 1.38 $
- $Date: 1995/05/10 16:09:36 $
- $State: Beta $
- *
- *      This file contains the low level functions
- *      that communicate with the floppy disk controller,
- *      for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/fdreg.h>
-
-#define FDC_SK_BIT      (0x20)
-#define FDC_MT_BIT      (0x80)
-
-#define FDC_READ        (FD_READ & ~(FDC_SK_BIT | FDC_MT_BIT))
-#define FDC_WRITE       (FD_WRITE & ~FDC_MT_BIT)
-#define FDC_READ_DELETED (0x4c)
-#define FDC_WRITE_DELETED (0x49)
-#define FDC_READID      (0x4a)
-#define FDC_SENSED      (0x04)
-#define FDC_SENSEI      (FD_SENSEI)
-#define FDC_RECAL       (FD_RECALIBRATE)
-#define FDC_SEEK        (FD_SEEK)
-#define FDC_SPECIFY     (FD_SPECIFY)
-#define FDC_RECALIBR    (FD_RECALIBRATE)
-#define FDC_VERSION     (FD_VERSION)
-#define FDC_PERPEND     (FD_PERPENDICULAR)
-#define FDC_DUMPREGS    (FD_DUMPREGS)
-#define FDC_LOCK        (FD_LOCK)
-#define FDC_UNLOCK      (FD_UNLOCK)
-#define FDC_CONFIGURE   (FD_CONFIGURE)
-#define FDC_DRIVE_SPEC  (0x8e) /* i82078 has this (any others?) */
-#define FDC_PARTID      (0x18) /* i82078 has this */
-#define FDC_SAVE        (0x2e) /* i82078 has this (any others?) */
-#define FDC_RESTORE     (0x4e) /* i82078 has this (any others?) */
-
-#define FDC_STATUS_MASK (STATUS_BUSY | STATUS_DMA | STATUS_DIR | STATUS_READY)
-#define FDC_DATA_READY  (STATUS_READY)
-#define FDC_DATA_OUTPUT (STATUS_DIR)
-#define FDC_DATA_READY_MASK (STATUS_READY | STATUS_DIR)
-#define FDC_DATA_OUT_READY  (STATUS_READY | STATUS_DIR)
-#define FDC_DATA_IN_READY   (STATUS_READY)
-#define FDC_BUSY        (STATUS_BUSY)
-#define FDC_CLK48_BIT   (0x80)
-#define FDC_SEL3V_BIT   (0x40)
-
-#define ST0_INT_MASK    (ST0_INTR)
-#define FDC_INT_NORMAL  (ST0_INTR & 0x00)
-#define FDC_INT_ABNORMAL (ST0_INTR & 0x40)
-#define FDC_INT_INVALID (ST0_INTR & 0x80)
-#define FDC_INT_READYCH (ST0_INTR & 0xC0)
-#define ST0_SEEK_END    (ST0_SE)
-#define ST3_TRACK_0     (ST3_TZ)
-
-#define FDC_RESET_NOT   (0x04)
-#define FDC_DMA_MODE    (0x08)
-#define FDC_MOTOR_0     (0x10)
-#define FDC_MOTOR_1     (0x20)
-
-typedef struct {
-       void (**hook) (void);   /* our wedge into the isr */
-       enum {
-               no_fdc, i8272, i82077, i82077AA, fc10,
-               i82078, i82078_1
-       } type;                 /* FDC type */
-       unsigned char irq;      /* FDC irq nr */
-       unsigned char dma;      /* FDC dma channel nr */
-       unsigned short sra;     /* Status register A (PS/2 only) */
-       unsigned short srb;     /* Status register B (PS/2 only) */
-       unsigned short dor;     /* Digital output register */
-       unsigned short tdr;     /* Tape Drive Register (82077SL-1 &
-                                  82078 only) */
-       unsigned short msr;     /* Main Status Register */
-       unsigned short dsr;     /* Datarate Select Register (8207x only) */
-       unsigned short fifo;    /* Data register / Fifo on 8207x */
-       unsigned short dir;     /* Digital Input Register */
-       unsigned short ccr;     /* Configuration Control Register */
-       unsigned short dor2;    /* Alternate dor on MACH-2 controller,
-                                  also used with FC-10, meaning unknown */
-} fdc_config_info;
-
-typedef enum {
-       fdc_data_rate_250 = 2,
-       fdc_data_rate_500 = 0,
-       fdc_data_rate_1000 = 3,
-       fdc_data_rate_2000 = 1, /* i82078-1: remember to use Data Rate Table #2 */
-} fdc_data_rate_type;
-
-typedef enum {
-       waiting = 0,
-       reading,
-       writing,
-       done,
-       error,
-} buffer_state_enum;
-
-typedef volatile enum {
-       fdc_idle = 0,
-       fdc_reading_data = FDC_READ,
-       fdc_seeking = FDC_SEEK,
-       fdc_writing_data = FDC_WRITE,
-       fdc_reading_id = FDC_READID,
-       fdc_recalibrating = FDC_RECAL,
-} fdc_mode_enum;
-
-/*
- *      fdc-io.c defined public variables
- */
-extern fdc_mode_enum fdc_mode;
-extern volatile enum runner_status_enum runner_status;
-extern int old_vfo;
-extern volatile int head;
-extern volatile int tail;
-extern int fdc_setup_error;    /* outdated ??? */
-extern struct wait_queue *wait_intr;
-extern volatile unsigned int next_segment;     /* next segment for read ahead */
-extern int ftape_unit;         /* fdc unit specified at ftape_open() */
-extern int ftape_motor;                /* fdc motor line state */
-extern int current_cylinder;   /* track nr the FDC thinks we're on */
-extern volatile byte fdc_head; /* FDC head */
-extern volatile byte fdc_cyl;  /* FDC track */
-extern volatile byte fdc_sect; /* FDC sector */
-extern fdc_config_info fdc;    /* FDC hardware configuration */
-
-/*
- *      fdc-io.c defined public functions
- */
-extern void fdc_catch_stray_interrupts(unsigned count);
-extern int fdc_ready_wait(int timeout);
-extern int fdc_write(byte data);
-extern int fdc_read(byte * data);
-extern int fdc_command(byte * cmd_data, int cmd_len);
-extern int fdc_result(byte * res_data, int res_len);
-extern int fdc_issue_command(byte * out_data, int out_count, \
-                            byte * in_data, int in_count);
-extern void fdc_isr(void);
-extern int fdc_interrupt_wait(int time);
-extern void fdt_sleep(unsigned int time);
-extern int fdc_specify(int head_unload_time, int seek_rate,
-                      int head_load_time, int non_dma);
-extern int fdc_set_seek_rate(int seek_rate);
-extern int fdc_seek(int track);
-extern int fdc_sense_drive_status(int *st3);
-extern void fdc_motor(int motor);
-extern void fdc_reset(void);
-extern int fdc_recalibrate(void);
-extern void fdc_disable(void);
-extern int fdc_wait_calibrate(void);
-extern int fdc_sense_interrupt_status(int *st0, int *current_cylinder);
-extern void fdc_save_drive_specs(void);
-extern void fdc_restore_drive_specs(void);
-extern void fdc_set_data_rate(int rate);
-extern int fdc_release_irq_and_dma(void);
-extern int fdc_init(void);
-extern int fdc_uninit(void);
-extern void fdc_set_write_precomp(int precomp);
-
-#endif
diff --git a/drivers/char/ftape/fdc-isr.c b/drivers/char/ftape/fdc-isr.c
deleted file mode 100644 (file)
index 0fda859..0000000
+++ /dev/null
@@ -1,813 +0,0 @@
-/*
- *      Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/fdc-isr.c,v $
- $Author: bas $
- *
- $Revision: 1.36 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- *      This file contains the interrupt service routine and associated
- *      code for the QIC-40/80 tape streamer device driver.
- */
-
-#include <linux/ftape.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#define volatile               /* */
-
-#include "tracing.h"
-#include "fdc-isr.h"
-#include "qic117.h"
-#include "fdc-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-io.h"
-#include "calibr.h"
-#include "ftape-bsm.h"
-
-/*      Global vars.
- */
-volatile int expected_stray_interrupts = 0;
-volatile int seek_completed = 0;
-volatile int interrupt_seen = 0;
-volatile int expect_stray_interrupt = 0;
-int random_rw = 0;
-
-/*      Local vars.
- */
-typedef enum {
-       no_error = 0, id_am_error = 0x01, id_crc_error = 0x02,
-       data_am_error = 0x04, data_crc_error = 0x08,
-       no_data_error = 0x10, overrun_error = 0x20,
-} error_cause;
-static int hide_interrupt;
-static int stop_read_ahead = 0;
-
-
-static void print_error_cause(int cause)
-{
-       TRACE_FUN(8, "print_error_cause");
-
-       switch (cause) {
-       case no_data_error:
-               TRACE(4, "no data error");
-               break;
-       case id_am_error:
-               TRACE(4, "id am error");
-               break;
-       case id_crc_error:
-               TRACE(4, "id crc error");
-               break;
-       case data_am_error:
-               TRACE(4, "data am error");
-               break;
-       case data_crc_error:
-               TRACE(4, "data crc error");
-               break;
-       case overrun_error:
-               TRACE(4, "overrun error");
-               break;
-       default:
-       }
-       TRACE_EXIT;
-}
-
-static char *
-get_fdc_mode_text(fdc_mode_enum fdc_mode)
-{
-       switch (fdc_mode) {
-       case fdc_idle:
-               return "fdc_idle";
-       case fdc_reading_data:
-               return "fdc_reading_data";
-       case fdc_seeking:
-               return "fdc_seeking";
-       case fdc_writing_data:
-               return "fdc_writing_data";
-       case fdc_reading_id:
-               return "fdc_reading_id";
-       case fdc_recalibrating:
-               return "fdc_recalibrating";
-       default:
-               return "unknown";
-       }
-}
-
-static void
-decode_irq_cause(fdc_mode_enum fdc_mode, byte st[],
-                char **fdc_mode_txt, error_cause * cause)
-{
-       TRACE_FUN(8, "decode_irq_cause");
-
-       /*  Valid st[], decode cause of interrupt.
-        */
-       *fdc_mode_txt = get_fdc_mode_text(fdc_mode);
-       switch (st[0] & ST0_INT_MASK) {
-       case FDC_INT_NORMAL:
-               TRACEx1(fdc_mode == fdc_reading_id ? 6 : 5,
-                       "normal completion: %s", *fdc_mode_txt);
-               *cause = no_error;
-               break;
-       case FDC_INT_ABNORMAL:
-               TRACEx1(5, "abnormal completion %s", *fdc_mode_txt);
-               TRACEx3(6, "ST0: 0x%02x, ST1: 0x%02x, ST2: 0x%02x",
-                       st[0], st[1], st[2]);
-               TRACEx4(6, "C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x",
-                       st[3], st[4], st[5], st[6]);
-               if (st[1] & 0x01) {
-                       if (st[2] & 0x01) {
-                               *cause = data_am_error;
-                       } else {
-                               *cause = id_am_error;
-                       }
-               } else if (st[1] & 0x20) {
-                       if (st[2] & 0x20) {
-                               *cause = data_crc_error;
-                       } else {
-                               *cause = id_crc_error;
-                       }
-               } else if (st[1] & 0x04) {
-                       *cause = no_data_error;
-               } else if (st[1] & 0x10) {
-                       *cause = overrun_error;
-               }
-               print_error_cause(*cause);
-               break;
-       case FDC_INT_INVALID:
-               TRACEx1(5, "invalid completion %s", *fdc_mode_txt);
-               *cause = no_error;
-               break;
-       case FDC_INT_READYCH:
-               TRACEx1(5, "ready change %s", *fdc_mode_txt);
-               *cause = no_error;
-               break;
-       default:
-       }
-       TRACE_EXIT;
-}
-
-static void update_history(error_cause cause)
-{
-       switch (cause) {
-       case id_am_error:
-               history.id_am_errors++;
-               break;
-       case id_crc_error:
-               history.id_crc_errors++;
-               break;
-       case data_am_error:
-               history.data_am_errors++;
-               break;
-       case data_crc_error:
-               history.data_crc_errors++;
-               break;
-       case overrun_error:
-               history.overrun_errors++;
-               break;
-       case no_data_error:
-               history.no_data_errors++;
-               break;
-       default:
-       }
-}
-
-static void skip_bad_sector(buffer_struct * buff)
-{
-       TRACE_FUN(8, "skip_bad_sector");
-
-       /* Mark sector as soft error and skip it
-        */
-       if (buff->remaining > 0) {
-               ++buff->sector_offset;
-               ++buff->data_offset;
-               --buff->remaining;
-               buff->ptr += SECTOR_SIZE;
-               buff->bad_sector_map >>= 1;
-       } else {
-               ++buff->sector_offset;  /* hack for error maps */
-               TRACE(1, "skipping last sector in segment");
-       }
-       TRACE_EXIT;
-}
-
-static void update_error_maps(buffer_struct * buff, unsigned error_offset)
-{
-       TRACE_FUN(8, "update_error_maps");
-       int hard = 0;
-
-       /* error_offset is a sector offset !
-        */
-       if (buff->retry < SOFT_RETRIES) {
-               buff->soft_error_map |= (1 << error_offset);
-       } else {
-               buff->hard_error_map |= (1 << error_offset);
-               buff->soft_error_map &= ~buff->hard_error_map;
-               buff->retry = -1;       /* will be set to 0 in setup_segment */
-               hard = 1;
-       }
-       TRACEx2(4, "sector %d : %s error", SECTOR(error_offset),
-               hard ? "hard" : "soft");
-       TRACEx2(5, "hard map: 0x%08lx, soft map: 0x%08lx",
-               buff->hard_error_map, buff->soft_error_map);
-       TRACE_EXIT;
-}
-
-/*
- *  Error cause:   Amount xferred:  Action:
- *
- *  id_am_error         0           mark bad and skip
- *  id_crc_error        0           mark bad and skip
- *  data_am_error       0           mark bad and skip
- *  data_crc_error    % 1024        mark bad and skip
- *  no_data_error       0           retry on write
- *                                  mark bad and skip on read
- *  overrun_error  [ 0..all-1 ]     mark bad and skip
- *  no_error           all          continue
- */
-static void determine_progress(buffer_struct * buff, error_cause cause, int mode)
-{
-       TRACE_FUN(8, "determine_progress");
-       unsigned nr_not_xferred;
-       unsigned nr_xferred;
-       unsigned dma_residue;
-
-       /*  Using less preferred order of disable_dma and get_dma_residue
-        *  because this seems to fail on at least one system if reversed!
-        */
-       dma_residue = get_dma_residue(fdc.dma);
-       disable_dma(fdc.dma);
-       nr_xferred = buff->sector_count * SECTOR_SIZE - dma_residue;
-       if (cause == no_error && dma_residue == 0) {
-               nr_not_xferred = 0;
-       } else {
-               if (cause == no_error) {
-                       TRACEx1(4, "unexpected DMA residue: 0x%04x", dma_residue);
-               } else {
-                       TRACEx1(6, "DMA residue = 0x%04x", dma_residue);
-               }
-               nr_not_xferred = ((dma_residue + (SECTOR_SIZE - 1)) / SECTOR_SIZE);
-               buff->sector_count -= nr_not_xferred;   /* adjust to actual value */
-       }
-       /*  Update var's influenced by the DMA operation.
-        */
-       if (buff->sector_count > 0) {
-               buff->sector_offset += buff->sector_count;
-               buff->data_offset += buff->sector_count;
-               buff->ptr += buff->sector_count * SECTOR_SIZE;
-               buff->remaining -= buff->sector_count;
-               buff->bad_sector_map >>= buff->sector_count;
-       }
-       if (cause == no_error) {
-               TRACEx1(5, "%d Sector(s) transferred", buff->sector_count);
-       } else if (cause == no_data_error) {
-               TRACEx1(5, "Sector %d not found", SECTOR(buff->sector_offset));
-       } else if (nr_xferred > 0 || cause == id_crc_error ||
-                  cause == id_am_error || cause == data_am_error) {
-               TRACEx1(5, "Error in sector %d", SECTOR(buff->sector_offset));
-       } else if (cause == overrun_error) {
-               /* got an overrun error on the first byte, must be a hardware problem
-                */
-               TRACE(-1, "Unexpected error: failing DMA controller ?");
-       } else {
-               TRACEx1(4, "Unexpected error at sector %d", SECTOR(buff->sector_offset));
-       }
-       /*  Sector_offset points to the problem area, except if we got
-        *  a data_crc_error. In that case it points one past the failing
-        *  sector.
-        *  Now adjust sector_offset so it always points one past he
-        *  failing sector. I.e. skip the bad sector.
-        */
-       if (cause != no_error) {
-               if (cause != data_crc_error) {
-                       skip_bad_sector(buff);
-               }
-               update_error_maps(buff, buff->sector_offset - 1);
-       }
-       TRACE_EXIT;
-}
-
-static int calc_steps(int cmd)
-{
-       if (current_cylinder > cmd) {
-               return current_cylinder - cmd;
-       } else {
-               return current_cylinder + cmd;
-       }
-}
-
-static void pause_tape(unsigned segment, int retry, int fdc_mode)
-{
-       TRACE_FUN(8, "pause_tape");
-       int result;
-       /*  The 3rd initializer needs to be explicit or else gcc will
-        *  generate a reference to memset :-(
-        */
-       byte out[3] =
-       {FDC_SEEK, FTAPE_UNIT, 0};
-
-       /*  We'll use a raw seek command to get the tape to rewind
-        *  and stop for a retry.
-        */
-       ++history.rewinds;
-       if (qic117_cmds[current_command].non_intr) {
-               TRACE(2, "motion command may be issued too soon");
-       }
-       if (retry && (fdc_mode == fdc_reading_data || fdc_mode == fdc_reading_id)) {
-               current_command = QIC_MICRO_STEP_PAUSE;
-               might_be_off_track = 1;
-       } else {
-               current_command = QIC_PAUSE;
-       }
-       out[2] = calc_steps(current_command);
-       result = fdc_command(out, 3);   /* issue QIC_117 command */
-       if (result < 0) {
-               TRACEx1(4, "qic-pause failed, status = %d", result);
-       } else {
-               location.known = 0;
-               runner_status = idle;
-               hide_interrupt = 1;
-               tape_running = 0;
-       }
-       TRACE_EXIT;
-}
-
-static void stop_tape(unsigned segment)
-{
-       TRACE_FUN(8, "stop_tape");
-       int result;
-       byte out[3] =
-       {FDC_SEEK, FTAPE_UNIT, calc_steps(QIC_STOP_TAPE)};
-
-       if (qic117_cmds[current_command].non_intr) {
-               TRACE(2, "motion command may be issued too soon");
-       }
-       current_command = QIC_STOP_TAPE;
-       /*  We'll use a raw seek command to get the tape to stop
-        */
-       result = fdc_command(out, 3);   /* issue QIC_117 command */
-       if (result < 0) {
-               TRACEx1(4, "qic-stop failed, status = %d", result);
-       } else {
-               runner_status = idle;
-               hide_interrupt = 1;
-               tape_running = 0;
-       }
-       TRACE_EXIT;
-}
-
-static void continue_xfer(buffer_struct ** p_buff, error_cause cause,
-                         int fdc_mode, unsigned skip)
-{
-       TRACE_FUN(8, "continue_xfer");
-       buffer_struct *buff = *p_buff;
-       int write = (fdc_mode == fdc_writing_data);
-       byte fdc_op = (write) ? FDC_WRITE : FDC_READ;
-
-       if (skip > 0) {
-               /* This part can be removed if it never happens
-                */
-               if (runner_status != running ||
-                   (buff->status != (write ? writing : reading))) {
-                       TRACEx2(1, "unexpected runner/buffer state %d/%d",
-                               runner_status, buff->status);
-                       buff->status = error;
-                       *p_buff = next_buffer(&head);   /* finish this buffer */
-                       runner_status = aborting;
-                       fdc_mode = fdc_idle;
-               }
-       }
-       if (buff->remaining > 0 && calc_next_cluster(&buffer[head]) > 0) {
-               /* still sectors left in current segment, continue with this segment
-                */
-               if (setup_fdc_and_dma(&buffer[head], fdc_op) < 0) {
-                       /* failed, abort operation
-                        */
-                       buff->bytes = buff->ptr - buff->address;
-                       buff->status = error;
-                       buff = *p_buff = next_buffer(&head);    /* finish this buffer */
-                       runner_status = aborting;
-                       fdc_mode = fdc_idle;
-               }
-       } else {
-               /* current segment completed
-                */
-               unsigned last_segment = buff->segment_id;
-               int eot = ((last_segment + 1) % segments_per_track) == 0;
-               int next = buff->next_segment;  /* 0 means stop ! */
-
-               buff->bytes = buff->ptr - buff->address;
-               buff->status = done;
-               buff = *p_buff = next_buffer(&head);
-               if (eot) {
-                       /* finished last segment on current track, can't continue
-                        */
-                       runner_status = logical_eot;
-                       fdc_mode = fdc_idle;
-               } else if (next > 0) {
-                       /* continue with next segment
-                        */
-                       if (buff->status == waiting) {
-                               if (write && next != buff->segment_id) {
-                                       TRACE(5, "segments out of order, aborting write");
-                                       runner_status = do_abort;
-                                       fdc_mode = fdc_idle;
-                               } else {
-                                       setup_new_segment(&buffer[head], next, 0);
-                                       if (stop_read_ahead) {
-                                               buff->next_segment = 0;
-                                               stop_read_ahead = 0;
-                                       }
-                                       if (calc_next_cluster(&buffer[head]) == 0 ||
-                                           setup_fdc_and_dma(&buffer[head], fdc_op) != 0) {
-                                               TRACEx1(1, "couldn't start %s-ahead", (write) ? "write" : "read");
-                                               runner_status = do_abort;
-                                               fdc_mode = fdc_idle;
-                                       } else {
-                                               buff->status = (write) ? writing : reading;     /* keep on going */
-                                       }
-                               }
-                       } else {
-                               TRACEx1(5, "all input buffers %s, pausing tape",
-                                       (write) ? "empty" : "full");
-                               pause_tape(last_segment, 0, fdc_mode);
-                               runner_status = idle;   /* not quite true until next irq */
-                       }
-               } else {
-                       /* don't continue with next segment
-                        */
-                       TRACEx1(5, "no %s allowed, stopping tape",
-                               (write) ? "write next" : "read ahead");
-                       if (random_rw) {
-                               stop_tape(last_segment);
-                       } else {
-                               pause_tape(last_segment, 0, fdc_mode);
-                       }
-                       runner_status = idle;   /* not quite true until next irq */
-               }
-       }
-       TRACE_EXIT;
-       return;
-}
-
-static void
-retry_sector(buffer_struct ** p_buff, error_cause cause, int fdc_mode,
-            unsigned skip)
-{
-       TRACE_FUN(8, "retry_sector");
-       buffer_struct *buff = *p_buff;
-
-       TRACEx1(4, "%s error, will retry",
-               (fdc_mode == fdc_writing_data) ? "write" : "read");
-       pause_tape(buff->segment_id, 1, fdc_mode);
-       runner_status = aborting;
-       buff->status = error;
-       buff->skip = skip;
-       TRACE_EXIT;
-}
-
-static unsigned
-find_resume_point(buffer_struct * buff)
-{
-       TRACE_FUN(8, "find_resume_point");
-       int i = 0;
-       unsigned long mask;
-       unsigned long map;
-
-       /*  This function is to be called after all variables have been
-        *  updated to point past the failing sector.
-        *  If there are any soft errors before the failing sector,
-        *  find the first soft error and return the sector offset.
-        *  Otherwise find the last hard error.
-        *  Note: there should always be at least one hard or soft error !
-        */
-       if (buff->sector_offset < 1 || buff->sector_offset > 32) {
-               TRACEx1(1, "bug: sector_offset = %d", buff->sector_offset);
-       } else {
-               if (buff->sector_offset >= 32) {        /* C-limitation on shift ! */
-                       mask = 0xffffffff;
-               } else {
-                       mask = (1 << buff->sector_offset) - 1;
-               }
-               map = buff->soft_error_map & mask;
-               if (map) {
-                       while ((map & (1 << i)) == 0) {
-                               ++i;
-                       }
-                       TRACEx1(4, "at sector %d", SECTOR(i));
-               } else {
-                       map = buff->hard_error_map & mask;
-                       i = buff->sector_offset - 1;
-                       if (map) {
-                               while ((map & (1 << i)) == 0) {
-                                       --i;
-                               }
-                               TRACEx1(4, "after sector %d", SECTOR(i));
-                               ++i;    /* first sector after last hard error */
-                       } else {
-                               TRACE(1, "bug: no soft or hard errors");
-                       }
-               }
-       }
-       TRACE_EXIT;
-       return i;
-}
-
-/*      FDC interrupt service routine.
- */
-void
-fdc_isr(void)
-{
-       TRACE_FUN(8, "fdc_isr");
-       int result;
-       int status;
-       error_cause cause = no_error;
-       byte in[7];
-       static int isr_active = 0;
-       int t0;
-       buffer_struct *buff = &buffer[head];
-       int skip;
-
-       t0 = timestamp();
-       if (isr_active) {
-               TRACE(-1, "nested interrupt, not good !");
-               *fdc.hook = fdc_isr;    /* hook our handler into the fdc code again */
-               TRACE_EXIT;
-               return;
-       }
-       ++isr_active;
-       sti();                  /* enables interrupts again */
-       status = inb_p(fdc.msr);
-       if (status & FDC_BUSY) {        /*  Entering Result Phase */
-               hide_interrupt = 0;
-               result = fdc_result(in, 7);     /* better get it fast ! */
-               if (result < 0) {
-                       /*  Entered unknown state...
-                        */
-                       TRACE(1, "probably fatal error during FDC Result Phase");
-                       TRACE(1, "drive may hang until (power) reset :-(");
-                       /*  what to do next ????
-                        */
-               } else {
-                       int i;
-                       char *fdc_mode_txt;
-
-                       decode_irq_cause(fdc_mode, in, &fdc_mode_txt, &cause);
-                       for (i = 0; i < NR_BUFFERS; ++i) {
-                               TRACEx3(8, "buffer[%d] status: %d, segment_id: %d",
-                                       i, buffer[i].status, buffer[i].segment_id);
-                       }
-                       switch (fdc_mode) {
-
-                       case fdc_reading_data:{
-
-                                       if (cause == no_error) {
-                                               TRACEi(5, "reading segment", buff->segment_id);
-                                       } else {
-                                               TRACEi(4, "error reading segment", buff->segment_id);
-                                       }
-                                       if (runner_status == aborting || runner_status == do_abort) {
-                                               TRACEx1(4, "aborting %s", fdc_mode_txt);
-                                               break;
-                                       }
-                                       if (buff->retry > 0) {
-                                               TRACEx1(5, "this is retry nr %d", buff->retry);
-                                       }
-                                       if (buff->bad_sector_map == FAKE_SEGMENT) {
-                                               /* This condition occurs when reading a `fake' sector that's
-                                                * not accessible. Doesn't really matter as we would have
-                                                * ignored it anyway !
-                                                * Chance is that we're past the next segment now, so the
-                                                * next operation may fail and result in a retry.
-                                                */
-                                               TRACE(4, "skipping empty segment (read)");
-                                               buff->remaining = 0;    /* skip failing sector */
-                                               continue_xfer(&buff, no_error, fdc_mode, 1);    /* fake success */
-                                       } else {
-                                               switch (cause) {
-                                               case no_error:{
-                                                               determine_progress(buff, cause, fdc_reading_data);
-                                                               if (in[2] & 0x40) {
-                                                                       /*  Handle deleted data in header segments.
-                                                                        *  Skip segment and force read-ahead.
-                                                                        */
-                                                                       TRACEx1(2, "deleted data in sector %d",
-                                                                               SECTOR(buff->sector_offset - 1));
-                                                                       buff->deleted = 1;
-                                                                       buff->remaining = 0;    /* abort transfer */
-                                                                       buff->soft_error_map |= (-1L << buff->sector_offset);
-                                                                       if (buff->segment_id == 0) {
-                                                                               stop_read_ahead = 1;    /* stop on next segment */
-                                                                       }
-                                                                       buff->next_segment = buff->segment_id + 1;      /* force read-ahead */
-                                                                       skip = (SECTORS_PER_SEGMENT - buff->sector_offset);
-                                                               } else {
-                                                                       skip = 0;
-                                                               }
-                                                               continue_xfer(&buff, cause, fdc_mode, skip);
-                                                               break;
-                                                       }
-                                               case no_data_error:
-                                                       /*  Tape started too far ahead of or behind the right sector.
-                                                        *  This may also happen in the middle of a segment !
-                                                        *  Handle no-data as soft error. If next sector fails too,
-                                                        *  a retry (with needed reposition) will follow.
-                                                        */
-                                               case id_am_error:
-                                               case id_crc_error:
-                                               case data_am_error:
-                                               case data_crc_error:
-                                               case overrun_error:{
-                                                               int first_error = (buff->soft_error_map == 0 &&
-                                                                                  buff->hard_error_map == 0);
-
-                                                               update_history(cause);
-                                                               determine_progress(buff, cause, fdc_reading_data);
-                                                               if (first_error) {
-                                                                       skip = buff->sector_offset;
-                                                               } else {
-                                                                       skip = find_resume_point(buff);
-                                                               }
-                                                               /*  Try to resume with next sector on single errors (let ecc
-                                                                *  correct it), but retry on no_data (we'll be past the
-                                                                *  target when we get here so we cannot retry) or on multiple
-                                                                *  errors (reduce chance on ecc failure).
-                                                                */
-                                                               if (first_error && cause != no_data_error) {
-                                                                       continue_xfer(&buff, cause, fdc_mode, skip);
-                                                               } else {
-                                                                       retry_sector(&buff, cause, fdc_mode, skip);
-                                                               }
-                                                               break;
-                                                       }
-                                               default:{
-                                                               /*  Don't know why this could happen but find out.
-                                                                */
-                                                               TRACE(1, "unexpected error");
-                                                               determine_progress(buff, cause, fdc_reading_data);
-                                                               retry_sector(&buff, cause, fdc_mode, 0);
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                                       break;
-                               }
-
-                       case fdc_reading_id:{
-
-                                       if (cause == no_error) {
-                                               fdc_cyl = in[3];
-                                               fdc_head = in[4];
-                                               fdc_sect = in[5];
-                                               TRACEx3(6, "id read: C: 0x%02x, H: 0x%02x, R: 0x%02x",
-                                                       fdc_cyl, fdc_head, fdc_sect);
-                                       } else {        /* no valid information, use invalid sector */
-                                               fdc_cyl =
-                                                   fdc_head =
-                                                   fdc_sect = 0;
-                                               TRACE(5, "Didn't find valid sector Id");
-                                       }
-                                       fdc_mode = fdc_idle;
-                                       break;
-                               }
-
-                       case fdc_writing_data:{
-
-                                       if (cause == no_error) {
-                                               TRACEi(5, "writing segment", buff->segment_id);
-                                       } else {
-                                               TRACEi(4, "error writing segment", buff->segment_id);
-                                       }
-                                       if (runner_status == aborting || runner_status == do_abort) {
-                                               TRACEx1(5, "aborting %s", fdc_mode_txt);
-                                               break;
-                                       }
-                                       if (buff->retry > 0) {
-                                               TRACEx1(5, "this is retry nr %d", buff->retry);
-                                       }
-                                       if (buff->bad_sector_map == FAKE_SEGMENT) {
-                                               /* This condition occurs when trying to write to a `fake'
-                                                * sector that's not accessible. Doesn't really matter as
-                                                * it isn't used anyway ! Might be located at wrong segment,
-                                                * then we'll fail on the next segment.
-                                                */
-                                               TRACE(4, "skipping empty segment (write)");
-                                               buff->remaining = 0;    /* skip failing sector */
-                                               continue_xfer(&buff, no_error, fdc_mode, 1);    /* fake success */
-                                       } else {
-                                               switch (cause) {
-                                               case no_error:{
-                                                               determine_progress(buff, cause, fdc_writing_data);
-                                                               continue_xfer(&buff, cause, fdc_mode, 0);
-                                                               break;
-                                                       }
-                                               case no_data_error:
-                                               case id_am_error:
-                                               case id_crc_error:
-                                               case data_am_error:
-                                               case overrun_error:{
-                                                               update_history(cause);
-                                                               determine_progress(buff, cause, fdc_writing_data);
-                                                               skip = find_resume_point(buff);
-                                                               retry_sector(&buff, cause, fdc_mode, skip);
-                                                               break;
-                                                       }
-                                               default:{
-                                                               if (in[1] & 0x02) {
-                                                                       TRACE(1, "media not writable");
-                                                               } else {
-                                                                       TRACE(-1, "unforeseen write error");
-                                                               }
-                                                               fdc_mode = fdc_idle;
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                                       break;
-                               }
-                       default:
-
-                               TRACEx1(1, "Warning: unexpected irq during: %s",
-                                       fdc_mode_txt);
-                               fdc_mode = fdc_idle;
-                               break;
-                       }
-               }
-               if (runner_status == do_abort) {
-                       /*      cease operation, remember tape position
-                        */
-                       TRACE(5, "runner aborting");
-                       runner_status = aborting;
-                       ++expected_stray_interrupts;
-               }
-       } else { /* !FDC_BUSY  */
-               /*  clear interrupt, cause should be gotten by issuing
-                *  a Sense Interrupt Status command.
-                */
-               if (fdc_mode == fdc_recalibrating || fdc_mode == fdc_seeking) {
-                       if (hide_interrupt) {
-                               int st0;
-                               int pcn;
-
-                               result = fdc_sense_interrupt_status(&st0, &pcn);
-                               current_cylinder = pcn;
-                               TRACE(5, "handled hidden interrupt");
-                       }
-                       seek_completed = 1;
-                       fdc_mode = fdc_idle;
-               } else if (!waitqueue_active(&wait_intr)) {
-                       if (expected_stray_interrupts == 0) {
-                               TRACE(2, "unexpected stray interrupt");
-                       } else {
-                               TRACE(5, "expected stray interrupt");
-                               --expected_stray_interrupts;
-                       }
-               } else {
-                       if (fdc_mode == fdc_reading_data || fdc_mode == fdc_writing_data ||
-                           fdc_mode == fdc_reading_id) {
-                               byte status = inb_p(fdc.msr);
-                               if (status & FDC_BUSY) {
-                                       TRACE(-1, "***** FDC failure, busy too late");
-                               } else {
-                                       TRACE(-1, "***** FDC failure, no busy");
-                               }
-                       } else {
-                               TRACE(6, "awaited stray interrupt");
-                       }
-               }
-               hide_interrupt = 0;
-       }
-       /*    Handle sleep code.
-        */
-       if (!hide_interrupt) {
-               ++interrupt_seen;
-               if (wait_intr) {
-                       wake_up_interruptible(&wait_intr);
-               }
-       } else {
-               TRACEx1(5, "hiding interrupt while %s", wait_intr ? "waiting" : "active");
-       }
-       t0 = timediff(t0, timestamp());
-       if (t0 >= 1000) {       /* only tell us about long calls */
-               TRACEx1(7, "isr() duration: %5d usec", t0);
-       }
-       *fdc.hook = fdc_isr;    /* hook our handler into the fdc code again */
-       TRACE_EXIT;
-       --isr_active;
-}
diff --git a/drivers/char/ftape/fdc-isr.h b/drivers/char/ftape/fdc-isr.h
deleted file mode 100644 (file)
index 123d416..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#ifndef _FDC_ISR_H
-#define _FDC_ISR_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/fdc-isr.h,v $
- $Author: bas $
- *
- $Revision: 1.8 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- *      This file contains the low level functions
- *      that communicate with the floppy disk controller,
- *      for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-/*
- *      fdc-isr.c defined public variables
- */
-extern volatile int expected_stray_interrupts; /* masks stray interrupts */
-extern volatile int seek_completed;    /* flag set by isr */
-extern volatile int interrupt_seen;    /* flag set by isr */
-extern volatile int expect_stray_interrupt;
-
-/*
- *      fdc-io.c defined public functions
- */
-extern void fdc_isr(void);
-
-/*
- *      A kernel hook that steals one interrupt from the floppy
- *      driver (Should be fixed when the new fdc driver gets ready)
- *      See the linux kernel source files:
- *          drivers/block/floppy.c & drivers/block/blk.h
- *      for the details.
- */
-extern void (*do_floppy) (void);
-
-#endif
diff --git a/drivers/char/ftape/ftape-bsm.c b/drivers/char/ftape/ftape-bsm.c
deleted file mode 100644 (file)
index 6e86b06..0000000
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- *      Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-bsm.c,v $
- $Author: bas $
- *
- $Revision: 1.7 $
- $Date: 1995/04/30 13:15:14 $
- $State: Beta $
- *
- *      This file contains the bad-sector map handling code for
- *      the QIC-117 floppy tape driver for Linux.
- *      QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented.
- */
-
-#include <linux/ftape.h>
-#include <linux/string.h>
-
-#include "tracing.h"
-#include "ftape-bsm.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-
-
-/*      Global vars.
- */
-int bad_sector_map_changed = 0;
-
-/*      Local vars.
- */
-static byte bad_sector_map[BAD_SECTOR_MAP_SIZE];
-typedef enum {
-       forward, backward
-} mode_type;
-
-#if 0
-/*  fix_tape converts a normal QIC-80 tape into a 'wide' tape.
- *  For testing purposes only !
- */
-void fix_tape(byte * buffer)
-{
-       static byte list[BAD_SECTOR_MAP_SIZE];
-       unsigned long *src_ptr = (unsigned long *) list;
-       byte *dst_ptr = bad_sector_map;
-       unsigned long map;
-       unsigned sector = 1;
-       int i;
-
-       memcpy(list, bad_sector_map, sizeof(list));
-       memset(bad_sector_map, 0, sizeof(bad_sector_map));
-       while ((byte *) src_ptr - list < sizeof(list)) {
-               map = *src_ptr++;
-               if (map == EMPTY_SEGMENT) {
-                       *(unsigned long *) dst_ptr = 0x800000 + sector;
-                       dst_ptr += 3;
-                       sector += SECTORS_PER_SEGMENT;
-               } else {
-                       for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
-                               if (map & 1) {
-                                       *(unsigned long *) dst_ptr = sector;
-                                       dst_ptr += 3;
-                               }
-                               map >>= 1;
-                               ++sector;
-                       }
-               }
-       }
-       bad_sector_map_changed = 1;
-       *(buffer + 4) = 4;      /* put new format code */
-       format_code = 4;
-}
-
-#endif
-
-byte *
- find_end_of_bsm_list(byte * ptr, byte * limit)
-{
-       while (ptr + 2 < limit) {
-               if (ptr[0] || ptr[1] || ptr[2]) {
-                       ptr += 3;
-               } else {
-                       return ptr;
-               }
-       }
-       return NULL;
-}
-
-void store_bad_sector_map(byte * buffer)
-{
-       TRACE_FUN(8, "store_bad_sector_map");
-       size_t count;
-       size_t offset;
-
-       /*  Store the bad sector map in buffer.
-        */
-       if (format_code == 4) {
-               offset = 256;
-               count = sizeof(bad_sector_map);
-       } else {
-               offset = 2 * SECTOR_SIZE;       /* skip failed sector log */
-               count = sizeof(bad_sector_map) - (offset - 256);
-       }
-       memcpy(buffer + offset, bad_sector_map, count);
-       TRACE_EXIT;
-}
-
-void put_sector(byte ** ptr, unsigned long sector)
-{
-       *(*ptr)++ = sector & 0xff;
-       sector >>= 8;
-       *(*ptr)++ = sector & 0xff;
-       sector >>= 8;
-       *(*ptr)++ = sector & 0xff;
-}
-
-unsigned long get_sector(byte ** ptr, mode_type mode)
-{
-       unsigned long sector;
-
-       if (mode == forward) {
-               sector = *(*ptr)++;
-               sector += *(*ptr)++ << 8;
-               sector += *(*ptr)++ << 16;
-       } else {
-               sector = *--(*ptr) << 16;
-               sector += *--(*ptr) << 8;
-               sector += *--(*ptr);
-       }
-       return sector;
-}
-
-void extract_bad_sector_map(byte * buffer)
-{
-       TRACE_FUN(8, "extract_bad_sector_map");
-
-       /*  Fill the bad sector map with the contents of buffer.
-        */
-       if (format_code == 4) {
-               /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
-                * sector log but use this area to extend the bad sector map.
-                */
-               memcpy(bad_sector_map, buffer + 256, sizeof(bad_sector_map));
-       } else {
-               /* non-wide QIC-80 tapes have a failed sector log area that
-                * mustn't be included in the bad sector map.
-                */
-               memcpy(bad_sector_map, buffer + 256 + FAILED_SECTOR_LOG_SIZE,
-                      sizeof(bad_sector_map) - FAILED_SECTOR_LOG_SIZE);
-       }
-#if 0
-       /* for testing of bad sector handling at end of tape
-        */
-       ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 3] = 0x000003e0;
-       ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 2] = 0xff3fffff;
-       ((unsigned long *) bad_sector_map)[segments_per_track * tracks_per_tape - 1] = 0xffffe000;
-#endif
-#if 0
-       /*  Enable to test bad sector handling
-        */
-       ((unsigned long *) bad_sector_map)[30] = 0xfffffffe;
-       ((unsigned long *) bad_sector_map)[32] = 0x7fffffff;
-       ((unsigned long *) bad_sector_map)[34] = 0xfffeffff;
-       ((unsigned long *) bad_sector_map)[36] = 0x55555555;
-       ((unsigned long *) bad_sector_map)[38] = 0xffffffff;
-       ((unsigned long *) bad_sector_map)[50] = 0xffff0000;
-       ((unsigned long *) bad_sector_map)[51] = 0xffffffff;
-       ((unsigned long *) bad_sector_map)[52] = 0xffffffff;
-       ((unsigned long *) bad_sector_map)[53] = 0x0000ffff;
-#endif
-#if 0
-       /*  Enable when testing multiple volume tar dumps.
-        */
-       for (i = first_data_segment; i <= ftape_last_segment.id - 7; ++i) {
-               ((unsigned long *) bad_sector_map)[i] = EMPTY_SEGMENT;
-       }
-#endif
-#if 0
-       /*  Enable when testing bit positions in *_error_map
-        */
-       for (i = first_data_segment; i <= ftape_last_segment.id; ++i) {
-               ((unsigned long *) bad_sector_map)[i] |= 0x00ff00ff;
-       }
-#endif
-       if (tracing > 2) {
-               unsigned int map;
-               int good_sectors = 0;
-               int bad_sectors;
-               unsigned int total_bad = 0;
-               int i;
-
-               if (format_code == 4 || format_code == 3) {
-                       byte *ptr = bad_sector_map;
-                       unsigned sector;
-
-                       do {
-                               sector = get_sector(&ptr, forward);
-                               if (sector != 0) {
-                                       if (format_code == 4 && sector & 0x800000) {
-                                               total_bad += SECTORS_PER_SEGMENT - 3;
-                                               TRACEx1(6, "bad segment at sector: %6d", sector & 0x7fffff);
-                                       } else {
-                                               ++total_bad;
-                                               TRACEx1(6, "bad sector: %6d", sector);
-                                       }
-                               }
-                       } while (sector != 0);
-                       /*  Display end-of-file marks
-                        */
-                       do {
-                               sector = *((unsigned short *) ptr)++;
-                               if (sector) {
-                                       TRACEx2(4, "eof mark: %4d/%2d", sector,
-                                           *((unsigned short *) ptr)++);
-                               }
-                       } while (sector);
-               } else {
-                       for (i = first_data_segment;
-                        i < segments_per_track * tracks_per_tape; ++i) {
-                               map = ((unsigned long *) bad_sector_map)[i];
-                               bad_sectors = count_ones(map);
-                               if (bad_sectors > 0) {
-                                       TRACEx2(6, "bsm for segment %4d: 0x%08x", i, map);
-                                       if (bad_sectors > SECTORS_PER_SEGMENT - 3) {
-                                               bad_sectors = SECTORS_PER_SEGMENT - 3;
-                                       }
-                                       total_bad += bad_sectors;
-                               }
-                       }
-               }
-               good_sectors = ((segments_per_track * tracks_per_tape - first_data_segment)
-                               * (SECTORS_PER_SEGMENT - 3)) - total_bad;
-               TRACEx1(3, "%d Kb usable on this tape",
-                       good_sectors - ftape_last_segment.free);
-               if (total_bad == 0) {
-                       TRACE(1, "WARNING: this tape has no bad blocks registered !");
-               } else {
-                       TRACEx1(2, "%d bad sectors", total_bad);
-               }
-       }
-       TRACE_EXIT;
-}
-
-unsigned long cvt2map(int sector)
-{
-       return 1 << (((sector & 0x7fffff) - 1) % SECTORS_PER_SEGMENT);
-}
-
-int cvt2segment(int sector)
-{
-       return ((sector & 0x7fffff) - 1) / SECTORS_PER_SEGMENT;
-}
-
-int forward_seek_entry(int segment_id, byte ** ptr, unsigned long *map)
-{
-       byte *tmp_ptr;
-       unsigned long sector;
-       int segment;
-       int count;
-
-       do {
-               sector = get_sector(ptr, forward);
-               segment = cvt2segment(sector);
-       } while (sector != 0 && segment < segment_id);
-       tmp_ptr = *ptr - 3;     /* point to first sector >= segment_id */
-       /*  Get all sectors in segment_id
-        */
-       if (format_code == 4 && (sector & 0x800000) && segment == segment_id) {
-               *map = EMPTY_SEGMENT;
-               count = 32;
-       } else {
-               *map = 0;
-               count = 0;
-               while (sector != 0 && segment == segment_id) {
-                       *map |= cvt2map(sector);
-                       sector = get_sector(ptr, forward);
-                       segment = cvt2segment(sector);
-                       ++count;
-               }
-       }
-       *ptr = tmp_ptr;
-       return count;
-}
-
-int backwards_seek_entry(int segment_id, byte ** ptr, unsigned long *map)
-{
-       unsigned long sector;
-       int segment;
-       int count;
-
-       *map = 0;
-       if (*ptr > bad_sector_map) {
-               do {
-                       sector = get_sector(ptr, backward);
-                       segment = cvt2segment(sector);
-               } while (*ptr > bad_sector_map && segment > segment_id);
-               count = 0;
-               if (segment > segment_id) {
-                       /*  at start of list, no entry found */
-               } else if (segment < segment_id) {
-                       /*  before smaller entry, adjust for overshoot */
-                       *ptr += 3;
-               } else {
-                       /*  get all sectors in segment_id */
-                       if (format_code == 4 && (sector & 0x800000)) {
-                               *map = EMPTY_SEGMENT;
-                               count = 32;
-                       } else {
-                               do {
-                                       *map |= cvt2map(sector);
-                                       ++count;
-                                       if (*ptr <= bad_sector_map) {
-                                               break;
-                                       }
-                                       sector = get_sector(ptr, backward);
-                                       segment = cvt2segment(sector);
-                               } while (segment == segment_id);
-                               if (segment < segment_id) {
-                                       *ptr += 3;
-                               }
-                       }
-               }
-       } else {
-               count = 0;
-       }
-       return count;
-}
-
-void put_bad_sector_entry(int segment_id, unsigned long new_map)
-{
-       byte *ptr = bad_sector_map;
-       int count;
-       int new_count;
-       unsigned long map;
-
-       if (format_code == 3 || format_code == 4) {
-               count = forward_seek_entry(segment_id, &ptr, &map);
-               new_count = count_ones(new_map);
-               /*  If format code == 4 put empty segment instead of 32 bad sectors.
-                */
-               if (new_count == 32 && format_code == 4) {
-                       new_count = 1;
-               }
-               if (count != new_count) {
-                       /*  insert (or delete if < 0) new_count - count entries.
-                        *  Move trailing part of list including terminating 0.
-                        */
-                       byte *hi_ptr = ptr;
-
-                       do {
-                       } while (get_sector(&hi_ptr, forward) != 0);
-                       memmove(ptr + new_count, ptr + count, hi_ptr - (ptr + count));
-               }
-               if (new_count == 1 && new_map == EMPTY_SEGMENT) {
-                       put_sector(&ptr, 0x800001 + segment_id * SECTORS_PER_SEGMENT);
-               } else {
-                       int i = 0;
-
-                       while (new_map) {
-                               if (new_map & 1) {
-                                       put_sector(&ptr, 1 + segment_id * SECTORS_PER_SEGMENT + i);
-                               }
-                               ++i;
-                               new_map >>= 1;
-                       }
-               }
-       } else {
-               ((unsigned long *) bad_sector_map)[segment_id] = new_map;
-       }
-       bad_sector_map_changed = 1;
-}
-
-unsigned long get_bad_sector_entry(int segment_id)
-{
-       TRACE_FUN(8, "get_bad_sector_entry");
-       static unsigned long map = 0;
-
-       if (used_header_segment == -1) {
-               /*  When reading header segment we'll need a blank map.
-                */
-               map = 0;
-       } else if (format_code == 3 || format_code == 4) {
-               /*  Invariants:
-                *    map - mask value returned on last call.
-                *    ptr - points to first sector greater or equal to
-                *          first sector in last_referenced segment.
-                *    last_referenced - segment id used in the last call,
-                *                      sector and map belong to this id.
-                *  This code is designed for sequential access and retries.
-                *  For true random access it may have to be redesigned.
-                */
-               static int last_reference = -1;
-               static byte *ptr = bad_sector_map;
-
-               if (segment_id > last_reference) {
-                       /*  Skip all sectors before segment_id
-                        */
-                       forward_seek_entry(segment_id, &ptr, &map);
-               } else if (segment_id < last_reference) {
-                       /*  Skip backwards until begin of buffer or first sector in segment_id
-                        */
-                       backwards_seek_entry(segment_id, &ptr, &map);
-               }               /* segment_id == last_reference : keep map */
-               last_reference = segment_id;
-       } else {
-               map = ((unsigned long *) bad_sector_map)[segment_id];
-       }
-       TRACE_EXIT;
-       return map;
-}
-
-void ftape_init_bsm(void)
-{
-       memset(bad_sector_map, 0, sizeof(bad_sector_map));
-}
diff --git a/drivers/char/ftape/ftape-bsm.h b/drivers/char/ftape/ftape-bsm.h
deleted file mode 100644 (file)
index e84363d..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef _FTAPE_BSM_H
-#define _FTAPE_BSM_H
-
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-bsm.h,v $
- $Author: bas $
- *
- $Revision: 1.5 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- *      This file contains definitions for the bad sector map handling
- *      routines for the QIC-117 floppy-tape driver for Linux.
- */
-
-#define EMPTY_SEGMENT           (0xffffffff)
-#define FAKE_SEGMENT            (0xfffffffe)
-
-/*  failed sector log size (only used if format code != 4).
- */
-#define FAILED_SECTOR_LOG_SIZE  (2 * SECTOR_SIZE - 256)
-
-/*  maximum (format code 4) bad sector map size (bytes).
- */
-#define BAD_SECTOR_MAP_SIZE     (29 * SECTOR_SIZE - 256)
-
-/*
- *      ftape-io.c defined global vars.
- */
-extern bad_sector_map_changed;
-
-/*
- *      ftape-io.c defined global functions.
- */
-extern void update_bad_sector_map(byte * buffer);
-extern void store_bad_sector_map(byte * buffer);
-extern void extract_bad_sector_map(byte * buffer);
-extern unsigned long get_bad_sector_entry(int segment_id);
-extern void put_bad_sector_entry(int segment_id, unsigned long mask);
-extern void add_segment_to_bad_sector_map(unsigned segment);
-extern void clear_bad_sector_map(int count);
-extern byte *find_end_of_bsm_list(byte * ptr, byte * limit);
-extern void ftape_init_bsm(void);
-
-#endif
diff --git a/drivers/char/ftape/ftape-ctl.c b/drivers/char/ftape/ftape-ctl.c
deleted file mode 100644 (file)
index e8c3ae1..0000000
+++ /dev/null
@@ -1,883 +0,0 @@
-/*
- *      Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- *      This file contains the non-read/write ftape functions
- *      for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-
-#include "tracing.h"
-#include "ftape-eof.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "ftape-rw.h"
-#include "qic117.h"
-#include "ftape-bsm.h"
-
-
-/*      Global vars.
- */
-int segments_per_track = 102;
-int segments_per_head = 1020;
-int segments_per_cylinder = 4;
-int tracks_per_tape = 20;
-int ftape_failure = 1;
-int ftape_seg_pos = 0;
-int first_data_segment = -1;
-int ftape_state = idle;                /* use buffer_state_enum */
-history_record history;
-int write_protected;
-int ftape_offline = 0;
-int no_tape = 1;
-int formatted = 0;
-int ftape_data_rate = 0;
-int going_offline = 0;
-int read_only = 0;
-
-/*      Local vars.
- */
-static int ftape_last_error = 0;
-static const vendor_struct vendors[] = QIC117_VENDORS;
-static const wakeup_method methods[] = WAKEUP_METHODS;
-static int init_drive_needed = 1;
-
-
-static int ftape_not_operational(int status)
-{
-       /* return true if status indicates tape can not be used.
-        */
-       return ((status ^ QIC_STATUS_CARTRIDGE_PRESENT) &
-               (QIC_STATUS_ERROR |
-                QIC_STATUS_CARTRIDGE_PRESENT |
-                QIC_STATUS_NEW_CARTRIDGE));
-}
-
-int ftape_seek_to_eot(void)
-{
-       TRACE_FUN(8, "ftape_seek_to_eot");
-       int result;
-       int status;
-
-       result = ftape_ready_wait(timeout.pause, &status);
-       while ((status & QIC_STATUS_AT_EOT) == 0) {
-               if (result < 0) {
-                       TRACE(1, "failed");
-                       TRACE_EXIT;
-                       return result;
-               }
-               if (ftape_not_operational(status)) {
-                       TRACE_EXIT;
-                       return -EIO;
-               }
-               result = ftape_command_wait(QIC_PHYSICAL_FORWARD,
-                                           timeout.rewind, &status);
-       }
-       TRACE_EXIT;
-       return 0;
-}
-
-int ftape_seek_to_bot(void)
-{
-       TRACE_FUN(8, "ftape_seek_to_bot");
-       int result;
-       int status;
-
-       result = ftape_ready_wait(timeout.pause, &status);
-       while ((status & QIC_STATUS_AT_BOT) == 0) {
-               if (result < 0) {
-                       TRACE(1, "failed");
-                       TRACE_EXIT;
-                       return result;
-               }
-               if (ftape_not_operational(status)) {
-                       TRACE_EXIT;
-                       return -EIO;
-               }
-               result = ftape_command_wait(QIC_PHYSICAL_REVERSE,
-                                           timeout.rewind, &status);
-       }
-       TRACE_EXIT;
-       return 0;
-}
-
-void ftape_reset_position(void)
-{
-       ftape_seg_pos = first_data_segment;
-       reset_eof_list();
-}
-
-int ftape_new_cartridge(void)
-{
-       location.track = -1;    /* force seek on first access */
-       first_data_segment = -1;        /* unknown */
-       ftape_zap_read_buffers();
-       ftape_zap_write_buffers();
-       ftape_reset_position();
-       return 0;
-}
-
-int ftape_abort_operation(void)
-{
-       TRACE_FUN(5, "ftape_abort_operation");
-       int result = 0;
-       int i;
-       int status;
-
-       if (runner_status == running) {
-               TRACE(5, "aborting runner, waiting");
-               runner_status = do_abort;
-               /* set timeout so that the tape will run to logical EOT
-                * if we missed the last sector and there are no queue pulses.
-                */
-               result = ftape_dumb_stop();
-               if (result == 0) {
-                       runner_status = idle;
-               }
-       }
-       if (runner_status != idle) {
-               if (runner_status == do_abort) {
-                       TRACE(5, "forcing runner abort");
-               }
-               TRACE(5, "stopping tape");
-               result = ftape_command_wait(QIC_STOP_TAPE, timeout.stop, &status);
-               location.known = 0;
-               runner_status = idle;
-       }
-       for (i = 0; i < NR_BUFFERS; ++i) {
-               buffer[i].status = waiting;
-       }
-       head = tail = 0;
-       TRACE_EXIT;
-       return result;
-}
-
-int lookup_vendor_id(int vendor_id)
-{
-       int i = 0;
-
-       while (vendors[i].vendor_id != vendor_id) {
-               if (++i >= NR_ITEMS(vendors)) {
-                       return -1;
-               }
-       }
-       return i;
-}
-
-void ftape_detach_drive(void)
-{
-       TRACE_FUN(8, "ftape_detach_drive");
-
-       TRACE(5, "disabling tape drive and fdc");
-       ftape_put_drive_to_sleep(drive_type);
-       fdc_catch_stray_interrupts(1);  /* one always comes */
-       fdc_disable();
-       fdc_release_irq_and_dma();
-       TRACE_EXIT;
-}
-
-static void clear_history(void)
-{
-       history.used = 0;
-       history.id_am_errors =
-           history.id_crc_errors =
-           history.data_am_errors =
-           history.data_crc_errors =
-           history.overrun_errors =
-           history.no_data_errors =
-           history.retries =
-           history.crc_errors =
-           history.crc_failures =
-           history.ecc_failures =
-           history.corrected =
-           history.defects =
-           history.rewinds = 0;
-}
-
-int ftape_activate_drive(vendor_struct * drive_type)
-{
-       TRACE_FUN(5, "ftape_activate_drive");
-       int result = 0;
-
-       /* If we already know the drive type, wake it up.
-        * Else try to find out what kind of drive is attached.
-        */
-       if (drive_type->wake_up != unknown_wake_up) {
-               TRACE(5, "enabling tape drive and fdc");
-               result = ftape_wakeup_drive(drive_type->wake_up);
-               if (result < 0) {
-                       TRACE(1, "known wakeup method failed");
-               }
-       } else {
-               int old_tracing = tracing;
-               wake_up_types method;
-
-               /*  Try to awaken the drive using all known methods.
-                *  Lower tracing for a while.
-                */
-               if (tracing <= 4) {
-                       tracing = 0;
-               }
-               for (method = no_wake_up; method < NR_ITEMS(methods); ++method) {
-                       drive_type->wake_up = method;
-#if 0
-                       /*  Test setup for dual drive configuration in dodo.
-                        *  /dev/rft2 uses mountain wakeup only -> Archive QIC-80
-                        *  /dev/rft3 uses colorado wakeup only -> Jumbo QIC-40
-                        *  Other systems will use the normal scheme.
-                        */
-                       if ((FTAPE_UNIT < 2) ||
-                       (FTAPE_UNIT == 2 && method == wake_up_mountain) ||
-                       (FTAPE_UNIT == 3 && method == wake_up_colorado)) {
-                               result = ftape_wakeup_drive(drive_type->wake_up);
-                       } else {
-                               result = -EIO;
-                       }
-#else
-                       result = ftape_wakeup_drive(drive_type->wake_up);
-#endif
-                       if (result >= 0) {
-                               int tracing = old_tracing;      /* fool TRACE */
-                               TRACEx1(2, "drive wakeup method: %s",
-                                     methods[drive_type->wake_up].name);
-                               break;
-                       }
-               }
-               tracing = old_tracing;
-               if (method >= NR_ITEMS(methods)) {
-                       /* no response at all, cannot open this drive */
-                       drive_type->wake_up = unknown_wake_up;
-                       TRACE(1, "no tape drive found !");
-                       tracing = old_tracing;
-                       result = -ENODEV;
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_get_drive_status(int *new_tape, int *no_tape, int *wp_tape)
-{
-       TRACE_FUN(5, "ftape_get_drive_status");
-       int result;
-       int status;
-
-       *no_tape =
-           *wp_tape = 0;
-       /*    Tape drive is activated now.
-        *    First clear error status if present.
-        */
-       do {
-               result = ftape_ready_wait(timeout.reset, &status);
-               if (result < 0) {
-                       if (result == -ETIME) {
-                               TRACE(1, "ftape_ready_wait timeout");
-                       } else if (result == -EINTR) {
-                               TRACE(1, "ftape_ready_wait aborted");
-                       } else {
-                               TRACE(1, "ftape_ready_wait failed");
-                       }
-                       result = -EIO;
-                       break;
-               }
-               /*  Clear error condition (drive is ready !)
-                */
-               if (status & QIC_STATUS_ERROR) {
-                       int error;
-                       int command;
-
-                       TRACE(1, "error status set");
-                       result = ftape_report_error(&error, &command, 1);
-                       if (result < 0) {
-                               TRACEi(1, "report_error_code failed:", result);
-                               ftape_reset_drive();    /* hope it's working next time */
-                               init_drive_needed = 1;
-                               result = -EIO;
-                               break;
-                       } else if (error != 0) {
-                               TRACEi(4, "error code   :", error);
-                               TRACEi(4, "error command:", command);
-                       }
-               }
-               if (status & QIC_STATUS_NEW_CARTRIDGE) {
-                       int error;
-                       int command;
-                       int old_tracing = tracing;
-
-                       /*  Undocumented feature: Must clear (not present!) error 
-                        *  here or we'll fail later.
-                        */
-                       tracing = 0;
-                       ftape_report_error(&error, &command, 1);
-                       tracing = old_tracing;
-                       TRACE(3, "status: new cartridge");
-                       *new_tape = 1;
-               }
-       } while (status & QIC_STATUS_ERROR);
-
-       *no_tape = !(status & QIC_STATUS_CARTRIDGE_PRESENT);
-       *wp_tape = (status & QIC_STATUS_WRITE_PROTECT);
-       if (*no_tape) {
-               TRACE(1, "no cartridge present");
-       } else {
-               if (*wp_tape) {
-                       TRACE(2, "Write protected cartridge");
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-void ftape_log_vendor_id(void)
-{
-       TRACE_FUN(5, "ftape_log_vendor_id");
-       int vendor_index;
-
-       ftape_report_vendor_id(&drive_type.vendor_id);
-       vendor_index = lookup_vendor_id(drive_type.vendor_id);
-       if (drive_type.vendor_id == UNKNOWN_VENDOR &&
-           drive_type.wake_up == wake_up_colorado) {
-               vendor_index = 0;
-               drive_type.vendor_id = 0;       /* hack to get rid of all this mail */
-       }
-       if (vendor_index < 0) {
-               /* Unknown vendor id, first time opening device.
-                * The drive_type remains set to type found at wakeup time, this
-                * will probably keep the driver operating for this new vendor.
-                */
-               TRACE(-1, "============ unknown vendor id ===========");
-               TRACE(-1, "A new, yet unsupported tape drive is found");
-               TRACE(-1, "Please report the following values:");
-               TRACEx1(-1, "   Vendor id     : 0x%04x", drive_type.vendor_id);
-               TRACEx1(-1, "   Wakeup method : %s", methods[drive_type.wake_up].name);
-               TRACE(-1, "And a description of your tape drive to:");
-               TRACE(-1, "Kai Harrekilde-Petersen <khp@dolphinics.no>");
-               TRACE(-1, "==========================================");
-               drive_type.speed = 500;         /* deci-ips: very safe value */
-       } else {
-               drive_type.name = vendors[vendor_index].name;
-               drive_type.speed = vendors[vendor_index].speed;
-               TRACEx1(3, "tape drive type: %s", drive_type.name);
-               /* scan all methods for this vendor_id in table */
-               while (drive_type.wake_up != vendors[vendor_index].wake_up) {
-                       if (vendor_index < NR_ITEMS(vendors) - 1 &&
-                           vendors[vendor_index + 1].vendor_id == drive_type.vendor_id) {
-                               ++vendor_index;
-                       } else {
-                               break;
-                       }
-               }
-               if (drive_type.wake_up != vendors[vendor_index].wake_up) {
-                       TRACE(-1, "==========================================");
-                       TRACE(-1, "wakeup type mismatch:");
-                       TRACEx2(-1, "found: %s, expected: %s",
-                               methods[drive_type.wake_up].name,
-                           methods[vendors[vendor_index].wake_up].name);
-                       TRACE(-1, "please report this to <khp@dolphinics.no>");
-                       TRACE(-1, "==========================================");
-               }
-       }
-       TRACE_EXIT;
-}
-
-void ftape_calc_timeouts(void)
-{
-       TRACE_FUN(8, "ftape_calc_timeouts");
-       int speed;              /* deci-ips ! */
-       int length;
-
-       /*                           tape transport speed
-        *  data rate:        QIC-40   QIC-80   QIC-3010 QIC-3020
-        *
-        *    250 Kbps        25 ips     n/a      n/a      n/a
-        *    500 Kbps        50 ips   34 ips   22.6 ips   n/a
-        *      1 Mbps          n/a    68 ips   45.2 ips 22.6 ips
-        *      2 Mbps          n/a      n/a      n/a    45.2 ips
-        *
-        *  fast tape transport speed is at least 68 ips.
-        */
-       switch (qic_std) {
-       case QIC_TAPE_QIC40:
-               speed = (ftape_data_rate == 3) ? 250 : 500;
-               break;
-       case QIC_TAPE_QIC80:
-               speed = (ftape_data_rate == 2) ? 340 : 680;
-               break;
-       case QIC_TAPE_QIC3010:
-               speed = (ftape_data_rate == 2) ? 226 : 452;
-               break;
-       case QIC_TAPE_QIC3020:
-               speed = (ftape_data_rate == 1) ? 226 : 452;
-               break;
-       default:
-               TRACE(-1, "Unknown qic_std (bug) ?");
-               speed = 500;
-               break;
-       }
-       if (tape_len <= 0) {
-               /*  Handle unknown length tapes as 1100 ft ones (worst case)
-                */
-               TRACE(1, "Unknown tape length, using worst case timing values!");
-               length = 1100;
-       } else {
-               length = tape_len;
-       }
-       if (drive_type.speed == 0) {
-               unsigned long t0;
-               int dt;
-
-               ftape_seek_to_bot();
-               t0 = jiffies;
-               ftape_seek_to_eot();
-               ftape_seek_to_bot();
-               dt = (int) ((jiffies - t0) * MSPT);
-               drive_type.speed = (2 * 12 * length * 1000) / dt;
-               TRACE(-1, "==========================================");
-               TRACEx1(-1, "drive : %s", drive_type.name);
-               TRACEx2(-1, "delta time = %d, length = %d", dt, length);
-               TRACEx1(-1, "has max tape speed of %d ips", drive_type.speed);
-               TRACE(-1, "please report this to <khp@dolphinics.no>");
-               TRACE(-1, "==========================================");
-       }
-       /*  time to go from bot to eot at normal speed (data rate):
-        *  time = (1+delta) * length (ft) * 12 (inch/ft) / speed (ips)
-        *  delta = 10 % for seek speed, 20 % for rewind speed.
-        */
-       timeout.seek = (length * 132 * SECOND) / speed;
-       timeout.rewind = (length * 144 * SECOND) / (10 * drive_type.speed);
-       timeout.reset = 20 * SECOND + timeout.rewind;
-       TRACEx2(4, "speed = %d, length = %d", speed, length);
-       TRACEx1(4, "seek timeout: %d sec", (timeout.seek + 500) / 1000);
-       TRACEx1(4, "rewind timeout: %d sec", (timeout.rewind + 500) / 1000);
-       TRACE_EXIT;
-}
-
-int ftape_init_drive(int *formatted)
-{
-       TRACE_FUN(5, "ftape_init_drive");
-       int result = 0;
-       int status;
-
-       result = ftape_report_raw_drive_status(&status);
-       if (result >= 0 && (status & QIC_STATUS_CARTRIDGE_PRESENT)) {
-               if (!(status & QIC_STATUS_AT_BOT)) {
-                       /*  Antique drives will get here after a soft reset,
-                        *  modern ones only if the driver is loaded when the
-                        *  tape wasn't rewound properly.
-                        */
-                       ftape_seek_to_bot();
-               }
-               if (!(status & QIC_STATUS_REFERENCED)) {
-                       TRACE(5, "starting seek_load_point");
-                       result = ftape_command_wait(QIC_SEEK_LOAD_POINT,
-                                                timeout.reset, &status);
-                       if (result < 0) {
-                               TRACE(1, "seek_load_point failed (command)");
-                       }
-               }
-       }
-       if (result >= 0) {
-               int rate;
-
-               *formatted = (status & QIC_STATUS_REFERENCED);
-               if (!*formatted) {
-                       TRACE(1, "Warning: tape is not formatted !");
-               }
-               /*  Select highest rate supported by both fdc and drive.
-                *  Start with highest rate supported by the fdc.
-                */
-               if (fdc.type >= i82078_1)
-                       rate = 0;
-               else if (fdc.type >= i82077)
-                       rate = 1;
-               else
-                       rate = 2;
-               do {
-                       result = ftape_set_data_rate(rate);
-                       if (result >= 0) {
-                               ftape_calc_timeouts();
-                               break;
-                       }
-                       ++rate;
-               } while (rate < 4);
-               if (result < 0) {
-                       result = -EIO;
-               }
-       }
-       if (result >= 0) {
-               /* Tape should be at bot if new cartridge ! */
-               ftape_new_cartridge();
-       }
-       init_drive_needed = 0;
-       TRACE_EXIT;
-       return result;
-}
-
-/*      OPEN routine called by kernel-interface code
- */
-int _ftape_open(void)
-{
-       TRACE_FUN(8, "_ftape_open");
-       int result;
-       static int new_tape = 1;
-
-       result = fdc_init();
-       if (result >= 0) {
-               result = ftape_activate_drive(&drive_type);
-               if (result < 0) {
-                       fdc_disable();
-                       fdc_release_irq_and_dma();
-
-               } else {
-                       result = ftape_get_drive_status(&new_tape, &no_tape, &write_protected);
-                       if (result < 0) {
-                               ftape_detach_drive();
-                       } else {
-                               if (drive_type.vendor_id == UNKNOWN_VENDOR) {
-                                       ftape_log_vendor_id();
-                               }
-                               if (no_tape) {
-                                       ftape_offline = 1;
-                               } else if (new_tape) {
-                                       ftape_offline = 0;
-                                       init_drive_needed = 1;
-                                       read_only = 0;  /* enable writes again */
-                               }
-                               if (!ftape_offline && init_drive_needed) {
-                                       result = ftape_init_drive(&formatted);
-                                       if (result >= 0) {
-                                               new_tape = 0;
-                                       } else {
-                                               ftape_detach_drive();
-                                       }
-                               }
-                               if (result >= 0) {
-                                       clear_history();
-                               }
-                       }
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-/*      RELEASE routine called by kernel-interface code
- */
-int _ftape_close(void)
-{
-       TRACE_FUN(8, "_ftape_close");
-       int result = 0;
-       int last_segment = 0;
-
-       if (!ftape_offline) {
-               result = ftape_flush_buffers();
-               last_segment = ftape_seg_pos - 1;
-               if (!(ftape_unit & FTAPE_NO_REWIND)) {
-                       if (result >= 0) {
-                               result = ftape_update_header_segments(NULL, 1);
-                               if (result < 0) {
-                                       TRACE(1, "error: update of header segments failed");
-                               }
-                       } else {
-                               TRACE(1, "error: unable to update header segments");
-                       }
-               }
-               ftape_abort_operation();
-               if (!(ftape_unit & FTAPE_NO_REWIND)) {
-                       if (!no_tape) {
-                               TRACE(5, "rewinding tape");
-                               result = ftape_seek_to_bot();
-                       }
-                       ftape_reset_position();
-                       ftape_zap_read_buffers();
-                       ftape_zap_write_buffers();
-               }
-       }
-       ftape_detach_drive();
-       fdc_uninit();
-       if (history.used) {
-               TRACE(3, "== Non-fatal errors this run: ==");
-               TRACE(3, "fdc isr statistics:");
-               TRACEi(3, " id_am_errors     :", history.id_am_errors);
-               TRACEi(3, " id_crc_errors    :", history.id_crc_errors);
-               TRACEi(3, " data_am_errors   :", history.data_am_errors);
-               TRACEi(3, " data_crc_errors  :", history.data_crc_errors);
-               TRACEi(3, " overrun_errors   :", history.overrun_errors);
-               TRACEi(3, " no_data_errors   :", history.no_data_errors);
-               TRACEi(3, " retries          :", history.retries);
-               if (history.used & 1) {
-                       TRACE(3, "ecc statistics:");
-                       TRACEi(3, " crc_errors       :", history.crc_errors);
-                       TRACEi(3, " crc_failures     :", history.crc_failures);
-                       TRACEi(3, " ecc_failures     :", history.ecc_failures);
-                       TRACEi(3, " sectors corrected:", history.corrected);
-               }
-               TRACEx2(3, "media defects     : %d%s", history.defects,
-                       history.defects ? " !!!" : "");
-               TRACEi(3, "repositions       :", history.rewinds);
-               TRACEi(3, "last segment      :", last_segment);
-       }
-       if (going_offline) {
-               going_offline = 0;
-               ftape_offline = 1;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-/*      IOCTL routine called by kernel-interface code
- */
-int _ftape_ioctl(unsigned int command, void *arg)
-{
-       TRACE_FUN(8, "ftape_ioctl");
-       int result = EINVAL;
-       union {
-               struct mtop mtop;
-               struct mtget mtget;
-       } krnl_arg;
-       int arg_size = (command & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
-
-       /* This check will only catch arguments that are too large !
-        */
-       if ((command & IOC_INOUT) && arg_size > sizeof(krnl_arg)) {
-               TRACEi(1, "bad argument size:", arg_size);
-               TRACE_EXIT;
-               return -EINVAL;
-       }
-       if (command & IOC_IN) {
-               int error = verify_area(VERIFY_READ, arg, arg_size);
-               if (error) {
-                       TRACE_EXIT;
-                       return error;
-               }
-               copy_from_user(&krnl_arg.mtop, arg, arg_size);
-       }
-       TRACEx1(5, "called with ioctl command: 0x%08x", command);
-       switch (command) {
-               /* cpio compatibility
-                * mtrasx and mtreset are mt extension by Hennus Bergman
-                * mtseek and mttell are mt extension by eddy olk
-                */
-       case MTIOCTOP:
-               TRACEx1(5, "calling MTIOCTOP command: 0x%08x", krnl_arg.mtop.mt_op);
-               switch (krnl_arg.mtop.mt_op) {
-               case MTNOP:
-                       /* gnu mt calls MTNOP before MTIOCGET to set status */
-                       result = 0;
-                       break;
-               case MTRESET:
-                       result = ftape_reset_drive();
-                       init_drive_needed = 1;
-                       if (result < 0 || ftape_offline) {
-                               break;
-                       }
-                       result = ftape_seek_to_bot();
-                       ftape_reset_position();
-                       break;
-               case MTREW:
-               case MTOFFL:
-                       if (ftape_offline) {
-                               result = -EIO;
-                               break;
-                       }
-                       ftape_flush_buffers();
-                       ftape_update_header_segments(NULL, 1);
-                       result = ftape_seek_to_bot();
-                       ftape_reset_position();
-                       if (krnl_arg.mtop.mt_op == MTOFFL) {
-                               going_offline = 1;
-                               TRACE(4, "Putting tape drive offline");
-                       }
-                       result = 0;
-                       break;
-               case MTRETEN:
-                       if (ftape_offline) {
-                               result = -EIO;
-                               break;
-                       }
-                       result = ftape_seek_to_eot();
-                       if (result >= 0) {
-                               result = ftape_seek_to_bot();
-                       }
-                       ftape_reset_position();
-                       break;
-               case MTERASE:
-                       if (ftape_offline) {
-                               result = -EIO;
-                               break;
-                       }
-                       result = ftape_erase();
-                       break;
-               case MTEOM:
-                       if (ftape_offline) {
-                               result = -EIO;
-                               break;
-                       }
-                       result = ftape_seek_eom();
-                       break;
-               case MTFSFM:
-                       if (ftape_offline) {
-                               result = -EIO;
-                               break;
-                       }
-                       eof_mark = 1;   /* position ready to extend */
-               case MTFSF:
-                       if (ftape_offline) {
-                               result = -EIO;
-                               break;
-                       }
-                       result = ftape_seek_eof(krnl_arg.mtop.mt_count);
-                       break;
-               case MTBSFM:
-                       if (ftape_offline) {
-                               result = -EIO;
-                               break;
-                       }
-                       eof_mark = 1;   /* position ready to extend */
-               case MTBSF:
-                       if (ftape_offline) {
-                               result = -EIO;
-                               break;
-                       }
-                       result = ftape_seek_eof(-krnl_arg.mtop.mt_count);
-                       break;
-               case MTFSR:
-                       if (ftape_offline) {
-                               result = -EIO;
-                               break;
-                       }
-                       tracing = krnl_arg.mtop.mt_count;
-                       TRACEx1(2, "tracing set to %d", tracing);
-                       result = 0;
-                       break;
-               case MTBSR:
-                       if (ftape_offline) {
-                               result = -EIO;
-                               break;
-                       }
-#if 0
-                       result = ftape_fix();
-#else
-                       result = 0;
-#endif
-                       break;
-               case MTWEOF:
-                       if (ftape_offline) {
-                               result = -EIO;
-                               break;
-                       }
-                       result = ftape_weof(krnl_arg.mtop.mt_count, ftape_seg_pos, 1);
-                       if (result >= 0) {
-                               ftape_seg_pos += krnl_arg.mtop.mt_count - 1;
-                       }
-                       break;
-                       /* MTRASx and MTRESET are mt extension by Hennus Bergman
-                        */
-               case MTRAS1:
-               case MTRAS2:
-               case MTRAS3:
-               case MTSEEK:
-               case MTTELL:
-               default:
-                       TRACEi(1, "MTIOCTOP sub-command not implemented:", krnl_arg.mtop.mt_op);
-                       result = -EIO;
-                       break;
-               }
-               break;
-       case MTIOCGET:
-               krnl_arg.mtget.mt_type = drive_type.vendor_id + 0x800000;
-               krnl_arg.mtget.mt_resid = 0;    /* not implemented */
-               krnl_arg.mtget.mt_dsreg = 0;    /* status register */
-               krnl_arg.mtget.mt_gstat =       /* device independent status */
-                   ((ftape_offline) ? 0 : GMT_ONLINE(-1L)) |
-                   ((write_protected) ? GMT_WR_PROT(-1L) : 0) |
-                   ((no_tape) ? GMT_DR_OPEN(-1L) : 0);
-               krnl_arg.mtget.mt_erreg = ftape_last_error;     /* error register */
-               result = ftape_file_no(&krnl_arg.mtget.mt_fileno,
-                                      &krnl_arg.mtget.mt_blkno);
-               break;
-       case MTIOCPOS:
-               TRACE(5, "Mag tape ioctl command: MTIOCPOS");
-               TRACE(1, "MTIOCPOS command not implemented");
-               break;
-       default:
-               result = -EINVAL;
-               break;
-       }
-       if (command & IOC_OUT) {
-               int error = verify_area(VERIFY_WRITE, arg, arg_size);
-               if (error) {
-                       TRACE_EXIT;
-                       return error;
-               }
-               copy_to_user(arg, &krnl_arg, arg_size);
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-void ftape_init_driver(void)
-{
-       drive_type.vendor_id = UNKNOWN_VENDOR;
-       drive_type.speed = 0;
-       drive_type.wake_up = unknown_wake_up;
-       drive_type.name = "Unknown";
-
-       timeout.seek = 650 * SECOND;
-       timeout.reset = 670 * SECOND;
-       timeout.rewind = 650 * SECOND;
-       timeout.head_seek = 15 * SECOND;
-       timeout.stop = 5 * SECOND;
-       timeout.pause = 16 * SECOND;
-
-       qic_std = -1;
-       tape_len = -1;
-       current_command = 0;
-       current_cylinder = -1;
-
-       segments_per_track = 102;
-       segments_per_head = 1020;
-       segments_per_cylinder = 4;
-       tracks_per_tape = 20;
-       ftape_failure = 1;
-       ftape_seg_pos = 0;
-       first_data_segment = -1;
-       ftape_state = idle;
-       no_tape = 1;
-       formatted = 0;
-       ftape_data_rate = 0;
-       going_offline = 0;
-       read_only = 0;
-
-       init_drive_needed = 1;
-       header_segment_1 = -1;
-       header_segment_2 = -1;
-       used_header_segment = -1;
-       location.track = -1;
-       location.known = 0;
-       tape_running = 0;
-       might_be_off_track = 1;
-
-       ftape_new_cartridge();  /* init some tape related variables */
-       ftape_init_bsm();
-}
diff --git a/drivers/char/ftape/ftape-ctl.h b/drivers/char/ftape/ftape-ctl.h
deleted file mode 100644 (file)
index 59fcfdc..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-#ifndef _FTAPE_CTL_H
-#define _FTAPE_CTL_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-ctl.h,v $
- $Author: bas $
- *
- $Revision: 1.4 $
- $Date: 1995/05/03 18:04:03 $
- $State: Beta $
- *
- *      This file contains the non-standard IOCTL related definitions
- *      for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/ioctl.h>
-#include <linux/mtio.h>
-
-#include "vendors.h"
-
-
-typedef struct {
-       int used;               /* any reading or writing done */
-       /* isr statistics */
-       unsigned int id_am_errors;      /* id address mark not found */
-       unsigned int id_crc_errors;     /* crc error in id address mark */
-       unsigned int data_am_errors;    /* data address mark not found */
-       unsigned int data_crc_errors;   /* crc error in data field */
-       unsigned int overrun_errors;    /* fdc access timing problem */
-       unsigned int no_data_errors;    /* sector not found */
-       unsigned int retries;   /* number of tape retries */
-       /* ecc statistics */
-       unsigned int crc_errors;        /* crc error in data */
-       unsigned int crc_failures;      /* bad data without crc error */
-       unsigned int ecc_failures;      /* failed to correct */
-       unsigned int corrected; /* total sectors corrected */
-       /* general statistics */
-       unsigned int rewinds;   /* number of tape rewinds */
-       unsigned int defects;   /* bad sectors due to media defects */
-} history_record;
-
-/*
- *      ftape-ctl.c defined global vars.
- */
-extern int ftape_failure;
-extern int write_protected;
-extern ftape_offline;
-extern int formatted;
-extern int no_tape;
-extern history_record history;
-extern int ftape_data_rate;
-extern int going_offline;
-extern vendor_struct drive_type;
-extern int segments_per_track;
-extern int segments_per_head;
-extern int segments_per_cylinder;
-extern int tracks_per_tape;
-extern int ftape_seg_pos;
-extern int first_data_segment;
-extern int ftape_state;
-extern int read_only;
-
-/*
- *      ftape-ctl.c defined global functions.
- */
-extern int _ftape_open(void);
-extern int _ftape_close(void);
-extern int _ftape_ioctl(unsigned int command, void *arg);
-extern int ftape_seek_to_bot(void);
-extern int ftape_seek_to_eot(void);
-extern int ftape_new_cartridge(void);
-extern int ftape_abort_operation(void);
-extern void ftape_reset_position(void);
-extern void ftape_calc_timeouts(void);
-extern void ftape_init_driver(void);
-
-#endif
diff --git a/drivers/char/ftape/ftape-eof.c b/drivers/char/ftape/ftape-eof.c
deleted file mode 100644 (file)
index 75c4cbd..0000000
+++ /dev/null
@@ -1,554 +0,0 @@
-
-/*
- *      Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-eof.c,v $
- $Author: bas $
- *
- $Revision: 1.21 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- *      This file contains the eof mark handling code
- *      for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/ftape.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-
-#include "tracing.h"
-#include "ftape-eof.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "ftape-rw.h"
-#include "ftape-ctl.h"
-#include "ftape-bsm.h"
-
-/*      Global vars.
- */
-int failed_sector_log_changed = 0;
-int eof_mark = 0;
-
-/*      Local vars.
- */
-static struct failed_sector_entry {
-       unsigned short segment;
-       unsigned short sector;
-} *eof_mark_ptr;
-
-typedef union {
-       struct failed_sector_entry mark;
-       unsigned long entry;
-} eof_mark_union;
-
-/*  a copy of the failed sector log from the header segment.
- */
-static eof_mark_union eof_map[(2048 - 256) / 4];
-
-/*  index into eof_map table pointing to last found eof mark.
- */
-static int eof_index;
-
-/*  number of eof marks (entries in bad sector log) on tape.
- */
-static int nr_of_eof_marks = -1;
-
-static char linux_tape_label[] = "Linux raw format V";
-enum {
-       min_fmt_version = 1, max_fmt_version = 2
-};
-static unsigned ftape_fmt_version = 0;
-
-
-/*  Ftape (mis)uses the bad sector log to record end-of-file marks.
- *  Initially (when the tape is erased) all entries in the bad sector
- *  log are added to the tape's bad sector map. The bad sector log
- *  then is cleared.
- *
- *  The bad sector log normally contains entries of the form:
- *  even 16-bit word: segment number of bad sector
- *   odd 16-bit word: encoded date
- *  There can be a total of 448 entries (1792 bytes).
- *
- *  My guess is that no program is using this bad sector log (the
- *  format seems useless as there is no indication of the bad sector
- *  itself, only the segment)
- *  However, if any program does use the bad sector log, the format
- *  used by ftape will let the program think there are some bad sectors
- *  and no harm is done.
- *  
- *  The eof mark entries that ftape stores in the bad sector log:
- *  even 16-bit word: segment number of eof mark
- *   odd 16-bit word: sector number of eof mark [1..32]
- *  
- *  The eof_map as maintained is a sorted list of eof mark entries.
- *
- *
- *  The tape name field in the header segments is used to store a
- *  linux tape identification string and a version number.
- *  This way the tape can be recognized as a Linux raw format
- *  tape when using tools under other OS's.
- *
- *  'Wide' QIC tapes (format code 4) don't have a failed sector list
- *  anymore. That space is used for the (longer) bad sector map that
- *  now is a variable length list too.
- *  We now store our end-of-file marker list after the bad-sector-map
- *  on tape. The list is delimited by a (long) 0 entry.
- */
-
-int ftape_validate_label(char *label)
-{
-       TRACE_FUN(8, "ftape_validate_label");
-       int result = 0;
-
-       TRACEx1(4, "tape  label = `%s'", label);
-       ftape_fmt_version = 0;
-       if (memcmp(label, linux_tape_label, strlen(linux_tape_label)) == 0) {
-               int pos = strlen(linux_tape_label);
-               while (label[pos] >= '0' && label[pos] <= '9') {
-                       ftape_fmt_version *= 10;
-                       ftape_fmt_version = label[pos++] - '0';
-               }
-               result = (ftape_fmt_version >= min_fmt_version &&
-                         ftape_fmt_version <= max_fmt_version);
-       }
-       TRACEx1(4, "format version = %d", ftape_fmt_version);
-       TRACE_EXIT;
-       return result;
-}
-
-static byte *
- find_end_of_eof_list(byte * ptr, byte * limit)
-{
-       while (ptr + 3 < limit) {
-               if (*(unsigned long *) ptr) {
-                       ++(unsigned long *) ptr;
-               } else {
-                       return ptr;
-               }
-       }
-       return NULL;
-}
-
-void reset_eof_list(void)
-{
-       TRACE_FUN(8, "reset_eof_list");
-
-       eof_mark_ptr = &eof_map[0].mark;
-       eof_index = 0;
-       eof_mark = 0;
-       TRACE_EXIT;
-}
-
-/*  Test if `segment' has an eof mark set (optimized for sequential access).
- *  return 0 if not eof mark or sector number (> 0) if eof mark set.
- */
-int check_for_eof(unsigned segment)
-{
-       TRACE_FUN(8, "check_for_eof");
-       static unsigned last_reference = INT_MAX;
-       int result;
-
-       if (segment < last_reference) {
-               reset_eof_list();
-       }
-       last_reference = segment;
-       while (eof_index < nr_of_eof_marks && segment > eof_mark_ptr->segment) {
-               ++eof_mark_ptr;
-               ++eof_index;
-       }
-       if (eof_index < nr_of_eof_marks && segment == eof_mark_ptr->segment) {
-               TRACEx3(5, "hit mark %d/%d at index %d",
-                       eof_map[eof_index].mark.segment, eof_map[eof_index].mark.sector,
-                       eof_index);
-               if (eof_mark_ptr->sector >= SECTORS_PER_SEGMENT) {
-                       TRACEx2(-1, "Bad file mark detected: %d/%d",
-                           eof_mark_ptr->segment, eof_mark_ptr->sector);
-                       result = 0;     /* return bogus (but valid) value */
-               } else {
-                       result = eof_mark_ptr->sector;
-               }
-       } else {
-               result = 0;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-void clear_eof_mark_if_set(unsigned segment, unsigned byte_count)
-{
-       TRACE_FUN(5, "clear_eof_mark_if_set");
-       if (ftape_fmt_version != 0 &&
-           check_for_eof(segment) > 0 &&
-           byte_count >= eof_mark_ptr->sector * SECTOR_SIZE) {
-               TRACEx3(5, "clearing mark %d/%d at index %d",
-                eof_mark_ptr->segment, eof_mark_ptr->sector, eof_index);
-               memmove(&eof_map[eof_index], &eof_map[eof_index + 1],
-                       (nr_of_eof_marks - eof_index) * sizeof(*eof_map));
-               --nr_of_eof_marks;
-               failed_sector_log_changed = 1;
-       }
-       TRACE_EXIT;
-}
-
-void put_file_mark_in_map(unsigned segment, unsigned sector)
-{
-       TRACE_FUN(8, "put_file_mark_in_map");
-       eof_mark_union new;
-       int index;
-       eof_mark_union *ptr;
-
-       if (ftape_fmt_version != 0) {
-               new.mark.segment = segment;
-               new.mark.sector = sector;
-               for (index = 0, ptr = &eof_map[0];
-                 index < nr_of_eof_marks && ptr->mark.segment < segment;
-                    ++index, ++ptr) {
-               }
-               if (index < nr_of_eof_marks) {
-                       if (ptr->mark.segment == segment) {
-                               /* overwrite */
-                               if (ptr->mark.sector == sector) {
-                                       TRACEx2(5, "mark %d/%d already exists",
-                                               new.mark.segment, new.mark.sector);
-                               } else {
-                                       TRACEx5(5, "overwriting %d/%d at index %d with %d/%d",
-                                               ptr->mark.segment, ptr->mark.sector, index,
-                                               new.mark.segment, new.mark.sector);
-                                       ptr->entry = new.entry;
-                                       failed_sector_log_changed = 1;
-                               }
-                       } else {
-                               /* insert */
-                               TRACEx5(5, "inserting %d/%d at index %d before %d/%d",
-                               new.mark.segment, new.mark.sector, index,
-                                   ptr->mark.segment, ptr->mark.sector);
-                               memmove(ptr + 1, ptr, (nr_of_eof_marks - index) * sizeof(*eof_map));
-                               ptr->entry = new.entry;
-                               ++nr_of_eof_marks;
-                               failed_sector_log_changed = 1;
-                       }
-               } else {
-                       /* append */
-                       TRACEx3(5, "appending %d/%d at index %d",
-                               new.mark.segment, new.mark.sector, index);
-                       ptr->entry = new.entry;
-                       ++nr_of_eof_marks;
-                       failed_sector_log_changed = 1;
-               }
-       }
-       TRACE_EXIT;
-}
-
-/*  Write count file marks to tape starting at first non-bad
- *  sector following the given segment and sector.
- *  sector = base 1 !
- */
-int ftape_weof(unsigned count, unsigned segment, unsigned sector)
-{
-       TRACE_FUN(5, "ftape_weof");
-       int result = 0;
-       unsigned long mask = get_bad_sector_entry(segment);
-       unsigned sector_nr = 0;
-
-       if (ftape_fmt_version != 0) {
-               if (sector < 1 || sector > 29 ||
-                   segment + count >= ftape_last_segment.id) {
-                       TRACEx3(5, "parameter out of range: %d, %d, %d", count, segment, sector);
-                       result = -EIO;
-               } else {
-                       while (count-- > 0) {
-                               do {    /* count logical sectors */
-                                       do {    /* skip until good sector */
-                                               while (mask & 1) {      /* skip bad sectors */
-                                                       ++sector_nr;
-                                                       mask >>= 1;
-                                               }
-                                               if (sector_nr >= 29) {
-                                                       if (++segment >= ftape_last_segment.id) {
-                                                               TRACEx1(5, "segment out of range: %d", segment);
-                                                               result = -EIO;
-                                                               break;
-                                                       }
-                                                       mask = get_bad_sector_entry(segment);
-                                                       sector_nr = 0;
-                                               }
-                                       } while (mask & 1);
-                                       ++sector_nr;    /* point to good sector */
-                                       mask >>= 1;
-                               } while (--sector);
-                               if (result >= 0) {
-                                       TRACEx2(5, "writing filemark %d/%d", segment, sector_nr);
-                                       put_file_mark_in_map(segment, sector_nr);
-                                       ++segment;      /* next segment */
-                                       sector_nr = 0;
-                                       sector = 1;     /* first sector */
-                               }
-                       }
-               }
-       } else {
-               result = -EPERM;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_erase(void)
-{
-       TRACE_FUN(5, "ftape_erase");
-       int result = 0;
-       int i;
-       unsigned long now = 0;
-       byte *buffer = deblock_buffer;
-
-       if (write_protected) {
-               result = -EROFS;
-       } else {
-               result = read_header_segment(buffer);
-               if (result >= 0) {
-                       /*  Copy entries from bad-sector-log into bad-sector-map
-                        */
-                       TRACEx1(5, "old label: `%s'", (char *) (buffer + 30));
-                       if (!ftape_validate_label((char *) &buffer[30])) {
-                               TRACE(5, "invalid label, overwriting with new");
-                               memset(buffer + 30, 0, 44);
-                               memcpy(buffer + 30, linux_tape_label, strlen(linux_tape_label));
-                               buffer[30 + strlen(linux_tape_label)] = '2';
-                               TRACEx1(5, "new label: `%s'", (char *) (buffer + 30));
-                               PUT4(buffer, 74, now);
-                               if (format_code != 4) {
-                                       for (i = 0; i < nr_of_eof_marks; ++i) {
-                                               unsigned failing_segment = eof_map[i].mark.segment;
-
-                                               if (!valid_segment_no(failing_segment)) {
-                                                       TRACEi(4, "bad entry in failed sector log:", failing_segment);
-                                               } else {
-                                                       put_bad_sector_entry(failing_segment, EMPTY_SEGMENT);
-                                                       TRACEx2(4, "moved entry %d from failed sector log (%d)",
-                                                               i, failing_segment);
-                                               }
-                                       }
-                               }
-                       }
-                       /*  Clear failed sector log: remove all tape marks
-                        */
-                       failed_sector_log_changed = 1;
-                       memset(eof_map, 0, sizeof(eof_map));
-                       nr_of_eof_marks = 0;
-                       ftape_fmt_version = max_fmt_version;
-#if 0
-                       fix_tape(buffer);       /* see ftape-bsm.c ! */
-#endif
-                       result = ftape_update_header_segments(buffer, 1);
-                       prevent_flush();        /* prevent flush_buffers writing file marks */
-                       reset_eof_list();
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-void extract_file_marks(byte * address)
-{
-       TRACE_FUN(8, "extract_file_marks");
-       int i;
-
-       if (format_code == 4) {
-               byte *end;
-               byte *start = find_end_of_bsm_list(address + 256,
-                                            address + 29 * SECTOR_SIZE);
-
-               memset(eof_map, 0, sizeof(eof_map));
-               nr_of_eof_marks = 0;
-               if (start) {
-                       start += 3;     /* skip end of list mark */
-                       end = find_end_of_eof_list(start, address + 29 * SECTOR_SIZE);
-                       if (end && end - start <= sizeof(eof_map)) {
-                               nr_of_eof_marks = (end - start) / sizeof(unsigned long);
-                               memcpy(eof_map, start, end - start);
-                       } else {
-                               TRACE(1, "File Mark List is too long or damaged !");
-                       }
-               } else {
-                       TRACE(1, "Bad Sector List is too long or damaged !");
-               }
-       } else {
-               memcpy(eof_map, address + 256, sizeof(eof_map));
-               nr_of_eof_marks = GET2(address, 144);
-       }
-       TRACEi(4, "number of file marks:", nr_of_eof_marks);
-       if (ftape_fmt_version == 1) {
-               TRACE(-1, "swapping version 1 fields");
-               /*  version 1 format uses swapped sector and segment fields, correct that !
-                */
-               for (i = 0; i < nr_of_eof_marks; ++i) {
-                       unsigned short tmp = eof_map[i].mark.segment;
-                       eof_map[i].mark.segment = eof_map[i].mark.sector;
-                       eof_map[i].mark.sector = tmp;
-               }
-       }
-       for (i = 0; i < nr_of_eof_marks; ++i) {
-               TRACEx2(4, "eof mark: %5d/%2d",
-                       eof_map[i].mark.segment, eof_map[i].mark.sector);
-       }
-       reset_eof_list();
-       TRACE_EXIT;
-}
-
-int update_failed_sector_log(byte * buffer)
-{
-       TRACE_FUN(8, "update_failed_sector_log");
-
-       if (ftape_fmt_version != 0 && failed_sector_log_changed) {
-               if (ftape_fmt_version == 1) {
-                       TRACE(-1, "upgrading version 1 format to version 2");
-                       /*  version 1 will be upgraded to version 2 when written.
-                        */
-                       buffer[30 + strlen(linux_tape_label)] = '2';
-                       ftape_fmt_version = 2;
-                       TRACEx1(-1, "new tape label = \"%s\"", &buffer[30]);
-               }
-               if (format_code == 4) {
-                       byte *dest = find_end_of_bsm_list(buffer + 256,
-                                         buffer + 29 * SECTOR_SIZE) + 3;
-
-                       if (dest) {
-                               TRACEx2(4, "eof_map at byte offset %6d, size %d",
-                                       dest - buffer - 256, nr_of_eof_marks * sizeof(unsigned long));
-                               memcpy(dest, eof_map, nr_of_eof_marks * sizeof(unsigned long));
-                               PUT4(dest, nr_of_eof_marks * sizeof(unsigned long), 0);
-                       }
-               } else {
-                       memcpy(buffer + 256, eof_map, sizeof(eof_map));
-                       PUT2(buffer, 144, nr_of_eof_marks);
-               }
-               failed_sector_log_changed = 0;
-               return 1;
-       }
-       TRACE_EXIT;
-       return 0;
-}
-
-int ftape_seek_eom(void)
-{
-       TRACE_FUN(5, "ftape_seek_eom");
-       int result = 0;
-       unsigned eom;
-
-       if (first_data_segment == -1) {
-               result = read_header_segment(deblock_buffer);
-       }
-       if (result >= 0 && ftape_fmt_version != 0) {
-               eom = first_data_segment;
-               eof_index = 0;
-               eof_mark_ptr = &eof_map[0].mark;
-               /*  If fresh tape, count should be zero but we don't
-                *  want to worry about the case it's one.
-                */
-               for (eof_index = 1, eof_mark_ptr = &eof_map[1].mark;
-                    eof_index < nr_of_eof_marks; ++eof_index, ++eof_mark_ptr) {
-                       /*  The eom is recorded as two eof marks in succeeding segments
-                        *  where the second one is always at segment number 1.
-                        */
-                       if (eof_mark_ptr->sector == 1) {
-                               if (eof_mark_ptr->segment == (eof_mark_ptr - 1)->segment + 1) {
-                                       eom = eof_mark_ptr->segment;
-                                       break;
-                               }
-                       }
-               }
-               ftape_seg_pos = eom;
-               TRACEx1(5, "eom found at segment %d", eom);
-       } else {
-               TRACE(5, "Couldn't get eof mark table");
-               result = -EIO;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_seek_eof(unsigned count)
-{
-       TRACE_FUN(5, "ftape_seek_eof");
-       int result = 0;
-       enum {
-               not = 0, begin, end
-       } bad_seek = not;
-
-       if (first_data_segment == -1) {
-               result = read_header_segment(deblock_buffer);
-       }
-       TRACEx1(5, "tape positioned at segment %d", ftape_seg_pos);
-       if (ftape_fmt_version == 0) {
-               result = -1;
-       }
-       if (result >= 0 && count != 0) {
-               for (eof_index = 0; eof_index <= nr_of_eof_marks; ++eof_index) {
-                       if (eof_index == nr_of_eof_marks ||     /* start seeking after last mark */
-                           ftape_seg_pos <= eof_map[eof_index].mark.segment) {
-                               eof_index += count;
-                               if (eof_index < 1) {    /* begin of tape */
-                                       ftape_seg_pos = first_data_segment;
-                                       if (eof_index < 0) {    /* `before' begin of tape */
-                                               eof_index = 0;
-                                               bad_seek = begin;
-                                       }
-                               } else if (eof_index >= nr_of_eof_marks) {      /* `after' end of tape */
-                                       ftape_seg_pos = segments_per_track * tracks_per_tape;
-                                       if (eof_index > nr_of_eof_marks) {
-                                               eof_index = nr_of_eof_marks;
-                                               bad_seek = end;
-                                       }
-                               } else {        /* after requested file mark */
-                                       ftape_seg_pos = eof_map[eof_index - 1].mark.segment + 1;
-                               }
-                               eof_mark_ptr = &eof_map[eof_index].mark;
-                               break;
-                       }
-               }
-       }
-       if (result < 0) {
-               TRACE(5, "Couldn't get eof mark table");
-               result = -EIO;
-       } else if (bad_seek != not) {
-               TRACEx1(1, "seek reached %s of tape",
-                       (bad_seek == begin) ? "begin" : "end");
-               result = -EIO;
-       } else {
-               TRACEx1(5, "tape repositioned to segment %d", ftape_seg_pos);
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_file_no(daddr_t * f_no, daddr_t * b_no)
-{
-       TRACE_FUN(5, "ftape_file_no");
-       int result = 0;
-       int i;
-
-       *f_no = eof_index;
-       *b_no = ftape_seg_pos;
-       TRACEi(4, "number of file marks:", nr_of_eof_marks);
-       for (i = 0; i < nr_of_eof_marks; ++i) {
-               TRACEx2(4, "eof mark: %5d/%2d",
-                       eof_map[i].mark.segment, eof_map[i].mark.sector);
-       }
-       TRACE_EXIT;
-       return result;
-}
diff --git a/drivers/char/ftape/ftape-eof.h b/drivers/char/ftape/ftape-eof.h
deleted file mode 100644 (file)
index 58df1f0..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-#ifndef _FTAPE_EOF_H
-#define _FTAPE_EOF_H
-
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-eof.h,v $
- $Author: bas $
- *
- $Revision: 1.12 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- *      Definitions and declarations for the end of file markers
- *      for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-/*      ftape-eof.c defined global vars.
- */
-extern int failed_sector_log_changed;
-extern int eof_mark;
-
-/*      ftape-eof.c defined global functions.
- */
-extern void clear_eof_mark_if_set(unsigned segment, unsigned byte_count);
-extern void reset_eof_list(void);
-extern int check_for_eof(unsigned segment);
-extern int ftape_weof(unsigned count, unsigned segment, unsigned sector);
-extern int ftape_erase(void);
-extern void put_file_mark_in_map(unsigned segment, unsigned sector);
-extern void extract_file_marks(byte * address);
-extern int update_failed_sector_log(byte * buffer);
-extern int ftape_seek_eom(void);
-extern int ftape_seek_eof(unsigned count);
-extern int ftape_file_no(daddr_t * file, daddr_t * block);
-extern int ftape_validate_label(char *label);
-
-#endif
diff --git a/drivers/char/ftape/ftape-io.c b/drivers/char/ftape/ftape-io.c
deleted file mode 100644 (file)
index 1f15eef..0000000
+++ /dev/null
@@ -1,1050 +0,0 @@
-/*
- *      Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-io.c,v $
- $Author: bas $
- *
- $Revision: 1.58 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- *      This file contains the general control functions
- *      for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/ioctl.h>
-#include <linux/mtio.h>
-
-#include "tracing.h"
-#include "fdc-io.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "ftape-eof.h"
-#include "kernel-interface.h"
-#include "calibr.h"
-
-/*      Global vars.
- */
-/* NOTE: sectors start numbering at 1, all others at 0 ! */
-timeout_table timeout;
-vendor_struct drive_type;
-int qic_std;
-int tape_len;
-volatile int current_command;
-const struct qic117_command_table qic117_cmds[] = QIC117_COMMANDS;
-int might_be_off_track;
-
-/*      Local vars.
- */
-static int command_parameter = 0;
-/*      command-restrictions is a table according
- *      to the QIC-117 specs specifying the state
- *      the drive status should be in at command execution.
- */
-static const ftape_error ftape_errors[] = QIC117_ERRORS;
-static int ftape_udelay_count;
-static int ftape_udelay_time;
-static const struct {
-       char *text;
-       int fdc_code;
-       byte drive_code;
-       int precomp;
-} rates[4] = {
-
-#if defined(FDC_82078SL)
-       {
-               "2 M", -1 /* unsupported */ , QIC_CONFIG_RATE_2000, 0
-       },
-#else
-       {
-               "2 M", fdc_data_rate_2000, QIC_CONFIG_RATE_2000, 0
-       },
-#endif
-       {
-               "1 M", fdc_data_rate_1000, QIC_CONFIG_RATE_1000, 42
-       },
-       {
-               "500 K", fdc_data_rate_500, QIC_CONFIG_RATE_500, 125
-       },
-       {
-               "250 K", fdc_data_rate_250, QIC_CONFIG_RATE_250, 250
-       },
-};
-typedef enum {
-       prehistoric, pre_qic117c, post_qic117b, post_qic117d
-} qic_model;
-
-
-void udelay(int usecs)
-{
-       volatile int count = (1 + (usecs * ftape_udelay_count - 1) /
-                             ftape_udelay_time);
-       volatile int i;
-
-       while (count-- > 0) {
-               for (i = 0; i < 20; ++i);
-       }
-}
-
-int udelay_calibrate(void)
-{
-       return calibrate("udelay", udelay, &ftape_udelay_count, &ftape_udelay_time);
-}
-
-/*      Delay (msec) routine.
- */
-void ftape_sleep(unsigned int time)
-{
-       TRACE_FUN(8, "ftape_sleep");
-       unsigned long flags;
-       int ticks = 1 + (time + MSPT - 1) / MSPT;
-
-       /*    error in range [0..1] MSPT
-        */
-       if (time < MSPT) {
-               /*  Time too small for scheduler, do a busy wait ! */
-               udelay(1000 * time);
-       } else {
-               TRACEx2(8, "%d msec, %d ticks", time, ticks);
-               current->timeout = jiffies + ticks;
-               current->state = TASK_INTERRUPTIBLE;
-               save_flags(flags);
-               sti();
-               do {
-                       while (current->state != TASK_RUNNING) {
-                               schedule();
-                       }
-                       if (signal_pending(current)) {
-                               TRACE(1, "awoken by non-blocked signal :-(");
-                               break;  /* exit on signal */
-                       }
-               } while (current->timeout > 0);
-               restore_flags(flags);
-       }
-       TRACE_EXIT;
-}
-
-/* forward */ int ftape_report_raw_drive_status(int *status);
-
-/*      Issue a tape command:
- *      Generate command # of step pulses.
- */
-int ftape_command(int command)
-{
-       TRACE_FUN(8, "ftape_command");
-       int result = 0;
-       int track;
-       int old_tracing = tracing;
-       static int level = 0;
-       int status = -1;
-
-       if (++level > 5) {
-               /*  This is a bug we'll want to know about.
-                */
-               TRACEx1(1, "bug - recursion for command: %d", command);
-               result = -EIO;
-       } else if (command_parameter) {
-               /*  Don't check restrictions for parameters.
-                */
-               TRACEx1(5, "called with parameter = %d", command - 2);
-       } else if (command <= 0 || command > NR_ITEMS(qic117_cmds)) {
-               /*  This is a bug we'll want to know about too.
-                */
-               TRACEx1(-1, "bug - bad command: %d", command);
-               result = -EIO;
-       } else {
-               /*  disable logging and restriction check for some commands,
-                *  check all other commands that have a prescribed starting status.
-                */
-               if (command == QIC_REPORT_DRIVE_STATUS) {
-                       TRACE(8, "report drive status called");
-                       tracing = 0;
-               } else if (command == QIC_REPORT_NEXT_BIT) {
-                       tracing = 0;
-               } else {
-                       TRACEx1(5, "%s", qic117_cmds[command].name);
-                       /*  A new motion command during an uninterruptible (motion)
-                        *  command requires a ready status before the new command
-                        *  can be issued. Otherwise a new motion command needs to
-                        *  be checked against required status.
-                        */
-                       if (qic117_cmds[command].cmd_type == motion &&
-                           qic117_cmds[current_command].non_intr) {
-                               ftape_report_raw_drive_status(&status);
-                               if ((status & QIC_STATUS_READY) == 0) {
-                                       TRACEx2(4, "motion cmd (%d) during non-intr cmd (%d)",
-                                               command, current_command);
-                                       TRACE(4, "waiting until drive gets ready");
-                                       ftape_ready_wait(timeout.seek, &status);
-                               }
-                       }
-                       if (qic117_cmds[command].mask != 0) {
-                               byte difference;
-
-                               /*  Some commands do require a certain status:
-                                */
-                               if (status == -1) {     /* not yet set */
-                                       ftape_report_raw_drive_status(&status);
-                               }
-                               difference = ((status ^ qic117_cmds[command].state) &
-                                             qic117_cmds[command].mask);
-                               /*  Wait until the drive gets ready. This may last forever
-                                *  if the drive never gets ready...
-                                */
-                               while ((difference & QIC_STATUS_READY) != 0) {
-                                       TRACEx1(4, "command %d issued while not ready", command);
-                                       TRACE(4, "waiting until drive gets ready");
-                                       ftape_ready_wait(timeout.seek, &status);
-                                       difference = ((status ^ qic117_cmds[command].state) &
-                                             qic117_cmds[command].mask);
-                                       /*  Bail out on signal !
-                                        */
-                                       if (current->signal & _DONT_BLOCK) {
-                                               result = -EINTR;
-                                               break;
-                                       }
-                               }
-                               while (result == 0 && (difference & QIC_STATUS_ERROR) != 0) {
-                                       int err;
-                                       int cmd;
-
-                                       TRACEx1(4, "command %d issued while error pending", command);
-                                       TRACE(4, "clearing error status");
-                                       ftape_report_error(&err, &cmd, 1);
-                                       ftape_report_raw_drive_status(&status);
-                                       difference = ((status ^ qic117_cmds[command].state) &
-                                             qic117_cmds[command].mask);
-                                       /*  Bail out on fatal signal !
-                                        */
-                                       if (current->signal & _DONT_BLOCK) {
-                                               result = -EINTR;
-                                               break;
-                                       }
-                               }
-                               if (result == 0 && difference) {
-                                       /*  Any remaining difference can't be solved here.
-                                        */
-                                       if (difference & (QIC_STATUS_CARTRIDGE_PRESENT |
-                                              QIC_STATUS_NEW_CARTRIDGE |
-                                               QIC_STATUS_REFERENCED)) {
-                                               TRACE(1, "Fatal: tape removed or reinserted !");
-                                               ftape_failure = 1;
-                                       } else {
-                                               TRACEx2(1, "wrong state: 0x%02x should be: 0x%02x",
-                                                       status & qic117_cmds[command].mask,
-                                                       qic117_cmds[command].state);
-                                       }
-                                       result = -EIO;
-                               }
-                               if (~status & QIC_STATUS_READY & qic117_cmds[command].mask) {
-                                       TRACE(1, "Bad: still busy!");
-                                       result = -EBUSY;
-                               }
-                       }
-               }
-       }
-       tracing = old_tracing;
-       /*  Now all conditions are met or result is < 0.
-        */
-       if (result >= 0) {
-               /*  Always wait for a command_timeout period to separate
-                *  individuals commands and/or parameters.
-                */
-               ftape_sleep(3 * MILLISECOND);
-               /*  Keep cylinder nr within range, step towards home if possible.
-                */
-               if (current_cylinder >= command) {
-                       track = current_cylinder - command;
-               } else {
-                       track = current_cylinder + command;
-               }
-               result = fdc_seek(track);
-               /*  position is no longer valid after any of these commands
-                *  have completed.
-                */
-               if (qic117_cmds[command].cmd_type == motion &&
-                   command != QIC_LOGICAL_FORWARD && command != QIC_STOP_TAPE) {
-                       location.known = 0;
-               }
-               command_parameter = 0;  /* always turned off for next command */
-               current_command = command;
-       }
-       --level;
-       TRACE_EXIT;
-       return result;
-}
-
-/*      Send a tape command parameter:
- *      Generates command # of step pulses.
- *      Skips tape-status call !
- */
-int ftape_parameter(int command)
-{
-       command_parameter = 1;
-       return ftape_command(command + 2);
-}
-
-/*      Wait for the drive to get ready.
- *      timeout time in milli-seconds
- *      Returned status is valid if result != -EIO
- */
-int ftape_ready_wait(int timeout, int *status)
-{
-       TRACE_FUN(8, "ftape_ready_wait");
-       int result;
-       unsigned long t0;
-       const int poll_delay = 100 * MILLISECOND;
-
-       for (;;) {
-               t0 = jiffies;
-               result = ftape_report_raw_drive_status(status);
-               if (result < 0) {
-                       TRACE(1, "ftape_report_raw_drive_status failed");
-                       result = -EIO;
-                       break;
-               }
-               if (*status & QIC_STATUS_READY) {
-                       result = 0;
-                       break;
-               }
-               if (timeout >= 0) {
-                       /* this will fail when jiffies wraps around about
-                        * once every year :-)
-                        */
-                       timeout -= ((jiffies - t0) * SECOND) / HZ;
-                       if (timeout <= 0) {
-                               TRACE(1, "timeout");
-                               result = -ETIME;
-                               break;
-                       }
-                       ftape_sleep(poll_delay);
-                       timeout -= poll_delay;
-               } else {
-                       ftape_sleep(poll_delay);
-               }
-               if (current->signal & _NEVER_BLOCK) {
-                       TRACE(1, "interrupted by fatal signal");
-                       result = -EINTR;
-                       break;  /* exit on signal */
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-/*      Issue command and wait up to timeout seconds for drive ready
- */
-int ftape_command_wait(int command, int timeout, int *status)
-{
-       TRACE_FUN(8, "ftape_command_wait");
-       int result;
-
-       /* Drive should be ready, issue command
-        */
-       result = ftape_command(command);
-       if (result >= 0) {
-               result = ftape_ready_wait(timeout, status);
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_parameter_wait(int command, int timeout, int *status)
-{
-       TRACE_FUN(8, "ftape_parameter_wait");
-       int result;
-
-       /* Drive should be ready, issue command
-        */
-       result = ftape_parameter(command);
-       if (result >= 0) {
-               result = ftape_ready_wait(timeout, status);
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-/*--------------------------------------------------------------------------
- *      Report operations
- */
-
-/* Query the drive about its status.  The command is sent and
-   result_length bits of status are returned (2 extra bits are read
-   for start and stop). */
-
-static int ftape_report_operation(int *status, int command, int result_length)
-{
-       TRACE_FUN(8, "ftape_report_operation");
-       int i, st3;
-       int result;
-       unsigned int t0, t1, dt;
-
-       result = ftape_command(command);
-       if (result < 0) {
-               TRACE(1, "ftape_command failed");
-               TRACE_EXIT;
-               return result;
-       }
-       t0 = timestamp();
-       dt = 0;
-       i = 0;
-       do {
-               ++i;
-               ftape_sleep(3 * MILLISECOND);   /* see remark below */
-               result = fdc_sense_drive_status(&st3);
-               if (result < 0) {
-                       TRACE(1, "fdc_sense_drive_status failed");
-                       TRACE_EXIT;
-                       return result;
-               }
-               /*  Calculate time difference every iteration because timer may
-                *  wrap around (but only one !) and timediff will account for this.
-                *  Note that the sleep above must be < 1/HZ or we'll lose ticks !
-                */
-               t1 = timestamp();
-               dt += timediff(t0, t1);
-               t0 = t1;
-               /*  Ack should be asserted within Ttimout + Tack = 6 msec.
-                *  Looks like some drives fail to do this so extend this
-                *  period to 300 msec.
-                */
-       } while (!(st3 & ST3_TRACK_0) && dt < 300000);
-       if (st3 & ST3_TRACK_0) {
-               /*  dt may be larger than expected because of other tasks
-                *  scheduled while we were sleeping.
-                */
-               if (i > 1 && dt > 6000) {
-                       TRACEx2(1, "Acknowledge after %u msec. (%i iter)", dt / 1000, i);
-               }
-       } else {
-               TRACEx2(1, "No acknowledge after %u msec. (%i iter)", dt / 1000, i);
-               TRACE(1, "timeout on Acknowledge");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       *status = 0;
-       for (i = 0; i < result_length + 1; i++) {
-               result = ftape_command(QIC_REPORT_NEXT_BIT);
-               if (result < 0) {
-                       TRACE(1, "report next bit failed");
-                       TRACE_EXIT;
-                       return result;
-               }
-#if 1
-               /*  fdc_seek does interrupt wait, so why should we ?
-                *  (it will only fail causing fdc to be reset...)
-                *  It's only purpose may be the delay, we'll have to find out!
-                */
-#else
-               fdc_interrupt_wait(25 * MILLISECOND);   /* fails only if hw fails */
-#endif
-               result = fdc_sense_drive_status(&st3);
-               if (result < 0) {
-                       TRACE(1, "fdc_sense_drive_status (2) failed");
-                       TRACE_EXIT;
-                       return result;
-               }
-               if (i < result_length) {
-                       *status |= ((st3 & ST3_TRACK_0) ? 1 : 0) << i;
-               } else {
-                       if ((st3 & ST3_TRACK_0) == 0) {
-                               TRACE(1, "missing status stop bit");
-                               TRACE_EXIT;
-                               return -EIO;
-                       }
-               }
-       }
-       /* this command will put track zero and index back into normal state */
-       result = ftape_command(QIC_REPORT_NEXT_BIT);
-       TRACE_EXIT;
-       return 0;
-}
-
-/* Report the current drive status. */
-
-int ftape_report_raw_drive_status(int *status)
-{
-       TRACE_FUN(8, "ftape_report_raw_drive_status");
-       int result;
-       int count = 0;
-
-       do {
-               result = ftape_report_operation(status, QIC_REPORT_DRIVE_STATUS, 8);
-       } while (result < 0 && ++count <= 3);
-       if (result < 0) {
-               TRACE(1, "report_operation failed");
-               result = -EIO;
-       } else if (*status & QIC_STATUS_READY) {
-               current_command = 0;    /* completed */
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_report_drive_status(int *status)
-{
-       TRACE_FUN(8, "ftape_report_drive_status");
-       int result;
-
-       result = ftape_report_raw_drive_status(status);
-       if (result < 0) {
-               TRACE(1, "ftape_report_raw_drive_status failed");
-               TRACE_EXIT;
-               return result;
-       }
-       if (*status & QIC_STATUS_NEW_CARTRIDGE ||
-           !(*status & QIC_STATUS_CARTRIDGE_PRESENT)) {
-               ftape_failure = 1;      /* will inhibit further operations */
-               TRACE_EXIT;
-               return -EIO;
-       }
-       if (*status & QIC_STATUS_READY && *status & QIC_STATUS_ERROR) {
-               /*  Let caller handle all errors */
-               TRACE(2, "warning: error status set!");
-               result = 1;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_report_error(int *error, int *command, int report)
-{
-       TRACE_FUN(8, "ftape_report_error");
-       int code;
-       int result;
-
-       result = ftape_report_operation(&code, QIC_REPORT_ERROR_CODE, 16);
-       if (result < 0) {
-               result = -EIO;
-       } else {
-               *error = code & 0xff;
-               *command = (code >> 8) & 0xff;
-               if (report) {
-                       if (*error != 0) {
-                               TRACEi(3, "errorcode:", *error);
-                       } else {
-                               TRACE(3, "No error");
-                       }
-               }
-               if (report && *error != 0 && tracing > 3) {
-                       if (*error >= 0 && *error < NR_ITEMS(ftape_errors)) {
-                               TRACEx1(-1, "%sFatal ERROR:",
-                                       (ftape_errors[*error].fatal ? "" : "Non-"));
-                               TRACEx1(-1, "%s ...", ftape_errors[*error].message);
-                       } else {
-                               TRACE(-1, "Unknown ERROR !");
-                       }
-                       if (*command >= 0 && *command < NR_ITEMS(qic117_cmds) &&
-                           qic117_cmds[*command].name != NULL) {
-                               TRACEx1(-1, "... caused by command \'%s\'",
-                                       qic117_cmds[*command].name);
-                       } else {
-                               TRACEi(-1, "... caused by unknown command", *command);
-                       }
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_in_error_state(int status)
-{
-       TRACE_FUN(8, "ftape_in_error_state");
-       int result = 0;
-
-       if ((status & QIC_STATUS_READY) && (status & QIC_STATUS_ERROR)) {
-               TRACE(2, "warning: error status set!");
-               result = 1;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-static int ftape_report_configuration(qic_model * model, int *rate,
-                                     int *qic_std, int *tape_len)
-{
-       int result;
-       int config;
-       int status;
-
-       TRACE_FUN(8, "ftape_report_configuration");
-       result = ftape_report_operation(&config, QIC_REPORT_DRIVE_CONFIGURATION, 8);
-       if (result < 0) {
-               *model = prehistoric;
-               *rate = QIC_CONFIG_RATE_500;
-               *qic_std = QIC_TAPE_QIC40;
-               *tape_len = 205;
-               result = 0;
-       } else {
-               *rate = (config & QIC_CONFIG_RATE_MASK) >> QIC_CONFIG_RATE_SHIFT;
-               result = ftape_report_operation(&status, QIC_REPORT_TAPE_STATUS, 8);
-               if (result < 0) {
-                       /* pre- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is valid.
-                        */
-                       *qic_std = (config & QIC_CONFIG_80) ? QIC_TAPE_QIC80 : QIC_TAPE_QIC40;
-                       *tape_len = (config & QIC_CONFIG_LONG) ? 307 : 205;
-                       *model = pre_qic117c;
-                       result = 0;
-               } else {
-                       *model = post_qic117b;
-                       TRACEx1(8, "report tape status result = %02x", status);
-                       /* post- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is invalid.
-                        */
-                       switch (status & QIC_TAPE_STD_MASK) {
-                       case QIC_TAPE_QIC40:
-                       case QIC_TAPE_QIC80:
-                       case QIC_TAPE_QIC3020:
-                       case QIC_TAPE_QIC3010:
-                               *qic_std = status & QIC_TAPE_STD_MASK;
-                               break;
-                       default:
-                               *qic_std = -1;
-                               break;
-                       }
-                       switch (status & QIC_TAPE_LEN_MASK) {
-                       case QIC_TAPE_205FT:
-                               /* Unfortunately the new QIC-117 rev G standard shows
-                                * no way to discriminate between 205 and 425 ft tapes.
-                                * The obvious way seems not to be used: the QIC_CONFIG_LONG
-                                * bit isn't used for this (on all drives ?).
-                                */
-                               if (config & QIC_CONFIG_LONG) {
-                                       *tape_len = 425;        /* will this ever execute ??? */
-                               } else {
-                                       *tape_len = 0;  /* length unknown: 205 or 425 ft. */
-                               }
-                               break;
-                       case QIC_TAPE_307FT:
-                               *tape_len = 307;
-                               break;
-                       case QIC_TAPE_400FT:
-                               /*
-                                * Trouble! Iomega Ditto 800 and Conner TST800R drives reports
-                                * 400ft for 750ft tapes. Yuck, yuck, yuck.  Since the value
-                                * is only used to compute a timeout value, the largest of the
-                                * two is used.
-                                */
-                               *tape_len = 750;        /* either 400 or 750 ft. */
-                               break;
-                       case QIC_TAPE_1100FT:
-                               *tape_len = 1100;
-                               break;
-                       case QIC_TAPE_FLEX:
-                               *tape_len = 0;
-                               break;
-                       default:
-                               *tape_len = -1;
-                               break;
-                       }
-                       if (*qic_std == -1 || *tape_len == -1) {
-                               TRACE(2, "post qic-117b spec drive with unknown tape");
-                               result = -EIO;
-                       } else {
-                               result = 0;
-                       }
-               }
-       }
-       TRACE_EXIT;
-       return (result < 0) ? -EIO : 0;
-}
-
-int ftape_report_rom_version(int *version)
-{
-       int result;
-
-       result = ftape_report_operation(version, QIC_REPORT_ROM_VERSION, 8);
-       return (result < 0) ? -EIO : 0;
-}
-
-int ftape_report_signature(int *signature)
-{
-       int result;
-
-       result = ftape_command(28);
-       result = ftape_report_operation(signature, 9, 8);
-       result = ftape_command(30);
-       return (result < 0) ? -EIO : 0;
-}
-
-void ftape_report_vendor_id(unsigned int *id)
-{
-       TRACE_FUN(8, "ftape_report_vendor_id");
-       int result;
-
-       /*
-        *    We'll try to get a vendor id from the drive.
-        *    First according to the QIC-117 spec, a 16-bit id is requested.
-        *    If that fails we'll try an 8-bit version, otherwise we'll try
-        *    an undocumented query.
-        */
-       result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 16);
-       if (result < 0) {
-               result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 8);
-               if (result < 0) {
-                       /*  The following is an undocumented call found in the CMS code.
-                        */
-                       result = ftape_report_operation((int *) id, 24, 8);
-                       if (result < 0) {
-                               *id = UNKNOWN_VENDOR;
-                       } else {
-                               TRACEx1(4, "got old 8 bit id: %04x", *id);
-                               *id |= 0x20000;
-                       }
-               } else {
-                       TRACEx1(4, "got 8 bit id: %04x", *id);
-                       *id |= 0x10000;
-               }
-       } else {
-               TRACEx1(4, "got 16 bit id: %04x", *id);
-       }
-       if (*id == 0x0047) {
-               int version;
-               int sign;
-
-               result = ftape_report_rom_version(&version);
-               if (result < 0) {
-                       TRACE(-1, "report rom version failed");
-                       TRACE_EXIT;
-                       return;
-               }
-               TRACEx1(4, "CMS rom version: %d", version);
-               ftape_command(QIC_ENTER_DIAGNOSTIC_1);
-               ftape_command(QIC_ENTER_DIAGNOSTIC_1);
-               result = ftape_report_operation(&sign, 9, 8);
-               if (result < 0) {
-                       int error, command;
-
-                       ftape_report_error(&error, &command, 1);
-                       ftape_command(QIC_ENTER_PRIMARY_MODE);
-                       TRACE_EXIT;
-                       return; /* faalt hier ! */
-               } else {
-                       TRACEx1(4, "CMS signature: %02x", sign);
-               }
-               if (sign == 0xa5) {
-                       result = ftape_report_operation(&sign, 37, 8);
-                       if (result < 0) {
-                               if (version >= 63) {
-                                       *id = 0x8880;
-                                       TRACE(4, "This is an Iomega drive !");
-                               } else {
-                                       *id = 0x0047;
-                                       TRACE(4, "This is a real CMS drive !");
-                               }
-                       } else {
-                               *id = 0x0047;
-                               TRACEx1(4, "CMS status: %d", sign);
-                       }
-               } else {
-                       *id = UNKNOWN_VENDOR;
-               }
-               ftape_command(QIC_ENTER_PRIMARY_MODE);
-       }
-       TRACE_EXIT;
-}
-
-void ftape_set_rate_test(int *supported)
-{
-       TRACE_FUN(8, "ftape_set_rate_test");
-       int error;
-       int command;
-       int i;
-       int result;
-       int status;
-
-       /*  Check if the drive does support the select rate command by testing
-        *  all different settings.
-        *  If any one is accepted we assume the command is supported, else not.
-        */
-       *supported = 0;
-       for (i = 0; i < NR_ITEMS(rates); ++i) {
-               result = ftape_command(QIC_SELECT_RATE);
-               if (result >= 0) {
-                       result = ftape_parameter_wait(rates[i].drive_code,
-                                                   1 * SECOND, &status);
-                       if (result >= 0) {
-                               if (status & QIC_STATUS_ERROR) {
-                                       result = ftape_report_error(&error, &command, 0);
-                               } else {
-                                       *supported = 1;         /* did accept a request */
-                               }
-                       }
-               }
-       }
-       TRACEx1(4, "Select Rate command is%s supported",
-               *supported ? "" : " not");
-       TRACE_EXIT;
-}
-
-int ftape_set_data_rate(int new_rate)
-{
-       TRACE_FUN(8, "ftape_set_data_rate");
-       int status;
-       int result;
-       int data_rate;
-       qic_model model;
-       int supported;
-       static int first_time = 1;
-
-       if (first_time) {
-               ftape_set_rate_test(&supported);
-               first_time = 0;
-       }
-       if (rates[new_rate].fdc_code == -1) {
-               TRACEx1(4, "%sb/s data rate not supported by the fdc",
-                       rates[new_rate].text);
-               result = -EINVAL;
-       } else {
-               int error = 0;
-               int command;
-
-               result = ftape_command(QIC_SELECT_RATE);
-               if (result >= 0) {
-                       result = ftape_parameter_wait(rates[new_rate].drive_code,
-                                                   1 * SECOND, &status);
-                       result = ftape_report_raw_drive_status(&status);
-                       if (result >= 0 && (status & QIC_STATUS_ERROR)) {
-                               result = ftape_report_error(&error, &command, 0);
-                               if (result >= 0 && supported &&
-                                   error == 31 && command == QIC_SELECT_RATE) {
-                                       result = -EINVAL;
-                               }
-                       }
-               }
-               if (result >= 0) {
-                       result = ftape_report_configuration(&model, &data_rate,
-                                                   &qic_std, &tape_len);
-                       if (result >= 0 && data_rate != rates[new_rate].drive_code) {
-                               result = -EINVAL;
-                       }
-               }
-               if (result < 0) {
-                       TRACEx1(4, "could not set %sb/s data rate", rates[new_rate].text);
-               } else {
-                       TRACEx2(2, "%s drive @ %sb/s",
-                               (model == prehistoric) ? "prehistoric" :
-                               ((model == pre_qic117c) ? "pre QIC-117C" :
-                                ((model == post_qic117b) ? "post QIC-117B" : "post QIC-117D")),
-                               rates[new_rate].text);
-                       if (tape_len == 0) {
-                               TRACEx1(2, "unknown length QIC-%s tape",
-                                    (qic_std == QIC_TAPE_QIC40) ? "40" :
-                                   ((qic_std == QIC_TAPE_QIC80) ? "80" :
-                                    ((qic_std == QIC_TAPE_QIC3010) ? "3010" : "3020")));
-                       } else {
-                               TRACEx2(2, "%d ft. QIC-%s tape",
-                                       tape_len,
-                                    (qic_std == QIC_TAPE_QIC40) ? "40" :
-                                   ((qic_std == QIC_TAPE_QIC80) ? "80" :
-                                    ((qic_std == QIC_TAPE_QIC3010) ? "3010" : "3020")));
-                       }
-                       /*
-                        *  Set data rate and write precompensation as specified:
-                        *
-                        *            |  QIC-40/80  | QIC-3010/3020
-                        *   rate     |   precomp   |    precomp
-                        *  ----------+-------------+--------------
-                        *  250 Kbps. |   250 ns.   |     0 ns.
-                        *  500 Kbps. |   125 ns.   |     0 ns.
-                        *    1 Mbps. |    42 ns.   |     0 ns.
-                        *    2 Mbps  |      N/A    |     0 ns.
-                        */
-                       if (qic_std == QIC_TAPE_QIC40 || qic_std == QIC_TAPE_QIC80) {
-                               fdc_set_write_precomp(rates[new_rate].precomp);
-                       } else {
-                               fdc_set_write_precomp(0);
-                       }
-                       fdc_set_data_rate(rates[new_rate].fdc_code);
-                       ftape_data_rate = new_rate;     /* store rate set */
-               }
-       }
-       if (result < 0 && result != -EINVAL) {
-               result = -EIO;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-/*      Seek the head to the specified track.
- */
-int ftape_seek_head_to_track(int track)
-{
-       TRACE_FUN(8, "ftape_seek_head_to_track");
-       int status;
-       int result;
-
-       location.track = -1;    /* remains set in case of error */
-       if (track < 0 || track >= tracks_per_tape) {
-               TRACE(-1, "track out of bounds");
-               result = -EINVAL;
-       } else {
-               TRACEx1(5, "seeking track %d", track);
-               result = ftape_command(QIC_SEEK_HEAD_TO_TRACK);
-               if (result < 0) {
-                       TRACE(1, "ftape_command failed");
-               } else {
-                       result = ftape_parameter_wait(track, timeout.head_seek, &status);
-                       if (result < 0) {
-                               TRACE(1, "ftape_parameter_wait failed");
-                       } else {
-                               location.track = track;
-                               might_be_off_track = 0;
-                       }
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_wakeup_drive(wake_up_types method)
-{
-       TRACE_FUN(8, "ftape_wakeup_drive");
-       int result;
-       int status;
-       int motor_on = 0;
-
-       switch (method) {
-       case wake_up_colorado:
-               result = ftape_command(QIC_PHANTOM_SELECT);
-               if (result == 0) {
-                       result = ftape_parameter( /* unit */ 0);
-               }
-               break;
-       case wake_up_mountain:
-               result = ftape_command(QIC_SOFT_SELECT);
-               if (result == 0) {
-                       ftape_sleep(MILLISECOND);       /* NEEDED */
-                       result = ftape_parameter(18);
-               }
-               break;
-       case wake_up_insight:
-               ftape_sleep(100 * MILLISECOND);
-               motor_on = 1;
-               fdc_motor(motor_on);    /* enable is done by motor-on */
-       case no_wake_up:
-               result = 0;
-               break;
-       default:
-               result = -ENODEV;       /* unknown wakeup method */
-       }
-       /*  If wakeup succeeded we shouldn't get an error here..
-        */
-       if (result == 0) {
-               result = ftape_report_raw_drive_status(&status);
-               if (result < 0 && motor_on) {
-                       fdc_motor(0);   /* motor off if failed */
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_put_drive_to_sleep(vendor_struct drive_type)
-{
-       TRACE_FUN(8, "ftape_put_drive_to_sleep");
-       int result;
-
-       switch (drive_type.wake_up) {
-       case wake_up_colorado:
-               result = ftape_command(QIC_PHANTOM_DESELECT);
-               break;
-       case wake_up_mountain:
-               result = ftape_command(QIC_SOFT_DESELECT);
-               break;
-       case wake_up_insight:
-               fdc_motor(0);   /* enable is done by motor-on */
-       case no_wake_up:        /* no wakeup / no sleep ! */
-               result = 0;
-               break;
-       default:
-               result = -ENODEV;       /* unknown wakeup method */
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_reset_drive(void)
-{
-       TRACE_FUN(8, "ftape_reset_drive");
-       int result = 0;
-       int status;
-       int err_code;
-       int err_command;
-       int i;
-
-       /*    We want to re-establish contact with our drive.
-        *    Fire a number of reset commands (single step pulses)
-        *    and pray for success.
-        */
-       for (i = 0; i < 2; ++i) {
-               TRACE(5, "Resetting fdc");
-               fdc_reset();
-               ftape_sleep(10 * MILLISECOND);
-               TRACE(5, "Reset command to drive");
-               result = ftape_command(QIC_RESET);
-               if (result == 0) {
-                       ftape_sleep(1 * SECOND);        /* drive not accessible during 1 second */
-                       TRACE(5, "Re-selecting drive");
-                       /*  Strange, the QIC-117 specs don't mention this but the
-                        *  drive gets deselected after a soft reset !
-                        *  So we need to enable it again.
-                        */
-                       result = ftape_wakeup_drive(drive_type.wake_up);
-                       if (result < 0) {
-                               TRACE(1, "Wakeup failed !");
-                       }
-                       TRACE(5, "Waiting until drive gets ready");
-                       result = ftape_ready_wait(timeout.reset, &status);
-                       if (result == 0 && status & QIC_STATUS_ERROR) {
-                               result = ftape_report_error(&err_code, &err_command, 1);
-                               if (result == 0 && err_code == 27) {
-                                       /* Okay, drive saw reset command and responded as it should
-                                        */
-                                       break;
-                               } else {
-                                       result = -EIO;
-                               }
-                       } else {
-                               result = -EIO;
-                       }
-               }
-               if (current->signal & _DONT_BLOCK) {
-                       TRACE(1, "aborted by non-blockable signal");
-                       result = -EINTR;
-                       break;  /* exit on signal */
-               }
-       }
-       if (result != 0) {
-               TRACE(1, "General failure to reset tape drive");
-       } else {
-               /*  Restore correct settings
-                */
-               ftape_set_data_rate(ftape_data_rate);   /* keep original rate */
-       }
-       TRACE_EXIT;
-       return result;
-}
diff --git a/drivers/char/ftape/ftape-io.h b/drivers/char/ftape/ftape-io.h
deleted file mode 100644 (file)
index 5e10512..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-#ifndef _FTAPE_IO_H
-#define _FTAPE_IO_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-io.h,v $
- $Author: bas $
- *
- $Revision: 1.36 $
- $Date: 1995/05/06 16:11:53 $
- $State: Beta $
- *
- *      This file contains definitions for the glue part
- *      of the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include "vendors.h"
-
-typedef struct {
-       unsigned seek;
-       unsigned reset;
-       unsigned rewind;
-       unsigned head_seek;
-       unsigned stop;
-       unsigned pause;
-} timeout_table;
-
-/*
- *      ftape-io.c defined global vars.
- */
-extern timeout_table timeout;
-extern int qic_std;
-extern int tape_len;
-extern volatile int current_command;
-extern const struct qic117_command_table qic117_cmds[];
-extern int might_be_off_track;
-
-/*
- *      ftape-io.c defined global functions.
- */
-extern void udelay(int usecs);
-extern int udelay_calibrate(void);
-extern void ftape_sleep(unsigned int time);
-extern void ftape_report_vendor_id(unsigned int *id);
-extern int ftape_command(int command);
-extern int ftape_command_wait(int command, int timeout, int *status);
-extern int ftape_report_drive_status(int *status);
-extern int ftape_report_raw_drive_status(int *status);
-extern int ftape_report_status(int *status);
-extern int ftape_interrupt_wait(int time);
-extern int ftape_ready_wait(int timeout, int *status);
-extern int ftape_seek_head_to_track(int track);
-extern int ftape_parameter(int command);
-extern int ftape_in_error_state(int status);
-extern int ftape_set_data_rate(int rate);
-extern int ftape_report_error(int *error, int *command, int report);
-extern int ftape_reset_drive(void);
-extern int ftape_put_drive_to_sleep(vendor_struct drive_type);
-extern int ftape_wakeup_drive(wake_up_types method);
-
-#endif
diff --git a/drivers/char/ftape/ftape-read.c b/drivers/char/ftape/ftape-read.c
deleted file mode 100644 (file)
index 23fdab9..0000000
+++ /dev/null
@@ -1,677 +0,0 @@
-/*
- *      Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-read.c,v $
- $Author: bas $
- *
- $Revision: 1.30 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- *      This file contains the reading code
- *      for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-
-#include "tracing.h"
-#include "ftape-read.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-write.h"
-#include "ftape-eof.h"
-#include "ecc.h"
-#include "ftape-bsm.h"
-
-/*      Global vars.
- */
-
-/*      Local vars.
- */
-int buf_pos_rd = 0;
-int buf_len_rd = 0;
-
-void ftape_zap_read_buffers(void)
-{
-       int i;
-
-       for (i = 0; i < NR_BUFFERS; ++i) {
-               /*
-                * changed to "fit" with dynamic allocation of tape_buffer. --khp
-                */
-               buffer[i].address = tape_buffer[i];
-               buffer[i].status = waiting;
-               buffer[i].bytes = 0;
-               buffer[i].skip = 0;
-               buffer[i].retry = 0;
-       }
-       buf_len_rd = 0;
-       buf_pos_rd = 0;
-       eof_mark = 0;
-       ftape_state = idle;
-}
-
-static unsigned long convert_sector_map(buffer_struct * buff)
-{
-       TRACE_FUN(8, "convert_sector_map");
-       int i = 0;
-       unsigned long bad_map = get_bad_sector_entry(buff->segment_id);
-       unsigned long src_map = buff->soft_error_map | buff->hard_error_map;
-       unsigned long dst_map = 0;
-
-       if (bad_map || src_map) {
-               TRACEx1(5, "bad_map = 0x%08lx", bad_map);
-               TRACEx1(5, "src_map = 0x%08lx", src_map);
-       }
-       while (bad_map) {
-               while ((bad_map & 1) == 0) {
-                       if (src_map & 1) {
-                               dst_map |= (1 << i);
-                       }
-                       src_map >>= 1;
-                       bad_map >>= 1;
-                       ++i;
-               }
-               /* (bad_map & 1) == 1 */
-               src_map >>= 1;
-               bad_map >>= 1;
-       }
-       if (src_map) {
-               dst_map |= (src_map << i);
-       }
-       if (dst_map) {
-               TRACEx1(5, "dst_map = 0x%08lx", dst_map);
-       }
-       TRACE_EXIT;
-       return dst_map;
-}
-
-int correct_and_copy(unsigned int tail, byte * destination)
-{
-       TRACE_FUN(8, "correct_and_copy");
-       struct memory_segment mseg;
-       int result;
-       BAD_SECTOR read_bad;
-
-       mseg.read_bad = convert_sector_map(&buffer[tail]);
-       mseg.marked_bad = 0;    /* not used... */
-       mseg.blocks = buffer[tail].bytes / SECTOR_SIZE;
-       mseg.data = buffer[tail].address;
-       /*    If there are no data sectors we can skip this segment.
-        */
-       if (mseg.blocks <= 3) {
-               TRACE(4, "empty segment");
-               TRACE_EXIT;
-               return 0;
-       }
-       read_bad = mseg.read_bad;
-       history.crc_errors += count_ones(read_bad);
-       result = ecc_correct_data(&mseg);
-       if (read_bad != 0 || mseg.corrected != 0) {
-               TRACElx(4, "crc error map:", read_bad);
-               TRACElx(4, "corrected map:", mseg.corrected);
-               history.corrected += count_ones(mseg.corrected);
-       }
-       if (result == ECC_CORRECTED || result == ECC_OK) {
-               if (result == ECC_CORRECTED) {
-                       TRACEi(3, "ecc corrected segment:", buffer[tail].segment_id);
-               }
-               memcpy(destination, mseg.data, (mseg.blocks - 3) * SECTOR_SIZE);
-               if ((read_bad ^ mseg.corrected) & mseg.corrected) {
-                       /* sectors corrected without crc errors set */
-                       history.crc_failures++;
-               }
-               TRACE_EXIT;
-               return (mseg.blocks - 3) * SECTOR_SIZE;
-       } else {
-               TRACEi(1, "ecc failure on segment", buffer[tail].segment_id);
-               history.ecc_failures++;
-               TRACE_EXIT;
-               return -EAGAIN; /* should retry */
-       }
-       TRACE_EXIT;
-       return 0;
-}
-
-/*      Read given segment into buffer at address.
- */
-int read_segment(unsigned segment_id, byte * address, int *eof_mark,
-                int read_ahead)
-{
-       TRACE_FUN(5, "read_segment");
-       int read_done = 0;
-       int result = 0;
-       int bytes_read = 0;
-       int retry = 0;
-
-       TRACEi(5, "segment_id =", segment_id);
-       if (ftape_state != reading) {
-               if (ftape_state == writing) {
-                       ftape_flush_buffers();  /* flush write buffer */
-                       TRACE(5, "calling ftape_abort_operation");
-                       result = ftape_abort_operation();
-                       if (result < 0) {
-                               TRACE(1, "ftape_abort_operation failed");
-                               TRACE_EXIT;
-                               return -EIO;
-                       }
-               } else {
-                       /* clear remaining read buffers */
-                       ftape_zap_read_buffers();
-               }
-               ftape_state = reading;
-       }
-       if (segment_id >= segments_per_track * tracks_per_tape) {
-               TRACE(5, "reading past end of tape");
-               TRACE_EXIT;
-               return -ENOSPC;
-       }
-       for (;;) {
-               /*    Search all full buffers for the first matching the wanted segment.
-                *    Clear other buffers on the fly.
-                */
-               while (!read_done && buffer[tail].status == done) {
-                       if (buffer[tail].segment_id == segment_id) {
-                               unsigned eof_sector;
-                               unsigned sector_count = 0;
-                               unsigned long bsm = get_bad_sector_entry(segment_id);
-                               int i;
-
-                               /*        If out buffer is already full, return its contents.
-                                */
-                               if (buffer[tail].deleted) {
-                                       TRACEi(5, "found segment in cache :", segment_id);
-                                       TRACE_EXIT;
-                                       /*  Return a value that read_header_segment understands.
-                                        *  As this should only occur when searching for the header
-                                        *  segments it shouldn't be misinterpreted elsewhere.
-                                        */
-                                       return 0;
-                               }
-                               TRACEi(5, "found segment in cache :", segment_id);
-                               eof_sector = check_for_eof(segment_id);
-                               if (eof_sector > 0) {
-                                       TRACEi(5, "end of file mark in sector:", eof_sector);
-                                       for (i = 1; i < eof_sector; ++i) {
-                                               if ((bsm & 1) == 0) {
-                                                       ++sector_count;
-                                               }
-                                               bsm >>= 1;
-                                       }
-                                       *eof_mark = 1;
-                               }
-                               if (eof_sector != 1) {  /* not found or gt 1 */
-                                       result = correct_and_copy(tail, address);
-                                       TRACEi(5, "segment contains (bytes) :", result);
-                                       if (result < 0) {
-                                               if (result != -EAGAIN) {
-                                                       TRACE_EXIT;
-                                                       return result;
-                                               }
-                                               /* keep read_done == 0, will trigger ftape_abort_operation
-                                                * because reading wrong segment.
-                                                */
-                                               TRACE(1, "ecc failed, retry");
-                                               ++retry;
-                                       } else {
-                                               read_done = 1;
-                                       }
-                               } else {
-                                       read_done = 1;
-                               }
-                               if (eof_sector > 0) {
-                                       bytes_read = sector_count * SECTOR_SIZE;
-                                       TRACEi(5, "partial read count:", bytes_read);
-                               } else {
-                                       bytes_read = result;
-                               }
-                       } else {
-                               TRACEi(5, "zapping segment in cache :", buffer[tail].segment_id);
-                       }
-                       buffer[tail].status = waiting;
-                       next_buffer(&tail);
-               }
-               if (!read_done && buffer[tail].status == reading) {
-                       if (buffer[tail].segment_id == segment_id) {
-                               int result = wait_segment(reading);
-                               if (result < 0) {
-                                       if (result == -EINTR) {
-                                               TRACE_EXIT;
-                                               return result;
-                                       }
-                                       TRACE(1, "wait_segment failed while reading");
-                                       ftape_abort_operation();
-                               }
-                       } else {
-                               /*        We're reading the wrong segment, stop runner.
-                                */
-                               ftape_abort_operation();
-                       }
-               }
-               /*    if just passed the last segment on a track, wait for BOT or EOT mark.
-                */
-               if (runner_status == logical_eot) {
-                       int status;
-                       result = ftape_ready_wait(timeout.seek, &status);
-                       if (result < 0) {
-                               TRACE(1, "ftape_ready_wait waiting for eot/bot failed");
-                       }
-                       if ((status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) {
-                               TRACE(1, "eot/bot not reached");
-                       }
-                       runner_status = end_of_tape;
-               }
-               /*    should runner stop ?
-                */
-               if (runner_status == aborting || runner_status == buffer_overrun ||
-                   runner_status == end_of_tape) {
-                       if (runner_status != end_of_tape &&
-                        !(runner_status == aborting && !tape_running)) {
-                               ftape_dumb_stop();
-                       }
-                       if (runner_status == aborting) {
-                               if (buffer[head].status == reading || buffer[head].status == error) {
-                                       if (buffer[head].status == error) {
-                                               history.defects += count_ones(buffer[head].hard_error_map);
-                                       }
-                                       buffer[head].status = waiting;
-                               }
-                       }
-                       runner_status = idle;   /* aborted ? */
-               }
-               /*    If segment to read is empty, do not start runner for it,
-                *    but wait for next read call.
-                */
-               if (get_bad_sector_entry(segment_id) == EMPTY_SEGMENT) {
-                       bytes_read = 0;         /* flag empty segment */
-                       read_done = 1;
-               }
-               /*  Allow escape from this loop on signal !
-                */
-               if (current->signal & _DONT_BLOCK) {
-                       TRACE(2, "interrupted by non-blockable signal");
-                       TRACE_EXIT;
-                       return -EINTR;
-               }
-               /*    If we got a segment: quit, or else retry up to limit.
-                */
-               if (read_done) {
-                       break;
-               }
-               if (retry > RETRIES_ON_ECC_ERROR) {
-                       history.defects++;
-                       TRACE(1, "too many retries on ecc failure");
-                       TRACE_EXIT;
-                       return -ENODATA;
-               }
-               /*    Now at least one buffer is empty !
-                *    Restart runner & tape if needed.
-                */
-               TRACEx3(8, "head: %d, tail: %d, runner_status: %d",
-                       head, tail, runner_status);
-               TRACEx2(8, "buffer[].status, [head]: %d, [tail]: %d",
-                       buffer[head].status, buffer[tail].status);
-               if (buffer[tail].status == waiting) {
-                       setup_new_segment(&buffer[head], segment_id, -1);
-                       if (!read_ahead) {
-                               buffer[head].next_segment = 0;  /* disable read-ahead */
-                       }
-                       calc_next_cluster(&buffer[head]);
-                       if (runner_status == idle) {
-                               result = ftape_start_tape(segment_id,
-                                            buffer[head].sector_offset);
-                               if (result < 0) {
-                                       TRACEx1(1, "Error: segment %d unreachable", segment_id);
-                                       TRACE_EXIT;
-                                       return result;
-                               }
-                               runner_status = running;
-                       }
-                       buffer[head].status = reading;
-                       setup_fdc_and_dma(&buffer[head], FDC_READ);
-               }
-       }
-       if (read_done) {
-               TRACE_EXIT;
-               return bytes_read;
-       } else {
-               TRACE(1, "too many retries");
-               TRACE_EXIT;
-               return -EIO;
-       }
-}
-
-int read_header_segment(byte * address)
-{
-       TRACE_FUN(5, "read_header_segment");
-       int i;
-       int result;
-       int header_segment = -1;
-       unsigned int max_floppy_side;
-       unsigned int max_floppy_track;
-       unsigned int max_floppy_sector;
-       int first_failed = 0;
-       int status;
-       int new_tape_len;
-
-       result = ftape_report_drive_status(&status);
-       if (result < 0) {
-               TRACE(1, "error: error_status or report failure");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       TRACE(5, "reading...");
-       ftape_last_segment.id = 68;     /* will allow us to read the header ! */
-       /*  We're looking for the first header segment.
-        *  A header segment cannot contain bad sectors, therefor at the
-        *  tape start, segments with bad sectors are (according to QIC-40/80)
-        *  written with deleted data marks and must be skipped.
-        */
-       used_header_segment = -1;
-       result = 0;
-       for (header_segment = 0;
-            header_segment < ftape_last_segment.id && result == 0;
-            ++header_segment) {
-               /*  Set no read-ahead, the isr will force read-ahead whenever
-                *  it encounters deleted data !
-                */
-               result = read_segment(header_segment, address, &status, 0);
-               if (result < 0 && !first_failed) {
-                       TRACE(1, "header segment damaged, trying backup");
-                       first_failed = 1;
-                       result = 0;     /* force read of next (backup) segment */
-               }
-       }
-       if (result < 0 || header_segment >= ftape_last_segment.id) {
-               TRACE(1, "no readable header segment found");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       result = ftape_abort_operation();
-       if (result < 0) {
-               TRACE(1, "ftape_abort_operation failed");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       if (GET4(address, 0) != 0xaa55aa55) {
-               TRACE(1, "wrong signature in header segment");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       header_segment_1 = GET2(address, 6);
-       header_segment_2 = GET2(address, 8);
-       TRACEx2(2, "header segments are %d and %d",
-               header_segment_1, header_segment_2);
-       used_header_segment = (first_failed) ? header_segment_2 : header_segment_1;
-
-       /*    Verify tape parameters...
-        *    QIC-40/80 spec:                 tape_parameters:
-        *
-        *    segments-per-track              segments_per_track
-        *    tracks-per-cartridge            tracks_per_tape
-        *    max-floppy-side                 (segments_per_track *
-        *                                    tracks_per_tape - 1) /
-        *                                    segments_per_head
-        *    max-floppy-track                segments_per_head /
-        *                                    segments_per_cylinder - 1
-        *    max-floppy-sector               segments_per_cylinder *
-        *                                    SECTORS_PER_SEGMENT
-        */
-       format_code = (format_type) * (address + 4);
-       segments_per_track = GET2(address, 24);
-       tracks_per_tape = *(address + 26);
-       max_floppy_side = *(address + 27);
-       max_floppy_track = *(address + 28);
-       max_floppy_sector = *(address + 29);
-       TRACEx6(4, "(fmt/spt/tpc/fhm/ftm/fsm) = %d/%d/%d/%d/%d/%d",
-               format_code, segments_per_track, tracks_per_tape,
-               max_floppy_side, max_floppy_track, max_floppy_sector);
-       new_tape_len = tape_len;
-       switch (format_code) {
-       case fmt_425ft:
-               new_tape_len = 425;
-               break;
-       case fmt_normal:
-               if (tape_len == 0) {    /* otherwise 307 ft */
-                       new_tape_len = 205;
-               }
-               break;
-       case fmt_1100ft:
-               new_tape_len = 1100;
-               break;
-       case fmt_wide:{
-                       int segments_per_1000_inch = 1;         /* non-zero default for switch */
-                       switch (qic_std) {
-                       case QIC_TAPE_QIC40:
-                               segments_per_1000_inch = 332;
-                               break;
-                       case QIC_TAPE_QIC80:
-                               segments_per_1000_inch = 488;
-                               break;
-                       case QIC_TAPE_QIC3010:
-                               segments_per_1000_inch = 730;
-                               break;
-                       case QIC_TAPE_QIC3020:
-                               segments_per_1000_inch = 1430;
-                               break;
-                       }
-                       new_tape_len = (1000 * segments_per_track +
-                                       (segments_per_1000_inch - 1)) / segments_per_1000_inch;
-                       break;
-               }
-       default:
-               TRACE(1, "unknown tape format, please report !");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       if (new_tape_len != tape_len) {
-               tape_len = new_tape_len;
-               TRACEx1(1, "calculated tape length is %d ft", tape_len);
-               ftape_calc_timeouts();
-       }
-       if (segments_per_track == 0 && tracks_per_tape == 0 &&
-           max_floppy_side == 0 && max_floppy_track == 0 &&
-           max_floppy_sector == 0) {
-               /*  QIC-40 Rev E and earlier has no values in the header.
-                */
-               segments_per_track = 68;
-               tracks_per_tape = 20;
-               max_floppy_side = 1;
-               max_floppy_track = 169;
-               max_floppy_sector = 128;
-       }
-       /*  This test will compensate for the wrong parameter on tapes
-        *  formatted by Conner software.
-        */
-       if (segments_per_track == 150 &&
-           tracks_per_tape == 28 &&
-           max_floppy_side == 7 &&
-           max_floppy_track == 149 &&
-           max_floppy_sector == 128) {
-               TRACE(-1, "the famous CONNER bug: max_floppy_side off by one !");
-               max_floppy_side = 6;
-       }
-       /*  This test will compensate for the wrong parameter on tapes
-        *  formatted by Colorado Windows software.
-        */
-       if (segments_per_track == 150 &&
-           tracks_per_tape == 28 &&
-           max_floppy_side == 6 &&
-           max_floppy_track == 150 &&
-           max_floppy_sector == 128) {
-               TRACE(-1, "the famous Colorado bug: max_floppy_track off by one !");
-               max_floppy_track = 149;
-       }
-       segments_per_head = ((max_floppy_sector / SECTORS_PER_SEGMENT) *
-                            (max_floppy_track + 1));
-       /*
-        *    Verify drive_configuration with tape parameters
-        */
-       if (segments_per_head == 0 || segments_per_cylinder == 0 ||
-         ((segments_per_track * tracks_per_tape - 1) / segments_per_head
-          != max_floppy_side) ||
-           (segments_per_head / segments_per_cylinder - 1 != max_floppy_track) ||
-       (segments_per_cylinder * SECTORS_PER_SEGMENT != max_floppy_sector)
-#ifdef TESTING
-           || (format_code == 4 && (max_floppy_track != 254 || max_floppy_sector != 128))
-#endif
-           ) {
-               TRACE(1, "Tape parameters inconsistency, please report");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       first_data_segment = GET2(address, 10);         /* first data segment */
-       TRACEi(4, "first data segment:", first_data_segment);
-       extract_bad_sector_map(address);
-       /*  Find the highest segment id that allows still one full
-        *  deblock_buffer to be written to tape.
-        */
-       ftape_last_segment.size = 0;
-       for (i = segments_per_track * tracks_per_tape - 1; i >= 0; --i) {
-               int space = SECTORS_PER_SEGMENT - 3 - count_ones(get_bad_sector_entry(i));
-               if (space > 0) {
-                       ftape_last_segment.size += space;       /* sectors free */
-                       ftape_last_segment.free = (ftape_last_segment.size -
-                                  sizeof(deblock_buffer) / SECTOR_SIZE);
-                       if (ftape_last_segment.free >= 0) {
-                               ftape_last_segment.id = i;
-                               TRACEx2(4, "`last' segment is %d, %d Kb",
-                                       ftape_last_segment.id, ftape_last_segment.size);
-                               break;
-                       }
-               }
-       }
-       /* Copy the failed sector log into our local buffer.
-        */
-       if (!ftape_validate_label(&deblock_buffer[30])) {
-               TRACE(-1, "This tape has no `Linux raw format' label,\n"
-                     "***** Use `mt' to erase this tape if you want to use file marks !");
-       } else {
-               extract_file_marks(address);
-       }
-       ftape_reset_position();
-       TRACE_EXIT;
-       return 0;
-}
-
-int _ftape_read(char *buff, int req_len)
-{
-       TRACE_FUN(5, "_ftape_read");
-       int result = 0;
-       int cnt;
-       int to_do = req_len;
-       static int remaining;
-       int bytes_read = 0;
-
-       if (ftape_offline || !formatted || no_tape) {
-               TRACEx3(-1, "offline = %d, formatted = %d, no_tape = %d",
-                       ftape_offline, formatted, no_tape);
-               result = -EIO;
-       } else {
-               history.used |= 1;
-               if (first_data_segment == -1) {
-                       result = read_header_segment(deblock_buffer);
-               }
-       }
-       if (result < 0) {
-               TRACE_EXIT;
-               return result;
-       }
-       /*  As GNU tar doesn't accept partial read counts when the multiple
-        *  volume flag is set, we make sure to return the requested amount
-        *  of data. Except, of course, at the end of the tape or file mark.
-        */
-       while (to_do > 0) {     /* don't return with a partial count ! */
-               /*  If we're reading the `last' segment(s) on tape, make sure we don't
-                *  get more than 29 Kb from it (As it only contains this much).
-                *  This works only for sequential access, so random access should
-                *  stay away from this `last' segment.
-                *  Note: ftape_seg_pos points to the next segment that will be
-                *        read, so it's one too high here!
-                */
-               if (!eof_mark && ftape_seg_pos - 1 >= ftape_last_segment.id) {
-                       TRACEi(5, "remaining of last segment:", remaining);
-                       if (to_do > remaining) {
-                               to_do = remaining;      /* fake a smaller request */
-                               TRACE(5, "clipped request to remaining");
-                       }
-               }
-               while (!eof_mark && buf_len_rd == 0) {
-                       /*  When starting to read the `last' segment, set remaining
-                        */
-                       if (ftape_seg_pos == ftape_last_segment.id) {
-                               remaining = sizeof(deblock_buffer);
-                               TRACEi(5, "remaining set to:", remaining);
-                       }
-                       result = read_segment(ftape_seg_pos, deblock_buffer, &eof_mark, 1);
-                       if (result < 0) {
-                               if (result == -ENODATA) {
-                                       /*  Unable to recover tape data, return error and skip bad spot.
-                                        */
-                                       ++ftape_seg_pos;
-                               }
-                               TRACEx1(4, "read_segment result: %d", result);
-                               TRACE_EXIT;
-                               return result;
-                       }
-                       /*  Allow escape from this loop on signal !
-                        */
-                       if (current->signal & _DONT_BLOCK) {
-                               TRACE(2, "interrupted by non-blockable signal");
-                               TRACE_EXIT;
-                               return -EINTR;
-                       }
-                       buf_pos_rd = 0;
-                       buf_len_rd = result;
-                       ++ftape_seg_pos;
-               }
-               /*  Take as much as we can use
-                */
-               cnt = (buf_len_rd < to_do) ? buf_len_rd : to_do;
-               TRACEi(7, "nr bytes just read:", cnt);
-               if (cnt > 0) {
-                       result = verify_area(VERIFY_WRITE, buff, cnt);
-                       if (result) {
-                               TRACEx1(1, "verify_area failed, exitcode = %d", result);
-                               TRACE_EXIT;
-                               return -EIO;
-                       }
-                       copy_to_user(buff, deblock_buffer + buf_pos_rd, cnt);
-                       buff += cnt;
-                       to_do -= cnt;   /* what's left from req_len */
-                       remaining -= cnt;       /* what remains on this tape */
-                       bytes_read += cnt;      /* what we got so far */
-                       buf_pos_rd += cnt;      /* index in buffer */
-                       buf_len_rd -= cnt;      /* remaining bytes in buffer */
-               }
-               if (eof_mark && buf_len_rd == 0) {      /* nothing left */
-                       TRACE(5, "partial count because of eof mark");
-                       if (bytes_read == 0) {
-                               eof_mark = 0;   /* no need for mark next read */
-                       }
-                       break;
-               }
-       }
-       TRACE_EXIT;
-       return bytes_read;
-}
diff --git a/drivers/char/ftape/ftape-read.h b/drivers/char/ftape/ftape-read.h
deleted file mode 100644 (file)
index aeca359..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef _FTAPE_READ_H
-#define _FTAPE_READ_H
-
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-read.h,v $
- $Author: bas $
- *
- $Revision: 1.13 $
- $Date: 1995/05/10 16:09:36 $
- $State: Beta $
- *
- *      This file contains the definitions for the read functions
- *      for the QIC-117 floppy-tape driver for Linux.
- *
- */
-
-/*      ftape-read.c defined global vars.
- */
-
-/*      ftape-read.c defined global functions.
- */
-extern int _ftape_read(char *buff, int req_len);
-extern int read_header_segment(byte * address);
-extern int read_segment(unsigned segment, byte * address, int *eof_mark,
-                       int read_ahead);
-extern void ftape_zap_read_buffers(void);
-
-#endif                         /* _FTAPE_READ_H */
diff --git a/drivers/char/ftape/ftape-rw.c b/drivers/char/ftape/ftape-rw.c
deleted file mode 100644 (file)
index f1e0c10..0000000
+++ /dev/null
@@ -1,953 +0,0 @@
-/*
- *      Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-rw.c,v $
- $Author: bas $
- *
- $Revision: 1.54 $
- $Date: 1995/05/27 08:55:27 $
- $State: Beta $
- *
- *      This file contains some common code for the segment read and segment
- *      write routines for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/ftape.h>
-
-#include "tracing.h"
-#include "ftape-rw.h"
-#include "fdc-io.h"
-#include "kernel-interface.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-read.h"
-#include "ftape-eof.h"
-#include "ecc.h"
-#include "ftape-bsm.h"
-
-/*      Global vars.
- */
-volatile enum runner_status_enum runner_status = idle;
-byte deblock_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
-byte scratch_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
-buffer_struct buffer[NR_BUFFERS];
-struct wait_queue *wait_intr = NULL;
-volatile int head;
-volatile int tail;             /* not volatile but need same type as head */
-int fdc_setup_error;
-ftape_last_segment_struct ftape_last_segment;
-int header_segment_1 = -1;
-int header_segment_2 = -1;
-int used_header_segment = -1;
-location_record location =
-{-1, 0};
-volatile int tape_running = 0;
-format_type format_code;
-
-/*      Local vars.
- */
-static int overrun_count_offset = 0;
-static int inhibit_correction = 0;
-
-
-/*      Increment cyclic buffer nr.
- */
-buffer_struct *
- next_buffer(volatile int *x)
-{
-       if (++*x >= NR_BUFFERS) {
-               *x = 0;
-       }
-       return &buffer[*x];
-}
-
-int valid_segment_no(unsigned segment)
-{
-       return (segment >= first_data_segment && segment <= ftape_last_segment.id);
-}
-
-/*      Count nr of 1's in pattern.
- */
-int count_ones(unsigned long mask)
-{
-       int bits;
-
-       for (bits = 0; mask != 0; mask >>= 1) {
-               if (mask & 1) {
-                       ++bits;
-               }
-       }
-       return bits;
-}
-
-/*      Calculate Floppy Disk Controller and DMA parameters for a segment.
- *      head:   selects buffer struct in array.
- *      offset: number of physical sectors to skip (including bad ones).
- *      count:  number of physical sectors to handle (including bad ones).
- */
-static int setup_segment(buffer_struct * buff, unsigned int segment_id,
-       unsigned int sector_offset, unsigned int sector_count, int retry)
-{
-       TRACE_FUN(8, "setup_segment");
-       unsigned long offset_mask;
-       unsigned long mask;
-
-       buff->segment_id = segment_id;
-       buff->sector_offset = sector_offset;
-       buff->remaining = sector_count;
-       buff->head = segment_id / segments_per_head;
-       buff->cyl = (segment_id % segments_per_head) / segments_per_cylinder;
-       buff->sect = (segment_id % segments_per_cylinder) * SECTORS_PER_SEGMENT + 1;
-       buff->deleted = 0;
-       offset_mask = (1 << buff->sector_offset) - 1;
-       mask = get_bad_sector_entry(segment_id) & offset_mask;
-       while (mask) {
-               if (mask & 1) {
-                       offset_mask >>= 1;      /* don't count bad sector */
-               }
-               mask >>= 1;
-       }
-       buff->data_offset = count_ones(offset_mask);    /* good sectors to skip */
-       buff->ptr = buff->address + buff->data_offset * SECTOR_SIZE;
-       TRACEx1(5, "data offset = %d sectors", buff->data_offset);
-       if (retry) {
-               buff->soft_error_map &= offset_mask;    /* keep skipped part */
-       } else {
-               buff->hard_error_map = buff->soft_error_map = 0;
-       }
-       buff->bad_sector_map = get_bad_sector_entry(buff->segment_id);
-       if (buff->bad_sector_map != 0) {
-               TRACEx2(4, "segment: %d, bad sector map: %08lx",
-                       buff->segment_id, buff->bad_sector_map);
-       } else {
-               TRACEx1(5, "segment: %d", buff->segment_id);
-       }
-       if (buff->sector_offset > 0) {
-               buff->bad_sector_map >>= buff->sector_offset;
-       }
-       if (buff->sector_offset != 0 || buff->remaining != SECTORS_PER_SEGMENT) {
-               TRACEx2(5, "sector offset = %d, count = %d",
-                       buff->sector_offset, buff->remaining);
-       }
-       /*
-        *    Segments with 3 or less sectors are not written with
-        *    valid data because there is no space left for the ecc.
-        *    The data written is whatever happens to be in the buffer.
-        *    Reading such a segment will return a zero byte-count.
-        *    To allow us to read/write segments with all bad sectors
-        *    we fake one readable sector in the segment. This prevents
-        *    having to handle these segments in a very special way.
-        *    It is not important if the reading of this bad sector
-        *    fails or not (the data is ignored). It is only read to
-        *    keep the driver running.
-        *    The QIC-40/80 spec. has no information on how to handle
-        *    this case, so this is my interpretation.
-        */
-       if (buff->bad_sector_map == EMPTY_SEGMENT) {
-               TRACE(5, "empty segment, fake first sector good");
-               buff->bad_sector_map = FAKE_SEGMENT;
-       }
-       fdc_setup_error = 0;
-       buff->next_segment = segment_id + 1;
-       TRACE_EXIT;
-       return 0;
-}
-
-/*      Calculate Floppy Disk Controller and DMA parameters for a new segment.
- */
-int setup_new_segment(buffer_struct * buff, unsigned int segment_id, int skip)
-{
-       TRACE_FUN(5, "setup_new_segment");
-       int result = 0;
-       static int old_segment_id = -1;
-       static int old_ftape_state = idle;
-       int retry = 0;
-       unsigned offset = 0;
-       int count = SECTORS_PER_SEGMENT;
-
-       TRACEx3(5, "%s segment %d (old = %d)",
-               (ftape_state == reading) ? "reading" : "writing",
-               segment_id, old_segment_id);
-       if (ftape_state != old_ftape_state) {   /* when verifying */
-               old_segment_id = -1;
-               old_ftape_state = ftape_state;
-       }
-       if (segment_id == old_segment_id) {
-               ++buff->retry;
-               ++history.retries;
-               TRACEx1(5, "setting up for retry nr %d", buff->retry);
-               retry = 1;
-               if (skip && buff->skip > 0) {   /* allow skip on retry */
-                       offset = buff->skip;
-                       count -= offset;
-                       TRACEx1(5, "skipping %d sectors", offset);
-               }
-       } else {
-               buff->retry = 0;
-               buff->skip = 0;
-               old_segment_id = segment_id;
-       }
-       result = setup_segment(buff, segment_id, offset, count, retry);
-       TRACE_EXIT;
-       return result;
-}
-
-/*      Determine size of next cluster of good sectors.
- */
-int calc_next_cluster(buffer_struct * buff)
-{
-       /* Skip bad sectors.
-        */
-       while (buff->remaining > 0 && (buff->bad_sector_map & 1) != 0) {
-               buff->bad_sector_map >>= 1;
-               ++buff->sector_offset;
-               --buff->remaining;
-       }
-       /* Find next cluster of good sectors
-        */
-       if (buff->bad_sector_map == 0) {        /* speed up */
-               buff->sector_count = buff->remaining;
-       } else {
-               unsigned long map = buff->bad_sector_map;
-
-               buff->sector_count = 0;
-               while (buff->sector_count < buff->remaining && (map & 1) == 0) {
-                       ++buff->sector_count;
-                       map >>= 1;
-               }
-       }
-       return buff->sector_count;
-}
-
-int check_bot_eot(int status)
-{
-       TRACE_FUN(5, "check_bot_eot");
-
-       if (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) {
-               location.bot = ((location.track & 1) == 0 ?
-                               (status & QIC_STATUS_AT_BOT) :
-                               (status & QIC_STATUS_AT_EOT));
-               location.eot = !location.bot;
-               location.segment = (location.track +
-                       (location.bot ? 0 : 1)) * segments_per_track - 1;
-               location.sector = -1;
-               location.known = 1;
-               TRACEx1(5, "tape at logical %s", location.bot ? "bot" : "eot");
-               TRACEx1(5, "segment = %d", location.segment);
-       } else {
-               location.known = 0;
-       }
-       TRACE_EXIT;
-       return location.known;
-}
-
-/*      Read Id of first sector passing tape head.
- */
-int ftape_read_id(void)
-{
-       TRACE_FUN(8, "ftape_read_id");
-       int result;
-       int status;
-       byte out[2];
-
-       /* Assume tape is running on entry, be able to handle
-        * situation where it stopped or is stopping.
-        */
-       location.known = 0;     /* default is location not known */
-       out[0] = FDC_READID;
-       out[1] = FTAPE_UNIT;
-       result = fdc_command(out, 2);
-       if (result < 0) {
-               TRACE(1, "fdc_command failed");
-       } else {
-               result = fdc_interrupt_wait(20 * SECOND);
-               if (result == 0) {
-                       if (fdc_sect == 0) {
-                               result = ftape_report_drive_status(&status);
-                               if (result == 0) {
-                                       if (status & QIC_STATUS_READY) {
-                                               tape_running = 0;
-                                               TRACE(5, "tape has stopped");
-                                               check_bot_eot(status);
-                                               if (!location.known) {
-                                                       result = -EIO;
-                                               }
-                                       } else {
-                                               /*  If read-id failed because of a hard or soft
-                                                *  error, return an error. Higher level must retry!
-                                                */
-                                               result = -EIO;
-                                       }
-                               }
-                       } else {
-                               location.known = 1;
-                               location.segment = (segments_per_head * fdc_head
-                                       + segments_per_cylinder * fdc_cyl
-                                + (fdc_sect - 1) / SECTORS_PER_SEGMENT);
-                               location.sector = (fdc_sect - 1) % SECTORS_PER_SEGMENT;
-                               location.eot =
-                                   location.bot = 0;
-                       }
-               } else if (result == -ETIME) {
-                       /*  Didn't find id on tape, must be near end: Wait until stopped.
-                        */
-                       result = ftape_ready_wait(FOREVER, &status);
-                       if (result >= 0) {
-                               tape_running = 0;
-                               TRACE(5, "tape has stopped");
-                               check_bot_eot(status);
-                               if (!location.known) {
-                                       result = -EIO;
-                               }
-                       }
-               } else {
-                       /* Interrupted or otherwise failing fdc_interrupt_wait()
-                        */
-                       TRACE(1, "fdc_interrupt_wait failed :(");
-                       result = -EIO;
-               }
-       }
-       if (!location.known) {
-               TRACE(5, "no id found");
-       } else {
-               if (location.sector == 0) {
-                       TRACEx2(5, "passing segment %d/%d", location.segment, location.sector);
-               } else {
-                       TRACEx2(6, "passing segment %d/%d", location.segment, location.sector);
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-static int logical_forward(void)
-{
-       tape_running = 1;
-       return ftape_command(QIC_LOGICAL_FORWARD);
-}
-
-static int stop_tape(int *pstatus)
-{
-       TRACE_FUN(5, "stop_tape");
-       int retry = 0;
-       int result;
-
-       do {
-               result = ftape_command_wait(QIC_STOP_TAPE, timeout.stop, pstatus);
-               if (result == 0) {
-                       if ((*pstatus & QIC_STATUS_READY) == 0) {
-                               result = -EIO;
-                       } else {
-                               tape_running = 0;
-                       }
-               }
-       } while (result < 0 && ++retry <= 3);
-       if (result < 0) {
-               TRACE(1, "failed ! (fatal)");
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_dumb_stop(void)
-{
-       TRACE_FUN(5, "ftape_dumb_stop");
-       int result;
-       int status;
-
-       /* Abort current fdc operation if it's busy (probably read
-        * or write operation pending) with a reset.
-        */
-       result = fdc_ready_wait(100 /* usec */ );
-       if (result < 0) {
-               TRACE(1, "aborting fdc operation");
-               fdc_reset();
-       }
-       /*  Reading id's after the last segment on a track may fail
-        *  but eventually the drive will become ready (logical eot).
-        */
-       result = ftape_report_drive_status(&status);
-       location.known = 0;
-       do {
-               if (result == 0 && status & QIC_STATUS_READY) {
-                       /* Tape is not running any more.
-                        */
-                       TRACE(5, "tape already halted");
-                       check_bot_eot(status);
-                       tape_running = 0;
-               } else if (tape_running) {
-                       /* Tape is (was) still moving.
-                        */
-#ifdef TESTING
-                       ftape_read_id();
-#endif
-                       result = stop_tape(&status);
-               } else {
-                       /* Tape not yet ready but stopped.
-                        */
-                       result = ftape_ready_wait(timeout.pause, &status);
-               }
-       } while (tape_running);
-#ifndef TESTING
-       location.known = 0;
-#endif
-       TRACE_EXIT;
-       return result;
-}
-
-/*      Wait until runner has finished tail buffer.
- */
-int wait_segment(buffer_state_enum state)
-{
-       TRACE_FUN(5, "wait_segment");
-       int result = 0;
-
-       while (buffer[tail].status == state) {
-               /*  First buffer still being worked on, wait up to timeout.
-                */
-               result = fdc_interrupt_wait(50 * SECOND);
-               if (result < 0) {
-                       if (result != -EINTR) {
-                               TRACE(1, "fdc_interrupt_wait failed");
-                               result = -ETIME;
-                       }
-                       break;
-               }
-               if (fdc_setup_error) {
-                       TRACE(1, "setup error");
-                       /* recover... */
-                       result = -EIO;
-                       break;
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-/* forward */ static int seek_forward(int segment_id);
-
-int fast_seek(int count, int reverse)
-{
-       TRACE_FUN(5, "fast_seek");
-       int result = 0;
-       int status;
-
-       if (count > 0) {
-               /*  If positioned at begin or end of tape, fast seeking needs
-                *  special treatment.
-                *  Starting from logical bot needs a (slow) seek to the first
-                *  segment before the high speed seek. Most drives do this
-                *  automatically but some older don't, so we treat them
-                *  all the same.
-                *  Starting from logical eot is even more difficult because
-                *  we cannot (slow) reverse seek to the last segment.
-                *  TO BE IMPLEMENTED.
-                */
-               inhibit_correction = 0;
-               if (location.known &&
-                   ((location.bot && !reverse) ||
-                    (location.eot && reverse))) {
-                       if (!reverse) {
-                               /*  (slow) skip to first segment on a track
-                                */
-                               seek_forward(location.track * segments_per_track);
-                               --count;
-                       } else {
-                               /*  When seeking backwards from end-of-tape the number
-                                *  of erased gaps found seems to be higher than expected.
-                                *  Therefor the drive must skip some more segments than
-                                *  calculated, but we don't know how many.
-                                *  Thus we will prevent the re-calculation of offset
-                                *  and overshoot when seeking backwards.
-                                */
-                               inhibit_correction = 1;
-                               count += 3;     /* best guess */
-                       }
-               }
-       } else {
-               TRACEx1(5, "warning: zero or negative count: %d", count);
-       }
-       if (count > 0) {
-               int i;
-               int nibbles = count > 255 ? 3 : 2;
-
-               if (count > 4095) {
-                       TRACE(4, "skipping clipped at 4095 segment");
-                       count = 4095;
-               }
-               /* Issue this tape command first. */
-               if (!reverse) {
-                       TRACEx1(4, "skipping %d segment(s)", count);
-                       result = ftape_command(nibbles == 3 ?
-                          QIC_SKIP_EXTENDED_FORWARD : QIC_SKIP_FORWARD);
-               } else {
-                       TRACEx1(4, "backing up %d segment(s)", count);
-                       result = ftape_command(nibbles == 3 ?
-                          QIC_SKIP_EXTENDED_REVERSE : QIC_SKIP_REVERSE);
-               }
-               if (result < 0) {
-                       TRACE(4, "Skip command failed");
-               } else {
-                       --count;        /* 0 means one gap etc. */
-                       for (i = 0; i < nibbles; ++i) {
-                               if (result >= 0) {
-                                       result = ftape_parameter(count & 15);
-                                       count /= 16;
-                               }
-                       }
-                       result = ftape_ready_wait(timeout.rewind, &status);
-                       if (result >= 0) {
-                               tape_running = 0;
-                       }
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-static int validate(int id)
-{
-       /*  Check to see if position found is off-track as reported once.
-        *  Because all tracks in one direction lie next to each other,
-        *  if off-track the error will be approximately 2 * segments_per_track.
-        */
-       if (location.track == -1) {
-               return 1;       /* unforeseen situation, don't generate error */
-       } else {
-               /*  Use margin of segments_per_track on both sides because ftape
-                *  needs some margin and the error we're looking for is much larger !
-                */
-               int lo = (location.track - 1) * segments_per_track;
-               int hi = (location.track + 2) * segments_per_track;
-
-               return (id >= lo && id < hi);
-       }
-}
-
-static int seek_forward(int segment_id)
-{
-       TRACE_FUN(5, "seek_forward");
-       int failures = 0;
-       int result = 0;
-       int count;
-       static int margin = 1;  /* fixed: stop this before target */
-       static int overshoot = 1;
-       static int min_count = 8;
-       int expected = -1;
-       int target = segment_id - margin;
-       int fast_seeking;
-
-       if (!location.known) {
-               TRACE(1, "fatal: cannot seek from unknown location");
-               result = -EIO;
-       } else if (!validate(segment_id)) {
-               TRACE(1, "fatal: head off track (bad hardware?)");
-               ftape_sleep(1 * SECOND);
-               ftape_failure = 1;
-               result = -EIO;
-       } else {
-               int prev_segment = location.segment;
-
-               TRACEx4(4, "from %d/%d to %d/0 - %d", location.segment,
-                       location.sector, segment_id, margin);
-               count = target - location.segment - overshoot;
-               fast_seeking = (count > min_count + (location.bot ? 1 : 0));
-               if (fast_seeking) {
-                       TRACEx1(5, "fast skipping %d segments", count);
-                       expected = segment_id - margin;
-                       fast_seek(count, 0);
-               }
-               if (!tape_running) {
-                       logical_forward();
-               }
-               while (location.segment < segment_id) {
-                       /*  This requires at least one sector in a (bad) segment to
-                        *  have a valid and readable sector id !
-                        *  It looks like this is not guaranteed, so we must try
-                        *  to find a way to skip an EMPTY_SEGMENT. !!! FIXME !!!
-                        */
-                       if (ftape_read_id() < 0 || !location.known) {
-                               location.known = 0;
-                               if (!tape_running || ++failures > SECTORS_PER_SEGMENT ||
-                                   (current->signal & _DONT_BLOCK)) {
-                                       TRACE(1, "read_id failed completely");
-                                       result = -EIO;
-                                       break;
-                               } else {
-                                       TRACEx1(5, "read_id failed, retry (%d)", failures);
-                               }
-                       } else if (fast_seeking) {
-                               TRACEx4(4, "ended at %d/%d (%d,%d)", location.segment,
-                                       location.sector, overshoot, inhibit_correction);
-                               if (!inhibit_correction &&
-                                   (location.segment < expected ||
-                                location.segment > expected + margin)) {
-                                       int error = location.segment - expected;
-                                       TRACEx2(4, "adjusting overshoot from %d to %d",
-                                          overshoot, overshoot + error);
-                                       overshoot += error;
-                                       /*  All overshoots have the same direction, so it should
-                                        *  never become negative, but who knows.
-                                        */
-                                       if (overshoot < -5 || overshoot > 10) {
-                                               if (overshoot < 0) {
-                                                       overshoot = -5;         /* keep sane value */
-                                               } else {
-                                                       overshoot = 10;         /* keep sane value */
-                                               }
-                                               TRACEx1(4, "clipped overshoot to %d", overshoot);
-                                       }
-                               }
-                               fast_seeking = 0;
-                       }
-                       if (location.known) {
-                               if (location.segment > prev_segment + 1) {
-                                       TRACEx1(4, "missed segment %d while skipping", prev_segment + 1);
-                               }
-                               prev_segment = location.segment;
-                       }
-               }
-               if (location.segment > segment_id) {
-                       TRACEx2(4, "failed: skip ended at segment %d/%d",
-                               location.segment, location.sector);
-                       result = -EIO;
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-static int skip_reverse(int segment_id, int *pstatus)
-{
-       TRACE_FUN(5, "skip_reverse");
-       int result = 0;
-       int failures = 0;
-       static int overshoot = 1;
-       static int min_rewind = 2;      /* 1 + overshoot */
-       static const int margin = 1;    /* stop this before target */
-       int expected = 0;
-       int count;
-       int short_seek;
-       int target = segment_id - margin;
-
-       if (location.known && !validate(segment_id)) {
-               TRACE(1, "fatal: head off track (bad hardware?)");
-               ftape_sleep(1 * SECOND);
-               ftape_failure = 1;
-               result = -EIO;
-       } else
-               do {
-                       if (!location.known) {
-                               TRACE(-1, "warning: location not known");
-                       }
-                       TRACEx4(4, "from %d/%d to %d/0 - %d",
-                               location.segment, location.sector, segment_id, margin);
-                       /*  min_rewind == 1 + overshoot_when_doing_minimum_rewind
-                        *  overshoot  == overshoot_when_doing_larger_rewind
-                        *  Initially min_rewind == 1 + overshoot, optimization
-                        *  of both values will be done separately.
-                        *  overshoot and min_rewind can be negative as both are
-                        *  sums of three components:
-                        *  any_overshoot == rewind_overshoot - stop_overshoot - start_overshoot
-                        */
-                       if (location.segment - target - (min_rewind - 1) < 1) {
-                               short_seek = 1;
-                       } else {
-                               count = location.segment - target - overshoot;
-                               short_seek = (count < 1);
-                       }
-                       if (short_seek) {
-                               count = 1;      /* do shortest rewind */
-                               expected = location.segment - min_rewind;
-                               if (expected / segments_per_track != location.track) {
-                                       expected = location.track * segments_per_track;
-                               }
-                       } else {
-                               expected = target;
-                       }
-                       fast_seek(count, 1);
-                       logical_forward();
-                       result = ftape_read_id();
-                       if (result == 0 && location.known) {
-                               TRACEx5(4, "ended at %d/%d (%d,%d,%d)", location.segment,
-                                       location.sector, min_rewind, overshoot, inhibit_correction);
-                               if (!inhibit_correction &&
-                                   (location.segment < expected ||
-                                location.segment > expected + margin)) {
-                                       int error = expected - location.segment;
-                                       if (short_seek) {
-                                               TRACEx2(4, "adjusting min_rewind from %d to %d",
-                                                       min_rewind, min_rewind + error);
-                                               min_rewind += error;
-                                               if (min_rewind < -5) {  /* is this right ? FIXME ! */
-                                                       min_rewind = -5;        /* keep sane value */
-                                                       TRACEx1(4, "clipped min_rewind to %d", min_rewind);
-                                               }
-                                       } else {
-                                               TRACEx2(4, "adjusting overshoot from %d to %d",
-                                                       overshoot, overshoot + error);
-                                               overshoot += error;
-                                               if (overshoot < -5 || overshoot > 10) {
-                                                       if (overshoot < 0) {
-                                                               overshoot = -5;         /* keep sane value */
-                                                       } else {
-                                                               overshoot = 10;         /* keep sane value */
-                                                       }
-                                                       TRACEx1(4, "clipped overshoot to %d", overshoot);
-                                               }
-                                       }
-                               }
-                       } else {
-                               if ((!tape_running && !location.known) ||
-                                   ++failures > SECTORS_PER_SEGMENT) {
-                                       TRACE(1, "read_id failed completely");
-                                       result = -EIO;
-                                       break;
-                               } else {
-                                       TRACEx1(5, "ftape_read_id failed, retry (%d)", failures);
-                               }
-                               result = ftape_report_drive_status(pstatus);
-                               if (result < 0) {
-                                       TRACEi(1, "ftape_report_drive_status failed with code", result);
-                                       break;
-                               }
-                       }
-               } while (location.segment > segment_id &&
-                        (current->signal & _DONT_BLOCK) == 0);
-       if (location.known) {
-               TRACEx2(4, "current location: %d/%d", location.segment, location.sector);
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-static int determine_position(void)
-{
-       TRACE_FUN(5, "determine_position");
-       int retry = 0;
-       int fatal = 0;
-       int status;
-       int result;
-
-       if (!tape_running) {
-               /*  This should only happen if tape is stopped by isr.
-                */
-               TRACE(5, "waiting for tape stop");
-               result = ftape_ready_wait(timeout.pause, &status);
-               if (result < 0) {
-                       TRACE(5, "drive still running (fatal)");
-                       tape_running = 1;       /* ? */
-               }
-       } else {
-               ftape_report_drive_status(&status);
-       }
-       if (status & QIC_STATUS_READY) {
-               /*  Drive must be ready to check error state !
-                */
-               TRACE(5, "drive is ready");
-               if (status & QIC_STATUS_ERROR) {
-                       int error;
-                       int command;
-
-                       /*  Report and clear error state, try to continue.
-                        */
-                       TRACE(5, "error status set");
-                       ftape_report_error(&error, &command, 1);
-                       ftape_ready_wait(timeout.reset, &status);
-                       tape_running = 0;       /* ? */
-               }
-               if (check_bot_eot(status)) {
-                       if (location.bot) {
-                               if ((status & QIC_STATUS_READY) == 0) {
-                                       /* tape moving away from bot/eot, let's see if we
-                                        * can catch up with the first segment on this track.
-                                        */
-                               } else {
-                                       TRACE(5, "start tape from logical bot");
-                                       logical_forward();      /* start moving */
-                               }
-                       } else {
-                               if ((status & QIC_STATUS_READY) == 0) {
-                                       TRACE(4, "waiting for logical end of track");
-                                       result = ftape_ready_wait(timeout.reset, &status);
-                                       /* error handling needed ? */
-                               } else {
-                                       TRACE(4, "tape at logical end of track");
-                               }
-                       }
-               } else {
-                       TRACE(5, "start tape");
-                       logical_forward();      /* start moving */
-                       location.known = 0;     /* not cleared by logical forward ! */
-               }
-       }
-       if (!location.known) {
-               /* tape should be moving now, start reading id's
-                */
-               TRACE(5, "location unknown");
-               do {
-                       result = ftape_read_id();
-                       if (result < 0) {
-                               /*  read-id somehow failed, tape may have reached end
-                                *  or some other error happened.
-                                */
-                               TRACE(5, "read-id failed");
-                               ftape_report_drive_status(&status);
-                               if (status & QIC_STATUS_READY) {
-                                       tape_running = 0;
-                                       TRACEx1(4, "tape stopped for unknown reason ! status = 0x%02x",
-                                               status);
-                                       if (status & QIC_STATUS_ERROR) {
-                                               fatal = 1;
-                                       } else {
-                                               if (check_bot_eot(status)) {
-                                                       result = 0;
-                                               } else {
-                                                       fatal = 1;      /* oops, tape stopped but not at end ! */
-                                               }
-                                       }
-                               }
-                               result = -EIO;
-                       }
-               } while (result < 0 && !fatal && ++retry < SECTORS_PER_SEGMENT);
-       } else {
-               result = 0;
-       }
-       TRACEx1(5, "tape is positioned at segment %d", location.segment);
-       TRACE_EXIT;
-       return result;
-}
-
-/*      Get the tape running and position it just before the
- *      requested segment.
- *      Seek tape-track and reposition as needed.
- */
-int ftape_start_tape(int segment_id, int sector_offset)
-{
-       TRACE_FUN(5, "ftape_start_tape");
-       int track = segment_id / segments_per_track;
-       int result = -EIO;
-       int status;
-       static int last_segment = -1;
-       static int bad_bus_timing = 0;
-       /* number of segments passing the head between starting the tape
-        * and being able to access the first sector.
-        */
-       static int start_offset = 1;
-       int retry = 0;
-
-       /* If sector_offset > 0, seek into wanted segment instead of
-        * into previous.
-        * This allows error recovery if a part of the segment is bad
-        * (erased) causing the tape drive to generate an index pulse
-        * thus causing a no-data error before the requested sector
-        * is reached.
-        */
-       tape_running = 0;
-       TRACEx3(4, "target segment: %d/%d%s", segment_id, sector_offset,
-               buffer[head].retry > 0 ? " retry" : "");
-       if (buffer[head].retry > 0) {   /* this is a retry */
-               if (!bad_bus_timing && ftape_data_rate == 1 &&
-                   history.overrun_errors - overrun_count_offset >= 8) {
-                       ftape_set_data_rate(ftape_data_rate + 1);
-                       bad_bus_timing = 1;
-                       TRACE(2, "reduced datarate because of excessive overrun errors");
-               }
-       }
-       last_segment = segment_id;
-       if (location.track != track || (might_be_off_track &&
-                                       buffer[head].retry == 0)) {
-               /* current track unknown or not equal to destination
-                */
-               ftape_ready_wait(timeout.seek, &status);
-               ftape_seek_head_to_track(track);
-               overrun_count_offset = history.overrun_errors;
-       }
-       do {
-               if (!location.known) {
-                       determine_position();
-               }
-               /*  Check if we are able to catch the requested segment in time.
-                */
-               if (location.known && location.segment >= segment_id -
-                   ((tape_running || location.bot) ? 0 : start_offset)) {
-                       /*  Too far ahead (in or past target segment).
-                        */
-                       if (tape_running) {
-                               result = stop_tape(&status);
-                               if (result < 0) {
-                                       TRACEi(1, "stop tape failed with code", result);
-                                       break;
-                               }
-                               TRACE(5, "tape stopped");
-                               tape_running = 0;
-                       }
-                       TRACE(5, "repositioning");
-                       ++history.rewinds;
-                       if (segment_id % segments_per_track < start_offset) {
-                               /*  If seeking to first segments on track better do a complete
-                                *  rewind to logical begin of track to get a more steady tape
-                                *  motion.
-                                */
-                               result = ftape_command_wait((location.track & 1) ?
-                                                  QIC_PHYSICAL_FORWARD :
-                                                   QIC_PHYSICAL_REVERSE,
-                                               timeout.rewind, &status);
-                               check_bot_eot(status);  /* update location */
-                       } else {
-                               result = skip_reverse(segment_id - start_offset, &status);
-                       }
-               }
-               if (!location.known) {
-                       TRACE(-1, "panic: location not known");
-                       result = -EIO;
-                       if ((current->signal & _DONT_BLOCK) || ftape_failure) {
-                               break;
-                       } else {
-                               continue;
-                       }
-               }
-               TRACEx2(4, "current segment: %d/%d", location.segment, location.sector);
-               /*  We're on the right track somewhere before the wanted segment.
-                *  Start tape movement if needed and skip to just before or inside
-                *  the requested segment. Keep tape running.
-                */
-               result = 0;
-               if (location.segment < segment_id -
-                   ((tape_running || location.bot) ? 0 : start_offset)) {
-                       if (sector_offset > 0) {
-                               result = seek_forward(segment_id);
-                       } else {
-                               result = seek_forward(segment_id - 1);
-                       }
-               }
-               if (result == 0 &&
-                   location.segment != segment_id - (sector_offset > 0 ? 0 : 1)) {
-                       result = -EIO;
-               }
-       } while (result < 0 && !ftape_failure &&
-                (current->signal & _DONT_BLOCK) == 0 &&
-                ++retry <= 5);
-       if (result < 0) {
-               TRACE(1, "failed to reposition");
-       }
-       TRACE_EXIT;
-       return result;
-}
diff --git a/drivers/char/ftape/ftape-rw.h b/drivers/char/ftape/ftape-rw.h
deleted file mode 100644 (file)
index 983a1ed..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-#ifndef _FTAPE_RW_H
-#define _FTAPE_RW_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-rw.h,v $
- $Author: bas $
- *
- $Revision: 1.33 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- *      This file contains the definitions for the read and write
- *      functions for the QIC-117 floppy-tape driver for Linux.
- *
- */
-
-#include "fdc-io.h"
-#include "kernel-interface.h"
-
-#define GET2( address, offset) *(short*)(address + offset)
-#define GET4( address, offset) *(long*)(address + offset)
-#define PUT2( address, offset, value) *(short*)(address + offset) = value
-#define PUT4( address, offset, value) *(long*)(address + offset) = value
-
-enum runner_status_enum {
-       idle = 0,
-       running,
-       do_abort,
-       aborting,
-       logical_eot,
-       end_of_tape,
-       buffer_overrun,
-       buffer_underrun,
-};
-
-typedef struct {
-       byte *address;
-       volatile buffer_state_enum status;
-       volatile byte *ptr;
-       volatile unsigned bytes;
-       volatile unsigned segment_id;
-
-       /* bitmap for remainder of segment not yet handled.
-        * one bit set for each bad sector that must be skipped.
-        */
-       volatile unsigned long bad_sector_map;
-
-       /* bitmap with bad data blocks in data buffer.
-        * the errors in this map may be retried.
-        */
-       volatile unsigned long soft_error_map;
-
-       /* bitmap with bad data blocks in data buffer
-        * the errors in this map may not be retried.
-        */
-       volatile unsigned long hard_error_map;
-
-       /* retry counter for soft errors.
-        */
-       volatile int retry;
-
-       /* sectors to skip on retry ???
-        */
-       volatile unsigned int skip;
-
-       /* nr of data blocks in data buffer
-        */
-       volatile unsigned data_offset;
-
-       /* offset in segment for first sector to be handled.
-        */
-       volatile unsigned sector_offset;
-
-       /* size of cluster of good sectors to be handled.
-        */
-       volatile unsigned sector_count;
-
-       /* size of remaining part of segment to be handled.
-        */
-       volatile unsigned remaining;
-
-       /* points to next segment (contiguous) to be handled,
-        * or is zero if no read-ahead is allowed.
-        */
-       volatile unsigned next_segment;
-
-       /* flag being set if deleted data was read.
-        */
-       volatile int deleted;
-
-       volatile byte head;
-       volatile byte cyl;
-       volatile byte sect;
-} buffer_struct;
-
-typedef struct {
-       int active;
-       int error;
-       int offset;
-} ftape_fast_start_struct;
-
-typedef struct {
-       int id;
-       int size;
-       int free;
-} ftape_last_segment_struct;
-
-typedef struct {
-       int track;              /* tape head position */
-       volatile int known;     /* validates bot, segment, sector */
-       volatile int bot;       /* logical begin of track */
-       volatile int eot;       /* logical end of track */
-       volatile int segment;   /* current segment */
-       volatile int sector;    /* sector offset within current segment */
-} location_record;
-
-typedef enum {
-       fmt_normal = 2, fmt_1100ft = 3, fmt_wide = 4, fmt_425ft = 5
-} format_type;
-
-/*      ftape-rw.c defined global vars.
- */
-extern int tracing;
-extern byte trace_id;
-extern buffer_struct buffer[];
-extern location_record location;
-extern volatile ftape_fast_start_struct ftape_fast_start;
-extern byte deblock_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
-extern byte scratch_buffer[(SECTORS_PER_SEGMENT - 3) * SECTOR_SIZE];
-extern ftape_last_segment_struct ftape_last_segment;
-extern int header_segment_1;
-extern int header_segment_2;
-extern int used_header_segment;
-extern unsigned int fast_seek_segment_time;
-extern volatile int tape_running;
-extern format_type format_code;
-
-/*      ftape-rw.c defined global functions.
- */
-extern int count_ones(unsigned long mask);
-extern int valid_segment_no(unsigned segment);
-extern int setup_new_segment(buffer_struct * buff, unsigned int segment_id,
-                            int offset);
-extern int calc_next_cluster(buffer_struct * buff);
-extern buffer_struct *next_buffer(volatile int *x);
-extern int ftape_read_id(void);
-extern void ftape_tape_parameters(byte drive_configuration);
-extern int wait_segment(buffer_state_enum state);
-extern int ftape_dumb_stop(void);
-extern int ftape_start_tape(int segment_id, int offset);
-
-/*      fdc-io.c defined global functions.
- */
-extern int setup_fdc_and_dma(buffer_struct * buff, byte operation);
-
-#endif                         /* _FTAPE_RW_H */
diff --git a/drivers/char/ftape/ftape-write.c b/drivers/char/ftape/ftape-write.c
deleted file mode 100644 (file)
index 0ba24c9..0000000
+++ /dev/null
@@ -1,723 +0,0 @@
-
-
-
-/*
- *      Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-write.c,v $
- $Author: bas $
- *
- $Revision: 1.26 $
- $Date: 1995/05/27 08:55:27 $
- $State: Beta $
- *
- *      This file contains the writing code
- *      for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/ftape.h>
-#include <asm/uaccess.h>
-
-#include "tracing.h"
-#include "ftape-write.h"
-#include "ftape-read.h"
-#include "qic117.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "ftape-eof.h"
-#include "ecc.h"
-#include "ftape-bsm.h"
-
-
-/*      Global vars.
- */
-
-/*      Local vars.
- */
-static int buf_pos_wr = 0;
-static int last_write_failed = 0;
-static int need_flush = 0;
-
-#define WRITE_MULTI  0
-#define WRITE_SINGLE 1
-
-void ftape_zap_write_buffers(void)
-{
-       int i;
-
-       for (i = 0; i < NR_BUFFERS; ++i) {
-               buffer[i].status = done;
-       }
-       need_flush = 0;
-}
-
-int copy_and_gen_ecc(char *destination, byte * source,
-                    unsigned int bad_sector_map)
-{
-       TRACE_FUN(8, "copy_and_gen_ecc");
-       int result;
-       struct memory_segment mseg;
-       int bads = count_ones(bad_sector_map);
-
-       if (bads > 0) {
-               TRACEi(4, "bad sectors in map:", bads);
-       }
-       if (bads + 3 >= SECTORS_PER_SEGMENT) {
-               TRACE(4, "empty segment");
-               mseg.blocks = 0;        /* skip entire segment */
-               result = 0;     /* nothing written */
-       } else {
-               mseg.blocks = SECTORS_PER_SEGMENT - bads;
-               mseg.data = destination;
-               memcpy(mseg.data, source, (mseg.blocks - 3) * SECTOR_SIZE);
-               result = ecc_set_segment_parity(&mseg);
-               if (result < 0) {
-                       TRACE(1, "ecc_set_segment_parity failed");
-               } else {
-                       result = (mseg.blocks - 3) * SECTOR_SIZE;
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-void prevent_flush(void)
-{
-       need_flush = 0;
-       ftape_state = idle;
-}
-
-int start_writing(int mode)
-{
-       TRACE_FUN(5, "start_writing");
-       int result = 0;
-       buffer_struct *buff = &buffer[head];
-       int segment_id = buff->segment_id;
-
-       if (ftape_state == writing && buff->status == waiting) {
-               setup_new_segment(buff, segment_id, 1);
-               if (mode == WRITE_SINGLE) {
-                       buffer[head].next_segment = 0;  /* stop tape instead of pause */
-               }
-               calc_next_cluster(buff);        /* prepare */
-               buff->status = writing;
-               if (runner_status == idle) {
-                       TRACEi(5, "starting runner for segment", segment_id);
-                       result = ftape_start_tape(segment_id, buff->sector_offset);
-                       if (result >= 0) {
-                               runner_status = running;
-                       }
-               }
-               if (result >= 0) {
-                       result = setup_fdc_and_dma(buff, FDC_WRITE);    /* go */
-               }
-               ftape_state = writing;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int loop_until_writes_done(void)
-{
-       TRACE_FUN(5, "loop_until_writes_done");
-       int i;
-       int result = 0;
-
-       /*
-        *  Wait until all data is actually written to tape.
-        */
-       while (ftape_state == writing && buffer[head].status != done) {
-               TRACEx2(7, "tail: %d, head: %d", tail, head);
-               for (i = 0; i < NR_BUFFERS; ++i) {
-                       TRACEx3(8, "buffer[ %d] segment_id: %d, status: %d",
-                             i, buffer[i].segment_id, buffer[i].status);
-               }
-               result = fdc_interrupt_wait(5 * SECOND);
-               if (result < 0) {
-                       TRACE(1, "fdc_interrupt_wait failed");
-                       last_write_failed = 1;
-                       break;
-               }
-               if (buffer[head].status == error) {
-                       /* Allow escape from loop when signaled !
-                        */
-                       if (current->signal & _DONT_BLOCK) {
-                               TRACE(2, "interrupted by signal");
-                               TRACE_EXIT;
-                               result = -EINTR;        /* is this the right return value ? */
-                               break;
-                       }
-                       if (buffer[head].hard_error_map != 0) {
-                               /*  Implement hard write error recovery here
-                                */
-                       }
-                       buffer[head].status = waiting;  /* retry this one */
-                       if (runner_status == aborting) {
-                               ftape_dumb_stop();
-                               runner_status = idle;
-                       }
-                       if (runner_status != idle) {
-                               TRACE(1, "unexpected state: runner_status != idle");
-                               result = -EIO;
-                               break;
-                       }
-                       start_writing(WRITE_MULTI);
-               }
-               TRACE(5, "looping until writes done");
-               result = 0;     /* normal exit status */
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-/*      Write given segment from buffer at address onto tape.
- */
-int write_segment(unsigned segment_id, byte * address, int flushing)
-{
-       TRACE_FUN(5, "write_segment");
-       int result = 0;
-       int bytes_written = 0;
-
-       TRACEi(5, "segment_id =", segment_id);
-       if (ftape_state != writing) {
-               if (ftape_state == reading) {
-                       TRACE(5, "calling ftape_abort_operation");
-                       result = ftape_abort_operation();
-                       if (result < 0) {
-                               TRACE(1, "ftape_abort_operation failed");
-                       }
-               }
-               ftape_zap_read_buffers();
-               ftape_zap_write_buffers();
-               ftape_state = writing;
-       }
-       /*    if all buffers full we'll have to wait...
-        */
-       wait_segment(writing);
-       if (buffer[tail].status == error) {
-               /*  setup for a retry
-                */
-               buffer[tail].status = waiting;
-               bytes_written = -EAGAIN;        /* force retry */
-               if (buffer[tail].hard_error_map != 0) {
-                       TRACEx1(1, "warning: %d hard error(s) in written segment",
-                               count_ones(buffer[tail].hard_error_map));
-                       TRACEx1(4, "hard_error_map = 0x%08lx", buffer[tail].hard_error_map);
-                       /*  Implement hard write error recovery here
-                        */
-               }
-       } else if (buffer[tail].status == done) {
-               history.defects += count_ones(buffer[tail].hard_error_map);
-       } else {
-               TRACE(1, "wait for empty segment failed");
-               result = -EIO;
-       }
-       /*    If just passed last segment on tape: wait for BOT or EOT mark.
-        */
-       if (result >= 0 && runner_status == logical_eot) {
-               int status;
-
-               result = ftape_ready_wait(timeout.seek, &status);
-               if (result < 0 || (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) {
-                       TRACE(1, "eot/bot not reached");
-               } else {
-                       runner_status = end_of_tape;
-               }
-       }
-       /*    should runner stop ?
-        */
-       if (result >= 0 &&
-       (runner_status == aborting || runner_status == buffer_underrun ||
-        runner_status == end_of_tape)) {
-               if (runner_status != end_of_tape) {
-                       result = ftape_dumb_stop();
-               }
-               if (result >= 0) {
-                       if (runner_status == aborting) {
-                               if (buffer[head].status == writing) {
-                                       buffer[head].status = done;     /* ????? */
-                               }
-                       }
-                       runner_status = idle;   /* aborted ? */
-               }
-       }
-       /*  Don't start tape if runner idle and segment empty.
-        */
-       if (result >= 0 && !(runner_status == idle &&
-                   get_bad_sector_entry(segment_id) == EMPTY_SEGMENT)) {
-               if (buffer[tail].status == done) {
-                       /*    now at least one buffer is empty, fill it with our data.
-                        *    skip bad sectors and generate ecc.
-                        *    copy_and_gen_ecc return nr of bytes written,
-                        *    range 0..29 Kb inclusive !
-                        */
-                       result = copy_and_gen_ecc(buffer[tail].address, address,
-                                      get_bad_sector_entry(segment_id));
-                       if (result >= 0) {
-                               bytes_written = result;
-                               buffer[tail].segment_id = segment_id;
-                               buffer[tail].status = waiting;
-                               next_buffer(&tail);
-                       }
-               }
-               /*    Start tape only if all buffers full or flush mode.
-                *    This will give higher probability of streaming.
-                */
-               if (result >= 0 && runner_status != running &&
-                   ((head == tail && buffer[tail].status == waiting) || flushing)) {
-                       result = start_writing(WRITE_MULTI);
-               }
-       }
-       TRACE_EXIT;
-       return (result < 0) ? result : bytes_written;
-}
-
-/*  Write as much as fits from buffer to the given segment on tape
- *  and handle retries.
- *  Return the number of bytes written (>= 0), or:
- *      -EIO          write failed
- *      -EINTR        interrupted by signal
- *      -ENOSPC       device full
- */
-int _write_segment(unsigned int segment_id, byte * buffer, int flush)
-{
-       TRACE_FUN(5, "_write_segment");
-       int retry = 0;
-       int result;
-
-       history.used |= 2;
-       for (;;) {
-               if (segment_id > ftape_last_segment.id && !flush) {
-                       result = -ENOSPC;       /* tape full */
-                       break;
-               }
-               result = write_segment(segment_id, buffer, flush);
-               if (result < 0) {
-                       if (result == -EAGAIN) {
-                               if (++retry > 100) {
-                                       TRACE(1, "write failed, >100 retries in segment");
-                                       result = -EIO;  /* give up */
-                                       break;
-                               } else {
-                                       TRACEx1(2, "write error, retry %d", retry);
-                               }
-                       } else {
-                               TRACEi(1, "write_segment failed, error:", -result);
-                               break;
-                       }
-               } else {        /* success */
-                       if (result == 0) {      /* empty segment */
-                               TRACE(4, "empty segment, nothing written");
-                       }
-                       break;
-               }
-               /* Allow escape from loop when signaled !
-                */
-               if (current->signal & _DONT_BLOCK) {
-                       TRACE(2, "interrupted by signal");
-                       TRACE_EXIT;
-                       result = -EINTR;        /* is this the right return value ? */
-                       break;
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int update_header_segment(unsigned segment, byte * buffer)
-{
-       TRACE_FUN(5, "update_header_segment");
-       int result = 0;
-       int status;
-
-       if (buffer == NULL) {
-               TRACE(5, "no input buffer specified");
-               buffer = deblock_buffer;
-               result = read_segment(used_header_segment, buffer, &status, 0);
-               if (bad_sector_map_changed) {
-                       store_bad_sector_map(buffer);
-               }
-               if (failed_sector_log_changed) {
-                       update_failed_sector_log(buffer);
-               }
-       }
-       if (result >= 0 && GET4(buffer, 0) != 0xaa55aa55) {
-               TRACE(1, "wrong header signature found, aborting");
-               result = -EIO;
-       }
-       if (result >= 0) {
-               result = _write_segment(segment, buffer, 0);
-               if (result >= 0 && runner_status == idle) {
-                       /*  Force flush for single segment instead of relying on
-                        *  flush in read_segment for multiple segments.
-                        */
-                       result = start_writing(WRITE_SINGLE);
-                       if (result >= 0 && ftape_state == writing) {
-                               result = loop_until_writes_done();
-                               prevent_flush();
-                       }
-               }
-#ifdef VERIFY_HEADERS
-               if (result >= 0) {      /* read back and verify */
-                       result = read_segment(segment, scratch_buffer, &status, 0);
-                       /*  Should retry if soft error during read !
-                        *  TO BE IMPLEMENTED
-                        */
-                       if (result >= 0) {
-                               if (memcmp(buffer, scratch_buffer, sizeof(buffer)) == 0) {
-                                       result = 0;     /* verified */
-                                       TRACE(5, "verified");
-                               } else {
-                                       result = -EIO;  /* verify failed */
-                                       TRACE(5, "verify failed");
-                               }
-                       }
-               }
-#endif
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_write_header_segments(byte * buffer)
-{
-       TRACE_FUN(5, "ftape_write_header_segments");
-       int result = 0;
-       int retry = 0;
-       int header_1_ok = 0;
-       int header_2_ok = 0;
-
-       do {
-               if (!header_1_ok) {
-                       result = update_header_segment(header_segment_1, buffer);
-                       if (result < 0) {
-                               continue;
-                       }
-                       header_1_ok = 1;
-               }
-               if (!header_2_ok) {
-                       result = update_header_segment(header_segment_2, buffer);
-                       if (result < 0) {
-                               continue;
-                       }
-                       header_2_ok = 1;
-               }
-       } while (result < 0 && retry++ < 3);
-       if (result < 0) {
-               if (!header_1_ok) {
-                       TRACE(1, "update of first header segment failed");
-               }
-               if (!header_2_ok) {
-                       TRACE(1, "update of second header segment failed");
-               }
-               result = -EIO;
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_update_header_segments(byte * buffer, int update)
-{
-       TRACE_FUN(5, "ftape_update_header_segments");
-       int result = 0;
-       int dummy;
-       int header_changed = 1;
-
-       if (ftape_state == writing) {
-               result = loop_until_writes_done();
-       }
-       if (read_only) {
-               result = 0;     /* exit and fake success */
-               TRACE(4, "Tape set read-only: no update");
-       } else if (result >= 0) {
-               result = ftape_abort_operation();
-               if (result >= 0) {
-                       if (buffer == NULL) {
-                               if (bad_sector_map_changed || failed_sector_log_changed) {
-                                       ftape_seek_to_bot();    /* prevents extra rewind */
-                                       buffer = deblock_buffer;
-                                       result = read_segment(used_header_segment, buffer, &dummy, 0);
-                                       if (result < 0) {
-                                               TRACE_EXIT;
-                                               return result;
-                                       }
-                               }
-                               header_changed = 0;
-                       }
-                       if (update) {
-                               if (bad_sector_map_changed) {
-                                       store_bad_sector_map(buffer);
-                                       header_changed = 1;
-                               }
-                               if (failed_sector_log_changed) {
-                                       update_failed_sector_log(buffer);
-                                       header_changed = 1;
-                               }
-                       }
-                       if (header_changed) {
-                               ftape_seek_to_bot();    /* prevents extra rewind */
-                               result = ftape_write_header_segments(buffer);
-                       }
-               }
-       }
-       TRACE_EXIT;
-       return result;
-}
-
-int ftape_flush_buffers(void)
-{
-       TRACE_FUN(5, "ftape_flush_buffers");
-       int result;
-       int pad_count;
-       int data_remaining;
-       static int active = 0;
-
-       if (active) {
-               TRACE(5, "nested call, abort");
-               TRACE_EXIT;
-               return 0;
-       }
-       active = 1;
-       TRACEi(5, "entered, ftape_state =", ftape_state);
-       if (ftape_state != writing && !need_flush) {
-               active = 0;
-               TRACE(5, "no need for flush");
-               TRACE_EXIT;
-               return 0;
-       }
-       data_remaining = buf_pos_wr;
-       buf_pos_wr = 0;         /* prevent further writes if this fails */
-       TRACE(5, "flushing write buffers");
-       if (last_write_failed) {
-               ftape_zap_write_buffers();
-               active = 0;
-               TRACE_EXIT;
-               return write_protected ? -EROFS : -EIO;
-       }
-       /*
-        *    If there is any data not written to tape yet, append zero's
-        *    up to the end of the sector. Then write the segment(s) to tape.
-        */
-       if (data_remaining > 0) {
-               int written;
-
-               do {
-                       TRACEi(4, "remaining in buffer:", data_remaining);
-                       pad_count = sizeof(deblock_buffer) - data_remaining;
-                       TRACEi(7, "flush, padding count:", pad_count);
-                       memset(deblock_buffer + data_remaining, 0, pad_count);  /* pad buffer */
-                       result = _write_segment(ftape_seg_pos, deblock_buffer, 1);
-                       if (result < 0) {
-                               if (result != -ENOSPC) {
-                                       last_write_failed = 1;
-                               }
-                               active = 0;
-                               TRACE_EXIT;
-                               return result;
-                       }
-                       written = result;
-                       clear_eof_mark_if_set(ftape_seg_pos, written);
-                       TRACEi(7, "flush, moved out buffer:", written);
-                       if (written > 0) {
-                               data_remaining -= written;
-                               if (data_remaining > 0) {
-                                       /*  Need another segment for remaining data, move the remainder
-                                        *  to the beginning of the buffer
-                                        */
-                                       memmove(deblock_buffer, deblock_buffer + written, data_remaining);
-                               }
-                       }
-                       ++ftape_seg_pos;
-               } while (data_remaining > 0);
-               /*  Data written to last segment == data_remaining + written
-                *  value is in range [1..29K].
-                */
-               TRACEx2(4, "last write: %d, netto pad-count: %d",
-                       data_remaining + written, -data_remaining);
-               if (-1024 < data_remaining && data_remaining <= 0) {
-                       /*  Last sector of segment was used for data, so put eof mark
-                        *  in next segment and position at second file mark.
-                        */
-                       if (ftape_weof(2, ftape_seg_pos, 1) >= 0) {
-                               ++ftape_seg_pos;        /* position between file marks */
-                       }
-               } else {
-                       /*  Put eof mark in previous segment after data and position
-                        *  at second file mark.
-                        */
-                       ftape_weof(2, ftape_seg_pos - 1, 1 +
-                                  ((SECTOR_SIZE - 1 + result + data_remaining) / SECTOR_SIZE));
-               }
-       } else {
-               TRACE(7, "deblock_buffer empty");
-               if (ftape_weof(2, ftape_seg_pos, 1) >= 0) {
-                       ++ftape_seg_pos;        /* position between file marks */
-               }
-               start_writing(WRITE_MULTI);
-       }
-       TRACE(7, "waiting");
-       result = loop_until_writes_done();
-       if (result < 0) {
-               TRACE(1, "flush buffers failed");
-       }
-       ftape_state = idle;
-       last_write_failed = 0;
-       need_flush = 0;
-       active = 0;
-       TRACE_EXIT;
-       return result;
-}
-
-int _ftape_write(const char *buff, int req_len)
-{
-       TRACE_FUN(5, "_ftape_write");
-       int result = 0;
-       int cnt;
-       int written = 0;
-
-       if (write_protected) {
-               TRACE(1, "error: cartridge write protected");
-               last_write_failed = 1;
-               result = -EROFS;
-       } else if (ftape_offline || !formatted || no_tape) {
-               result = -EIO;
-       } else if (first_data_segment == -1) {
-               /*
-                *    If we haven't read the header segment yet, do it now.
-                *    This will verify the configuration, get the eof markers
-                *    and the bad sector table.
-                *    We'll use the deblock buffer for scratch.
-                */
-               result = read_header_segment(deblock_buffer);
-               if (result >= 0 && ftape_seg_pos > ftape_last_segment.id) {
-                       result = -ENOSPC;       /* full is full */
-               }
-       }
-       if (result < 0) {
-               TRACE_EXIT;
-               return result;
-       }
-       /*
-        *    This part writes data blocks to tape until the
-        *    requested amount is written.
-        *    The data will go in a buffer until it's enough
-        *    for a segment without bad sectors. Then we'll write
-        *    that segment to tape.
-        *    The bytes written will be removed from the buffer
-        *    and the process is repeated until there is less
-        *    than one segment to write left in the buffer.
-        */
-       while (req_len > 0) {
-               int space_left = sizeof(deblock_buffer) - buf_pos_wr;
-
-               TRACEi(7, "remaining req_len:", req_len);
-               TRACEi(7, "          buf_pos:", buf_pos_wr);
-               cnt = (req_len < space_left) ? req_len : space_left;
-               if (cnt > 0) {
-                       result = verify_area(VERIFY_READ, buff, cnt);
-                       if (result) {
-                               TRACE(1, "verify_area failed");
-                               last_write_failed = 1;
-                               TRACE_EXIT;
-                               return result;
-                       }
-                       copy_from_user(deblock_buffer + buf_pos_wr, buff, cnt);
-                       buff += cnt;
-                       req_len -= cnt;
-                       buf_pos_wr += cnt;
-               }
-               TRACEi(7, "moved into blocking buffer:", cnt);
-               while (buf_pos_wr >= sizeof(deblock_buffer)) {
-                       /*  If this is the last buffer to be written, let flush handle it.
-                        */
-                       if (ftape_seg_pos >= ftape_last_segment.id) {
-                               TRACEi(7, "remaining in blocking buffer:", buf_pos_wr);
-                               TRACEi(7, "just written bytes:", written + cnt);
-                               TRACE_EXIT;
-                               return written + cnt;
-                       }
-                       /* Got one full buffer, write it to disk
-                        */
-                       result = _write_segment(ftape_seg_pos, deblock_buffer, 0);
-                       TRACEi(5, "_write_segment result =", result);
-                       if (result < 0) {
-                               if (result == -EAGAIN) {
-                                       TRACE(5, "retry...");
-                                       continue;       /* failed, retry same segment */
-                               }
-                               last_write_failed = 1;
-                               TRACE_EXIT;
-                               return result;
-                       } else {
-                               clear_eof_mark_if_set(ftape_seg_pos, result);
-                       }
-                       if (result > 0 && result < buf_pos_wr) {
-                               /* Partial write: move remainder in lower part of buffer
-                                */
-                               memmove(deblock_buffer, deblock_buffer + result, buf_pos_wr - result);
-                       }
-                       TRACEi(7, "moved out of blocking buffer:", result);
-                       buf_pos_wr -= result;   /* remainder */
-                       ++ftape_seg_pos;
-                       /* Allow us to escape from this loop with a signal !
-                        */
-                       if (current->signal & _DONT_BLOCK) {
-                               TRACE(2, "interrupted by signal");
-                               last_write_failed = 1;
-                               TRACE_EXIT;
-                               return -EINTR;  /* is this the right return value ? */
-                       }
-               }
-               written += cnt;
-       }
-       TRACEi(7, "remaining in blocking buffer:", buf_pos_wr);
-       TRACEi(7, "just written bytes:", written);
-       last_write_failed = 0;
-       if (!need_flush && written > 0) {
-               need_flush = 1;
-       }
-       TRACE_EXIT;
-       return written;         /* bytes written */
-}
-
-int ftape_fix(void)
-{
-       TRACE_FUN(5, "ftape_fix");
-       int result = 0;
-       int dummy;
-       int status;
-
-       if (write_protected) {
-               result = -EROFS;
-       } else {
-               /*  This will copy header segment 2 to header segment 1
-                *  Spares us a tape format operation if header 2 is still good.
-                */
-               header_segment_1 = 0;
-               header_segment_2 = 1;
-               first_data_segment = 2;
-               result = read_segment(header_segment_2, scratch_buffer, &dummy, 0);
-               result = ftape_ready_wait(timeout.pause, &status);
-               result = ftape_write_header_segments(scratch_buffer);
-       }
-       TRACE_EXIT;
-       return result;
-}
diff --git a/drivers/char/ftape/ftape-write.h b/drivers/char/ftape/ftape-write.h
deleted file mode 100644 (file)
index 2cbf076..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef _FTAPE_WRITE_H
-#define _FTAPE_WRITE_H
-
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape-write.h,v $
- $Author: bas $
- *
- $Revision: 1.13 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- *      This file contains the definitions for the write functions
- *      for the QIC-117 floppy-tape driver for Linux.
- *
- */
-
-/*      ftape-write.c defined global vars.
- */
-
-/*      ftape-write.c defined global functions.
- */
-extern int _ftape_write(const char *buff, int req_len);
-extern int ftape_flush_buffers(void);
-extern int ftape_write_header_segments(byte * buffer);
-extern int ftape_update_header_segments(byte * buffer, int update);
-extern int write_segment(unsigned segment, byte * address, int flushing);
-extern int ftape_fix(void);
-extern void prevent_flush(void);
-extern void ftape_zap_write_buffers(void);
-
-#endif                         /* _FTAPE_WRITE_H */
diff --git a/drivers/char/ftape/kernel-interface.c b/drivers/char/ftape/kernel-interface.c
deleted file mode 100644 (file)
index 9bcb35e..0000000
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- *      Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- *      This file contains the code that interfaces the kernel
- *      for the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <asm/uaccess.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/major.h>
-#include <linux/malloc.h>
-#include <linux/init.h>
-#include <linux/ftape.h>
-#include <asm/dma.h>
-
-#include "tracing.h"
-#include "kernel-interface.h"
-#include "ftape-read.h"
-#include "ftape-write.h"
-#include "ftape-io.h"
-#include "ftape-ctl.h"
-#include "ftape-rw.h"
-#include "fdc-io.h"
-
-
-/*      Global vars.
- */
-
-/* Allocating a 96Kb DMAable buffer in one chunk won't work due to
- * memory fragmentation.  To avoid this, it is broken up into
- * NR_BUFFERS chunks of 32Kbyte. --khp
- */
-
-byte *tape_buffer[NR_BUFFERS] = {NULL};
-
-/*      Local vars.
- */
-static int busy_flag = 0;
-static int old_sigmask;
-
-static int ftape_open(struct inode *ino, struct file *filep);
-static int ftape_close(struct inode *ino, struct file *filep);
-static int ftape_ioctl(struct inode *ino, struct file *filep,
-                      unsigned int command, unsigned long arg);
-static long ftape_read(struct inode *ino, struct file *fp,
-                      char *buff, unsigned long req_len);
-static long ftape_write(struct inode *ino, struct file *fp,
-                       const char *buff, unsigned long req_len);
-
-static struct file_operations ftape_cdev =
-{
-       NULL,                   /* lseek */
-       ftape_read,             /* read */
-       ftape_write,            /* write */
-       NULL,                   /* readdir */
-       NULL,                   /* poll */
-       ftape_ioctl,            /* ioctl */
-       NULL,                   /* mmap */
-       ftape_open,             /* open */
-       ftape_close,            /* release */
-       NULL,                   /* fsync */
-};
-
-/*
- * DMA'able memory allocation stuff.
- */
-
-/* Pure 2^n version of get_order */
-static inline int __get_order(unsigned long size)
-{
-       int order;
-
-       size = (size-1) >> (PAGE_SHIFT-1);
-       order = -1;
-       do {
-               size >>= 1;
-               order++;
-       } while (size);
-       return order;
-}
-
-static inline
-void *dmaalloc(int order)
-{
-       return (void *) __get_dma_pages(GFP_KERNEL, order);
-}
-
-static inline
-void dmafree(void *addr, int order)
-{
-       free_pages((unsigned long) addr, order);
-}
-
-/*
- * Called by modules package when installing the driver
- * or by kernel during the initialization phase
- */
-
-#ifdef MODULE
-EXPORT_NO_SYMBOLS;
-#define ftape_init init_module
-#endif
-
-__initfunc(int ftape_init(void))
-{
-       int n;
-       int order;
-       TRACE_FUN(5, "ftape_init");
-#ifdef MODULE
-       printk(KERN_INFO "ftape-2.08 960314\n"
-              KERN_INFO " (c) 1993-1995 Bas Laarhoven (bas@vimec.nl)\n"
-              KERN_INFO " (c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no)\n"
-              KERN_INFO " QIC-117 driver for QIC-40/80/3010/3020 tape drives\n"
-              KERN_INFO " Compiled for kernel version " UTS_RELEASE
-#ifdef MODVERSIONS
-              " with versioned symbols"
-#endif
-              "\n");
-#else /* !MODULE */
-       /* print a short no-nonsense boot message */
-       printk("ftape-2.08 960314 for Linux 1.3.70\n");
-#endif /* MODULE */
-       TRACE(3, "installing QIC-117 ftape driver...");
-       if (register_chrdev(QIC117_TAPE_MAJOR, "ft", &ftape_cdev)) {
-               TRACE(1, "register_chrdev failed");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       TRACEx1(3, "ftape_init @ 0x%p", ftape_init);
-       /*
-        * Allocate the DMA buffers. They are deallocated at cleanup() time.
-        */
-       order = __get_order(BUFF_SIZE);
-       for (n = 0; n < NR_BUFFERS; n++) {
-               tape_buffer[n] = (byte *) dmaalloc(order);
-               if (!tape_buffer[n]) {
-                       TRACE(1, "dmaalloc() failed");
-                       for (n = 0; n < NR_BUFFERS; n++) {
-                               if (tape_buffer[n]) {
-                                       dmafree(tape_buffer[n], order);
-                                       tape_buffer[n] = NULL;
-                               }
-                       }
-                       current->blocked = old_sigmask;         /* restore mask */
-                       if (unregister_chrdev(QIC117_TAPE_MAJOR, "ft") != 0) {
-                               TRACE(3, "unregister_chrdev failed");
-                       }
-                       TRACE_EXIT;
-                       return -ENOMEM;
-               } else {
-                       TRACEx2(3, "dma-buffer #%d @ %p", n, tape_buffer[n]);
-               }
-       }
-       busy_flag = 0;
-       ftape_unit = -1;
-       ftape_failure = 1;      /* inhibit any operation but open */
-       udelay_calibrate();     /* must be before fdc_wait_calibrate ! */
-       fdc_wait_calibrate();
-       TRACE_EXIT;
-
-       return 0;
-}
-
-
-#ifdef MODULE
-/*      Called by modules package when removing the driver
- */
-void cleanup_module(void)
-{
-       int n;
-       int order;
-       TRACE_FUN(5, "cleanup_module");
-
-       if (unregister_chrdev(QIC117_TAPE_MAJOR, "ft") != 0) {
-               TRACE(3, "failed");
-       } else {
-               TRACE(3, "successful");
-       }
-       order = __get_order(BUFF_SIZE);
-       for (n = 0; n < NR_BUFFERS; n++) {
-               if (tape_buffer[n]) {
-                       dmafree(tape_buffer[n], order);
-                       tape_buffer[n] = NULL;
-                       TRACEx1(3, "removed dma-buffer #%d", n);
-               } else {
-                       TRACEx1(1, "dma-buffer #%d == NULL (bug?)", n);
-               }
-       }
-       TRACE_EXIT;
-}
-#endif                         /* MODULE */
-
-/*      Open ftape device
- */
-static int ftape_open(struct inode *ino, struct file *filep)
-{
-       TRACE_FUN(4, "ftape_open");
-       int result;
-       MOD_INC_USE_COUNT;      /* lock module in memory */
-
-       TRACEi(5, "called for minor", MINOR(ino->i_rdev));
-       if (busy_flag) {
-               TRACE(1, "failed: already busy");
-               MOD_DEC_USE_COUNT;      /* unlock module in memory */
-               TRACE_EXIT;
-               return -EBUSY;
-       }
-       if ((MINOR(ino->i_rdev) & ~FTAPE_NO_REWIND) > 3) {
-               TRACE(1, "failed: illegal unit nr");
-               MOD_DEC_USE_COUNT;      /* unlock module in memory */
-               TRACE_EXIT;
-               return -ENXIO;
-       }
-       if (ftape_unit == -1 || FTAPE_UNIT != (MINOR(ino->i_rdev) & 3)) {
-               /*  Other selection than last time
-                */
-               ftape_init_driver();
-       }
-       ftape_unit = MINOR(ino->i_rdev);
-       ftape_failure = 0;      /* allow tape operations */
-       old_sigmask = current->blocked;
-       current->blocked = _BLOCK_ALL;
-       fdc_save_drive_specs(); /* save Drive Specification regs on i82078-1's */
-       result = _ftape_open();
-       if (result < 0) {
-               TRACE(1, "_ftape_open failed");
-               current->blocked = old_sigmask;         /* restore mask */
-               MOD_DEC_USE_COUNT;      /* unlock module in memory */
-               TRACE_EXIT;
-               return result;
-       } else {
-               busy_flag = 1;
-               /*  Mask signals that will disturb proper operation of the
-                *  program that is calling.
-                */
-               current->blocked = old_sigmask | _DO_BLOCK;
-               TRACE_EXIT;
-               return 0;
-       }
-}
-
-/*      Close ftape device
- */
-static int ftape_close(struct inode *ino, struct file *filep)
-{
-       TRACE_FUN(4, "ftape_close");
-       int result;
-
-       if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit) {
-               TRACE(1, "failed: not busy or wrong unit");
-               TRACE_EXIT;
-               return 0;               /* keep busy_flag !(?) */
-       }
-       current->blocked = _BLOCK_ALL;
-       result = _ftape_close();
-       if (result < 0) {
-               TRACE(1, "_ftape_close failed");
-       }
-       fdc_restore_drive_specs();      /* restore original values */
-       ftape_failure = 1;      /* inhibit any operation but open */
-       busy_flag = 0;
-       current->blocked = old_sigmask;         /* restore before open state */
-       TRACE_EXIT;
-       MOD_DEC_USE_COUNT;      /* unlock module in memory */
-       return 0;
-}
-
-/*      Ioctl for ftape device
- */
-static int ftape_ioctl(struct inode *ino, struct file *filep,
-                      unsigned int command, unsigned long arg)
-{
-       TRACE_FUN(4, "ftape_ioctl");
-       int result = -EIO;
-       int old_sigmask;
-
-       if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) {
-               TRACE(1, "failed: not busy, failure or wrong unit");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       old_sigmask = current->blocked;         /* save mask */
-       current->blocked = _BLOCK_ALL;
-       /* This will work as long as sizeof( void*) == sizeof( long)
-        */
-       result = _ftape_ioctl(command, (void *) arg);
-       current->blocked = old_sigmask;         /* restore mask */
-       TRACE_EXIT;
-       return result;
-}
-
-/*      Read from tape device
- */
-static long ftape_read(struct inode *ino, struct file *fp,
-       char *buff, unsigned long req_len)
-{
-       TRACE_FUN(5, "ftape_read");
-       int result = -EIO;
-       int old_sigmask;
-
-       TRACEi(5, "called with count:", (int) req_len);
-       if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) {
-               TRACE(1, "failed: not busy, failure or wrong unit");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       old_sigmask = current->blocked;         /* save mask */
-       current->blocked = _BLOCK_ALL;
-       result = _ftape_read(buff, req_len);
-       TRACEi(7, "return with count:", result);
-       current->blocked = old_sigmask;         /* restore mask */
-       TRACE_EXIT;
-       return result;
-}
-
-/*      Write to tape device
- */
-static long ftape_write(struct inode *ino, struct file *fp,
-       const char *buff, unsigned long req_len)
-{
-       TRACE_FUN(8, "ftape_write");
-       int result = -EIO;
-       int old_sigmask;
-
-       TRACEi(5, "called with count:", (int) req_len);
-       if (!busy_flag || MINOR(ino->i_rdev) != ftape_unit || ftape_failure) {
-               TRACE(1, "failed: not busy, failure or wrong unit");
-               TRACE_EXIT;
-               return -EIO;
-       }
-       old_sigmask = current->blocked;         /* save mask */
-       current->blocked = _BLOCK_ALL;
-       result = _ftape_write(buff, req_len);
-       TRACEi(7, "return with count:", result);
-       current->blocked = old_sigmask;         /* restore mask */
-       TRACE_EXIT;
-       return result;
-}
diff --git a/drivers/char/ftape/kernel-interface.h b/drivers/char/ftape/kernel-interface.h
deleted file mode 100644 (file)
index 9843390..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef _KERNEL_INTERFACE_H
-#define _KERNEL_INTERFACE_H
-
-/*
- * Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/kernel-interface.h,v $
- $Author: bas $
- *
- $Revision: 1.24 $
- $Date: 1995/04/30 13:15:14 $
- $State: Beta $
- *
- * ----Description----
- *
- */
-
-#include <linux/linkage.h>
-#include <linux/signal.h>
-
-#define _S(nr) (1<<((nr)-1))
-#define _NEVER_BLOCK    (_S(SIGKILL)|_S(SIGSTOP))
-#define _DONT_BLOCK     (_NEVER_BLOCK|_S(SIGINT))
-#define _DO_BLOCK       (_S(SIGPIPE))
-#define _BLOCK_ALL      (0xffffffffL)
-
-
-#ifndef QIC117_TAPE_MAJOR
-#define QIC117_TAPE_MAJOR 27
-#endif
-
-#define FTAPE_NO_REWIND 4      /* mask for minor nr */
-
-/*      kernel-interface.c defined global variables.
- */
-extern byte *tape_buffer[];
-extern char kernel_version[];
-
-/*      kernel-interface.c defined global functions.
- */
-asmlinkage extern int init_module(void);
-asmlinkage extern void cleanup_module(void);
-
-/*      kernel global functions not (yet) standard accessible
- *      (linked at load time by modules package).
- */
-asmlinkage extern sys_sgetmask(void);
-asmlinkage extern sys_ssetmask(int);
-
-#endif
diff --git a/drivers/char/ftape/lowlevel/Makefile b/drivers/char/ftape/lowlevel/Makefile
new file mode 100644 (file)
index 0000000..36834e8
--- /dev/null
@@ -0,0 +1,60 @@
+#
+#       Copyright (C) 1996, 1997 Clau-Justus Heine.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING.  If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/Makefile,v $
+# $Revision: 1.4 $
+# $Date: 1997/10/07 09:26:02 $
+#
+#      Makefile for the lowlevel part QIC-40/80/3010/3020 floppy-tape
+#      driver for Linux.
+#
+
+#
+#  This isn't used inside the kernel, only for my private development
+#  version
+#
+ifndef TOPDIR
+TOPDIR=../..
+include $(TOPDIR)/MCONFIG
+endif
+
+O_TARGET := ftape.o
+O_OBJS    = ftape-init.o fdc-io.o fdc-isr.o \
+           ftape-bsm.o ftape-ctl.o ftape-read.o ftape-rw.o \
+           ftape-write.o ftape-io.o ftape-calibr.o ftape-ecc.o fc-10.o \
+           ftape-buffer.o ftape-format.o
+
+ifeq ($(CONFIG_FTAPE),y)
+O_OBJS   += ftape-setup.o
+endif
+
+ifndef CONFIG_FT_NO_TRACE_AT_ALL
+O_OBJS   += ftape-tracing.o
+endif
+
+ifeq ($(CONFIG_PROC_FS),y)
+ifeq ($(CONFIG_FT_PROC_FS),y)
+O_OBJS += ftape-proc.o
+endif
+endif
+
+OX_OBJS = ftape_syms.o
+
+M_OBJS  = $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/char/ftape/lowlevel/fc-10.c b/drivers/char/ftape/lowlevel/fc-10.c
new file mode 100644 (file)
index 0000000..cbfd71f
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ *
+
+   Copyright (C) 1993,1994 Jon Tombs.
+
+   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.
+
+   The entire guts of this program was written by dosemu, modified to
+   record reads and writes to the ports in the 0x180-0x188 address space,
+   while running the CMS program TAPE.EXE V2.0.5 supplied with the drive.
+
+   Modified to use an array of addresses and generally cleaned up (made
+   much shorter) 4 June 94, dosemu isn't that good at writing short code it
+   would seem :-). Made independent of 0x180, but I doubt it will work
+   at any other address.
+
+   Modified for distribution with ftape source. 21 June 94, SJL.
+
+   Modifications on 20 October 95, by Daniel Cohen (catman@wpi.edu):
+   Modified to support different DMA, IRQ, and IO Ports.  Borland's
+   Turbo Debugger in virtual 8086 mode (TD386.EXE with hardware breakpoints
+   provided by the TDH386.SYS Device Driver) was used on the CMS program
+   TAPE V4.0.5.  I set breakpoints on I/O to ports 0x180-0x187.  Note that
+   CMS's program will not successfully configure the tape drive if you set
+   breakpoints on IO Reads, but you can set them on IO Writes without problems.
+   Known problems:
+   - You can not use DMA Channels 5 or 7.
+
+   Modification on 29 January 96, by Daniel Cohen (catman@wpi.edu):
+   Modified to only accept IRQs 3 - 7, or 9.  Since we can only send a 3 bit
+   number representing the IRQ to the card, special handling is required when
+   IRQ 9 is selected.  IRQ 2 and 9 are the same, and we should request IRQ 9
+   from the kernel while telling the card to use IRQ 2.  Thanks to Greg
+   Crider (gcrider@iclnet.org) for finding and locating this bug, as well as
+   testing the patch.
+
+   Modification on 11 December 96, by Claus Heine (claus@momo.math.rwth-aachen.de):
+   Modified a little to use variahle ft_fdc_base, ft_fdc_irq, ft_fdc_dma 
+   instead of preprocessor symbols. Thus we can compile this into the module
+   or kernel and let the user specify the options as command line arguments.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fc-10.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:04 $
+ *
+ *      This file contains code for the CMS FC-10/FC-20 card.
+ */
+
+#include <asm/io.h>
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/fc-10.h"
+
+__u16 inbs_magic[] = {
+       0x3, 0x3, 0x0, 0x4, 0x7, 0x2, 0x5, 0x3, 0x1, 0x4,
+       0x3, 0x5, 0x2, 0x0, 0x3, 0x7, 0x4, 0x2,
+       0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7
+};
+
+__u16 fc10_ports[] = {
+       0x180, 0x210, 0x2A0, 0x300, 0x330, 0x340, 0x370
+};
+
+int fc10_enable(void)
+{
+       int i;
+       __u8 cardConfig = 0x00;
+       __u8 x;
+       TRACE_FUN(ft_t_flow);
+
+/*  This code will only work if the FC-10 (or FC-20) is set to
+ *  use DMA channels 1, 2, or 3.  DMA channels 5 and 7 seem to be 
+ *  initialized by the same command as channels 1 and 3, respectively.
+ */
+       if (ft_fdc_dma > 3) {
+               TRACE_ABORT(0, ft_t_err,
+"Error: The FC-10/20 must be set to use DMA channels 1, 2, or 3!");
+       }
+/*  Only allow the FC-10/20 to use IRQ 3-7, or 9.  Note that CMS's program
+ *  only accepts IRQ's 2-7, but in linux, IRQ 2 is the same as IRQ 9.
+ */
+       if (ft_fdc_irq < 3 || ft_fdc_irq == 8 || ft_fdc_irq > 9) {
+               TRACE_ABORT(0, ft_t_err, 
+"Error: The FC-10/20 must be set to use IRQ levels 3 - 7, or 9!\n"
+KERN_INFO "Note: IRQ 9 is the same as IRQ 2");
+       }
+       /*  Clear state machine ???
+        */
+       for (i = 0; i < NR_ITEMS(inbs_magic); i++) {
+               inb(ft_fdc_base + inbs_magic[i]);
+       }
+       outb(0x0, ft_fdc_base);
+
+       x = inb(ft_fdc_base);
+       if (x == 0x13 || x == 0x93) {
+               for (i = 1; i < 8; i++) {
+                       if (inb(ft_fdc_base + i) != x) {
+                               TRACE_EXIT 0;
+                       }
+               }
+       } else {
+               TRACE_EXIT 0;
+       }
+
+       outb(0x8, ft_fdc_base);
+
+       for (i = 0; i < 8; i++) {
+               if (inb(ft_fdc_base + i) != 0x0) {
+                       TRACE_EXIT 0;
+               }
+       }
+       outb(0x10, ft_fdc_base);
+
+       for (i = 0; i < 8; i++) {
+               if (inb(ft_fdc_base + i) != 0xff) {
+                       TRACE_EXIT 0;
+               }
+       }
+
+       /*  Okay, we found a FC-10 card ! ???
+        */
+       outb(0x0, fdc.ccr);
+
+       /*  Clear state machine again ???
+        */
+       for (i = 0; i < NR_ITEMS(inbs_magic); i++) {
+               inb(ft_fdc_base + inbs_magic[i]);
+       }
+       /* Send io port */
+       for (i = 0; i < NR_ITEMS(fc10_ports); i++)
+               if (ft_fdc_base == fc10_ports[i])
+                       cardConfig = i + 1;
+       if (cardConfig == 0) {
+               TRACE_EXIT 0;   /* Invalid I/O Port */
+       }
+       /* and IRQ - If using IRQ 9, tell the FC card it is actually IRQ 2 */
+       if (ft_fdc_irq != 9)
+               cardConfig |= ft_fdc_irq << 3;
+       else
+               cardConfig |= 2 << 3;
+
+       /* and finally DMA Channel */
+       cardConfig |= ft_fdc_dma << 6;
+       outb(cardConfig, ft_fdc_base);  /* DMA [2 bits]/IRQ [3 bits]/BASE [3 bits] */
+
+       /*  Enable FC-10 ???
+        */
+       outb(0, fdc.ccr);
+       outb(0, fdc.dor2);
+       outb(FDC_DMA_MODE /* 8 */, fdc.dor);
+       outb(FDC_DMA_MODE /* 8 */, fdc.dor);
+       outb(1, fdc.dor2);
+
+       /*************************************
+        *
+        * cH: why the hell should this be necessary? This is done 
+        *     by fdc_reset()!!!
+        *
+        *************************************/
+       /*  Initialize fdc, select drive B:
+        */
+       outb(FDC_DMA_MODE, fdc.dor);    /* assert reset, dma & irq enabled */
+       /*       0x08    */
+       outb(FDC_DMA_MODE|FDC_RESET_NOT, fdc.dor);      /* release reset */
+       /*       0x08    |   0x04   = 0x0c */
+       outb(FDC_DMA_MODE|FDC_RESET_NOT|FDC_MOTOR_1|FTAPE_SEL_B, fdc.dor);
+       /*       0x08    |   0x04      |  0x20     |  0x01  = 0x2d */    
+       /* select drive 1 */ /* why not drive 0 ???? */
+       TRACE_EXIT (x == 0x93) ? 2 : 1;
+}
diff --git a/drivers/char/ftape/lowlevel/fc-10.h b/drivers/char/ftape/lowlevel/fc-10.h
new file mode 100644 (file)
index 0000000..da7b88b
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _FC_10_H
+#define _FC_10_H
+
+/*
+ * Copyright (C) 1994-1996 Bas Laarhoven.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fc-10.h,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/09/19 09:05:22 $
+ *
+ *      This file contains definitions for the FC-10 code
+ *      of the QIC-40/80 floppy-tape driver for Linux.
+ */
+
+/*
+ *      fc-10.c defined global vars.
+ */
+
+/*
+ *      fc-10.c defined global functions.
+ */
+extern int fc10_enable(void);
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/fdc-io.c b/drivers/char/ftape/lowlevel/fdc-io.c
new file mode 100644 (file)
index 0000000..904a99a
--- /dev/null
@@ -0,0 +1,1439 @@
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ *           (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-io.c,v $
+ * $Revision: 1.7.4.2 $
+ * $Date: 1997/11/16 14:48:17 $
+ *
+ *      This file contains the low-level floppy disk interface code
+ *      for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
+ *      Linux.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/fdc-isr.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-calibr.h"
+#include "../lowlevel/fc-10.h"
+
+/*      Global vars.
+ */
+int ftape_motor = 0;
+volatile int ftape_current_cylinder = -1;
+volatile fdc_mode_enum fdc_mode = fdc_idle;
+fdc_config_info fdc = {0};
+struct wait_queue *ftape_wait_intr = NULL;
+
+unsigned int ft_fdc_base       = CONFIG_FT_FDC_BASE;
+unsigned int ft_fdc_irq        = CONFIG_FT_FDC_IRQ;
+unsigned int ft_fdc_dma        = CONFIG_FT_FDC_DMA;
+unsigned int ft_fdc_threshold  = CONFIG_FT_FDC_THR;  /* bytes */
+unsigned int ft_fdc_rate_limit = CONFIG_FT_FDC_MAX_RATE; /* bits/sec */
+int ft_probe_fc10        = CONFIG_FT_PROBE_FC10;
+int ft_mach2             = CONFIG_FT_MACH2;
+
+/*      Local vars.
+ */
+static unsigned int fdc_calibr_count;
+static unsigned int fdc_calibr_time;
+static int fdc_status;
+volatile __u8 fdc_head;                /* FDC head from sector id */
+volatile __u8 fdc_cyl;         /* FDC track from sector id */
+volatile __u8 fdc_sect;                /* FDC sector from sector id */
+static int fdc_data_rate = 500;        /* data rate (Kbps) */
+static int fdc_rate_code = 0;  /* data rate code (0 == 500 Kbps) */
+static int fdc_seek_rate = 2;  /* step rate (msec) */
+static void (*do_ftape) (void);
+static int fdc_fifo_state;     /* original fifo setting - fifo enabled */
+static int fdc_fifo_thr;       /* original fifo setting - threshold */
+static int fdc_lock_state;     /* original lock setting - locked */
+static int fdc_fifo_locked = 0;        /* has fifo && lock set ? */
+static __u8 fdc_precomp = 0;   /* default precomp. value (nsec) */
+static __u8 fdc_prec_code = 0; /* fdc precomp. select code */
+
+static char ftape_id[] = "ftape";  /* used by request irq and free irq */
+
+void fdc_catch_stray_interrupts(int count)
+{
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       if (count == 0) {
+               ft_expected_stray_interrupts = 0;
+       } else {
+               ft_expected_stray_interrupts += count;
+       }
+       restore_flags(flags);
+}
+
+/*  Wait during a timeout period for a given FDC status.
+ *  If usecs == 0 then just test status, else wait at least for usecs.
+ *  Returns -ETIME on timeout. Function must be calibrated first !
+ */
+int fdc_wait(unsigned int usecs, __u8 mask, __u8 state)
+{
+       int count_1 = (fdc_calibr_count * usecs +
+                       fdc_calibr_count - 1) / fdc_calibr_time;
+
+       do {
+               fdc_status = inb_p(fdc.msr);
+               if ((fdc_status & mask) == state) {
+                       return 0;
+               }
+       } while (count_1-- >= 0);
+       return -ETIME;
+}
+
+int fdc_ready_wait(unsigned int usecs)
+{
+       return fdc_wait(usecs, FDC_DATA_READY | FDC_BUSY, FDC_DATA_READY);
+}
+
+/* Why can't we just use udelay()?
+ */
+static void fdc_usec_wait(unsigned int usecs)
+{
+       fdc_wait(usecs, 0, 1);  /* will always timeout ! */
+}
+
+int fdc_ready_out_wait(unsigned int usecs)
+{
+       fdc_usec_wait(FT_RQM_DELAY);    /* wait for valid RQM status */
+       return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_OUT_READY);
+}
+
+int fdc_ready_in_wait(unsigned int usecs)
+{
+       fdc_usec_wait(FT_RQM_DELAY);    /* wait for valid RQM status */
+       return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_IN_READY);
+}
+
+void fdc_wait_calibrate(void)
+{
+       ftape_calibrate("fdc_wait",
+                       fdc_usec_wait, &fdc_calibr_count, &fdc_calibr_time); 
+}
+
+/*  Wait for a (short) while for the FDC to become ready
+ *  and transfer the next command byte.
+ *  Return -ETIME on timeout on getting ready (depends on hardware!).
+ */
+static int fdc_write(const __u8 data)
+{
+       fdc_usec_wait(FT_RQM_DELAY);    /* wait for valid RQM status */
+       if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_IN_READY) < 0) {
+               return -ETIME;
+       } else {
+               outb(data, fdc.fifo);
+               return 0;
+       }
+}
+
+/*  Wait for a (short) while for the FDC to become ready
+ *  and transfer the next result byte.
+ *  Return -ETIME if timeout on getting ready (depends on hardware!).
+ */
+static int fdc_read(__u8 * data)
+{
+       fdc_usec_wait(FT_RQM_DELAY);    /* wait for valid RQM status */
+       if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_OUT_READY) < 0) {
+               return -ETIME;
+       } else {
+               *data = inb(fdc.fifo);
+               return 0;
+       }
+}
+
+/*  Output a cmd_len long command string to the FDC.
+ *  The FDC should be ready to receive a new command or
+ *  an error (EBUSY or ETIME) will occur.
+ */
+int fdc_command(const __u8 * cmd_data, int cmd_len)
+{
+       int result = 0;
+       unsigned long flags;
+       int count = cmd_len;
+       int retry = 0;
+#ifdef TESTING
+       static unsigned int last_time = 0;
+       unsigned int time;
+#endif
+       TRACE_FUN(ft_t_any);
+
+       fdc_usec_wait(FT_RQM_DELAY);    /* wait for valid RQM status */
+       save_flags(flags);
+       cli();
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,30)
+       if (!in_interrupt())
+#else
+       if (!intr_count)
+#endif
+               /* Yes, I know, too much comments inside this function
+                * ...
+                * 
+                * Yet another bug in the original driver. All that
+                * havoc is caused by the fact that the isr() sends
+                * itself a command to the floppy tape driver (pause,
+                * micro step pause).  Now, the problem is that
+                * commands are transmitted via the fdc_seek
+                * command. But: the fdc performs seeks in the
+                * background i.e. it doesn't signal busy while
+                * sending the step pulses to the drive. Therefore the
+                * non-interrupt level driver has no chance to tell
+                * whether the isr() just has issued a seek. Therefore
+                * we HAVE TO have a look at the the ft_hide_interrupt
+                * flag: it signals the non-interrupt level part of
+                * the driver that it has to wait for the fdc until it
+                * has completet seeking.
+                *
+                * THIS WAS PRESUMABLY THE REASON FOR ALL THAT
+                * "fdc_read timeout" errors, I HOPE :-)
+                */
+               if (ft_hide_interrupt) {
+                       restore_flags(flags);
+                       TRACE(ft_t_info,
+                             "Waiting for the isr() completing fdc_seek()");
+                       if (fdc_interrupt_wait(2 * FT_SECOND) < 0) {
+                               TRACE(ft_t_warn,
+                     "Warning: timeout waiting for isr() seek to complete");
+                       }
+                       if (ft_hide_interrupt || !ft_seek_completed) {
+                               /* There cannot be another
+                                * interrupt. The isr() only stops
+                                * the tape and the next interrupt
+                                * won't come until we have send our
+                                * command to the drive.
+                                */
+                               TRACE_ABORT(-EIO, ft_t_bug,
+                                           "BUG? isr() is still seeking?\n"
+                                           KERN_INFO "hide: %d\n"
+                                           KERN_INFO "seek: %d",
+                                           ft_hide_interrupt,
+                                           ft_seek_completed);
+
+                       }
+                       fdc_usec_wait(FT_RQM_DELAY);    /* wait for valid RQM status */
+                       save_flags(flags);
+                       cli();
+               }
+       fdc_status = inb(fdc.msr);
+       if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_IN_READY) {
+               restore_flags(flags);
+               TRACE_ABORT(-EBUSY, ft_t_err, "fdc not ready");
+       } 
+       fdc_mode = *cmd_data;   /* used by isr */
+#ifdef TESTING
+       if (fdc_mode == FDC_SEEK) {
+               time = ftape_timediff(last_time, ftape_timestamp());
+               if (time < 6000) {
+       TRACE(ft_t_bug,"Warning: short timeout between seek commands: %d",
+             time);
+               }
+       }
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,30)
+       if (!in_interrupt()) {
+               /* shouldn't be cleared if called from isr
+                */
+               ft_interrupt_seen = 0;
+       }
+#else
+       if (!intr_count) {
+               /* shouldn't be cleared if called from isr
+                */
+               ft_interrupt_seen = 0;
+       }
+#endif
+       while (count) {
+               result = fdc_write(*cmd_data);
+               if (result < 0) {
+                       TRACE(ft_t_fdc_dma,
+                             "fdc_mode = %02x, status = %02x at index %d",
+                             (int) fdc_mode, (int) fdc_status,
+                             cmd_len - count);
+                       if (++retry <= 3) {
+                               TRACE(ft_t_warn, "fdc_write timeout, retry");
+                       } else {
+                               TRACE(ft_t_err, "fdc_write timeout, fatal");
+                               /* recover ??? */
+                               break;
+                       }
+               } else {
+                       --count;
+                       ++cmd_data;
+               }
+        }
+#ifdef TESTING
+       if (fdc_mode == FDC_SEEK) {
+               last_time = ftape_timestamp();
+       }
+#endif
+       restore_flags(flags);
+       TRACE_EXIT result;
+}
+
+/*  Input a res_len long result string from the FDC.
+ *  The FDC should be ready to send the result or an error
+ *  (EBUSY or ETIME) will occur.
+ */
+int fdc_result(__u8 * res_data, int res_len)
+{
+       int result = 0;
+       unsigned long flags;
+       int count = res_len;
+       int retry = 0;
+       TRACE_FUN(ft_t_any);
+
+       save_flags(flags);
+       cli();
+       fdc_status = inb(fdc.msr);
+       if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_OUT_READY) {
+               TRACE(ft_t_err, "fdc not ready");
+               result = -EBUSY;
+       } else while (count) {
+               if (!(fdc_status & FDC_BUSY)) {
+                       restore_flags(flags);
+                       TRACE_ABORT(-EIO, ft_t_err, "premature end of result phase");
+               }
+               result = fdc_read(res_data);
+               if (result < 0) {
+                       TRACE(ft_t_fdc_dma,
+                             "fdc_mode = %02x, status = %02x at index %d",
+                             (int) fdc_mode,
+                             (int) fdc_status,
+                             res_len - count);
+                       if (++retry <= 3) {
+                               TRACE(ft_t_warn, "fdc_read timeout, retry");
+                       } else {
+                               TRACE(ft_t_err, "fdc_read timeout, fatal");
+                               /* recover ??? */
+                               break;
+                               ++retry;
+                       }
+               } else {
+                       --count;
+                       ++res_data;
+               }
+       }
+       restore_flags(flags);
+       fdc_usec_wait(FT_RQM_DELAY);    /* allow FDC to negate BSY */
+       TRACE_EXIT result;
+}
+
+/*      Handle command and result phases for
+ *      commands without data phase.
+ */
+int fdc_issue_command(const __u8 * out_data, int out_count,
+                     __u8 * in_data, int in_count)
+{
+       TRACE_FUN(ft_t_any);
+
+       if (out_count > 0) {
+               TRACE_CATCH(fdc_command(out_data, out_count),);
+       }
+       /* will take 24 - 30 usec for fdc_sense_drive_status and
+        * fdc_sense_interrupt_status commands.
+        *    35 fails sometimes (5/9/93 SJL)
+        * On a loaded system it incidentally takes longer than
+        * this for the fdc to get ready ! ?????? WHY ??????
+        * So until we know what's going on use a very long timeout.
+        */
+       TRACE_CATCH(fdc_ready_out_wait(500 /* usec */),);
+       if (in_count > 0) {
+               TRACE_CATCH(fdc_result(in_data, in_count),
+                           TRACE(ft_t_err, "result phase aborted"));
+       }
+       TRACE_EXIT 0;
+}
+
+/*      Wait for FDC interrupt with timeout (in milliseconds).
+ *      Signals are blocked so the wait will not be aborted.
+ *      Note: interrupts must be enabled ! (23/05/93 SJL)
+ */
+int fdc_interrupt_wait(unsigned int time)
+{
+       struct wait_queue wait = {current, NULL};
+       int current_blocked = current->blocked;
+       static int resetting = 0;
+       TRACE_FUN(ft_t_fdc_dma);
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
+       if (waitqueue_active(&ftape_wait_intr)) {
+               TRACE_ABORT(-EIO, ft_t_err, "error: nested call");
+       }
+#else
+       if (ftape_wait_intr) {
+               TRACE_ABORT(-EIO, ft_t_err, "error: nested call");
+       }
+#endif
+       /* timeout time will be up to USPT microseconds too long ! */
+       current->timeout = jiffies + (1000 * time + FT_USPT - 1) / FT_USPT;
+       current->state = TASK_INTERRUPTIBLE;
+       current->blocked = _BLOCK_ALL; /* isn't this already set by the
+                                       * high level routines?
+                                       */
+       add_wait_queue(&ftape_wait_intr, &wait);
+       while (!ft_interrupt_seen && current->state != TASK_RUNNING) {
+               schedule();     /* sets TASK_RUNNING on timeout */
+        }
+       current->blocked = current_blocked;     /* restore */
+       remove_wait_queue(&ftape_wait_intr, &wait);
+       /*  the following IS necessary. True: as well
+        *  wake_up_interruptible() as the schedule() set TASK_RUNNING
+        *  when they wakeup a task, BUT: it may very well be that
+        *  ft_interrupt_seen is already set to 1 when we enter here
+        *  in which case schedule() gets never called, and
+        *  TASK_RUNNING never set. This has the funny effect that we
+        *  execute all the code until we leave kernel space, but then
+        *  the task is stopped (a task CANNOT be preempted while in
+        *  kernel mode. Sending a pair of SIGSTOP/SIGCONT to the
+        *  tasks wakes it up again. Funny! :-)
+        */
+       current->state = TASK_RUNNING; 
+       if (ft_interrupt_seen) { /* woken up by interrupt */
+               current->timeout = 0;     /* interrupt hasn't cleared this */
+               ft_interrupt_seen = 0;
+               TRACE_EXIT 0;
+       }
+       /*  Original comment:
+        *  In first instance, next statement seems unnecessary since
+        *  it will be cleared in fdc_command. However, a small part of
+        *  the software seems to rely on this being cleared here
+        *  (ftape_close might fail) so stick to it until things get fixed !
+        */
+       /*  My deeply sought of knowledge:
+        *  Behold NO! It is obvious. fdc_reset() doesn't call fdc_command()
+        *  but nevertheless uses fdc_interrupt_wait(). OF COURSE this needs to
+        *  be reset here.
+        */
+       ft_interrupt_seen = 0;  /* clear for next call */
+       if (!resetting) {
+               resetting = 1;  /* break infinite recursion if reset fails */
+               TRACE(ft_t_any, "cleanup reset");
+               fdc_reset();
+               resetting = 0;
+       }
+       TRACE_EXIT (current->signal & ~current->blocked) ? -EINTR : -ETIME;
+}
+
+/*      Start/stop drive motor. Enable DMA mode.
+ */
+void fdc_motor(int motor)
+{
+       int unit = ft_drive_sel;
+       int data = unit | FDC_RESET_NOT | FDC_DMA_MODE;
+       TRACE_FUN(ft_t_any);
+
+       ftape_motor = motor;
+       if (ftape_motor) {
+               data |= FDC_MOTOR_0 << unit;
+               TRACE(ft_t_noise, "turning motor %d on", unit);
+       } else {
+               TRACE(ft_t_noise, "turning motor %d off", unit);
+       }
+       if (ft_mach2) {
+               outb_p(data, fdc.dor2);
+       } else {
+               outb_p(data, fdc.dor);
+       }
+       ftape_sleep(10 * FT_MILLISECOND);
+       TRACE_EXIT;
+}
+
+static void fdc_update_dsr(void)
+{
+       TRACE_FUN(ft_t_any);
+
+       TRACE(ft_t_flow, "rate = %d Kbps, precomp = %d ns",
+             fdc_data_rate, fdc_precomp);
+       if (fdc.type >= i82077) {
+               outb_p((fdc_rate_code & 0x03) | fdc_prec_code, fdc.dsr);
+       } else {
+               outb_p(fdc_rate_code & 0x03, fdc.ccr);
+       }
+       TRACE_EXIT;
+}
+
+void fdc_set_write_precomp(int precomp)
+{
+       TRACE_FUN(ft_t_any);
+
+       TRACE(ft_t_noise, "New precomp: %d nsec", precomp);
+       fdc_precomp = precomp;
+       /*  write precompensation can be set in multiples of 41.67 nsec.
+        *  round the parameter to the nearest multiple and convert it
+        *  into a fdc setting. Note that 0 means default to the fdc,
+        *  7 is used instead of that.
+        */
+       fdc_prec_code = ((fdc_precomp + 21) / 42) << 2;
+       if (fdc_prec_code == 0 || fdc_prec_code > (6 << 2)) {
+               fdc_prec_code = 7 << 2;
+       }
+       fdc_update_dsr();
+       TRACE_EXIT;
+}
+
+/*  Reprogram the 82078 registers to use Data Rate Table 1 on all drives.
+ */
+void fdc_set_drive_specs(void)
+{
+       __u8 cmd[] = { FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0};
+       int result;
+       TRACE_FUN(ft_t_any);
+
+       TRACE(ft_t_flow, "Setting of drive specs called");
+       if (fdc.type >= i82078_1) {
+               cmd[1] = (0 << 5) | (2 << 2);
+               cmd[2] = (1 << 5) | (2 << 2);
+               cmd[3] = (2 << 5) | (2 << 2);
+               cmd[4] = (3 << 5) | (2 << 2);
+               result = fdc_command(cmd, NR_ITEMS(cmd));
+               if (result < 0) {
+                       TRACE(ft_t_err, "Setting of drive specs failed");
+               }
+       }
+       TRACE_EXIT;
+}
+
+/* Select clock for fdc, must correspond with tape drive setting !
+ * This also influences the fdc timing so we must adjust some values.
+ */
+int fdc_set_data_rate(int rate)
+{
+       int bad_rate = 0;
+       TRACE_FUN(ft_t_any);
+
+       /* Select clock for fdc, must correspond with tape drive setting !
+        * This also influences the fdc timing so we must adjust some values.
+        */
+       TRACE(ft_t_fdc_dma, "new rate = %d", rate);
+       switch (rate) {
+       case 250:
+               fdc_rate_code = fdc_data_rate_250;
+               break;
+       case 500:
+               fdc_rate_code = fdc_data_rate_500;
+               break;
+       case 1000:
+               if (fdc.type < i82077) {
+                       bad_rate = 1;
+                } else {
+                       fdc_rate_code = fdc_data_rate_1000;
+               }
+               break;
+       case 2000:
+               if (fdc.type < i82078_1) {
+                       bad_rate = 1;
+                } else {
+                       fdc_rate_code = fdc_data_rate_2000;
+               }
+               break;
+       default:
+               bad_rate = 1;
+        }
+       if (bad_rate) {
+               TRACE_ABORT(-EIO,
+                           ft_t_fdc_dma, "%d is not a valid data rate", rate);
+       }
+       fdc_data_rate = rate;
+       fdc_update_dsr();
+       fdc_set_seek_rate(fdc_seek_rate);  /* clock changed! */
+       ftape_udelay(1000);
+       TRACE_EXIT 0;
+}
+
+/*  keep the unit select if keep_select is != 0,
+ */
+static void fdc_dor_reset(int keep_select)
+{
+       __u8 fdc_ctl = ft_drive_sel;
+
+       if (keep_select != 0) {
+               fdc_ctl |= FDC_DMA_MODE;
+               if (ftape_motor) {
+                       fdc_ctl |= FDC_MOTOR_0 << ft_drive_sel;
+               }
+       }
+       ftape_udelay(10); /* ??? but seems to be necessary */
+       if (ft_mach2) {
+               outb_p(fdc_ctl & 0x0f, fdc.dor);
+               outb_p(fdc_ctl, fdc.dor2);
+       } else {
+               outb_p(fdc_ctl, fdc.dor);
+       }
+       fdc_usec_wait(10); /* delay >= 14 fdc clocks */
+       if (keep_select == 0) {
+               fdc_ctl = 0;
+       }
+       fdc_ctl |= FDC_RESET_NOT;
+       if (ft_mach2) {
+               outb_p(fdc_ctl & 0x0f, fdc.dor);
+               outb_p(fdc_ctl, fdc.dor2);
+       } else {
+               outb_p(fdc_ctl, fdc.dor);
+       }
+}
+
+/*      Reset the floppy disk controller. Leave the ftape_unit selected.
+ */
+void fdc_reset(void)
+{
+       int st0;
+       int i;
+       int dummy;
+       unsigned long flags;
+       TRACE_FUN(ft_t_any);
+
+       save_flags(flags);
+       cli();
+
+       fdc_dor_reset(1); /* keep unit selected */
+
+       fdc_mode = fdc_idle;
+
+       /*  maybe the cli()/sti() pair is not necessary, BUT:
+        *  the following line MUST be here. Otherwise fdc_interrupt_wait()
+        *  won't wait. Note that fdc_reset() is called from 
+        *  ftape_dumb_stop() when the fdc is busy transferring data. In this
+        *  case fdc_isr() MOST PROBABLY sets ft_interrupt_seen, and tries
+        *  to get the result bytes from the fdc etc. CLASH.
+        */
+       ft_interrupt_seen = 0;
+       
+       /*  Program data rate
+        */
+       fdc_update_dsr();               /* restore data rate and precomp */
+
+       restore_flags(flags);
+
+        /*
+         *     Wait for first polling cycle to complete
+        */
+       if (fdc_interrupt_wait(1 * FT_SECOND) < 0) {
+               TRACE(ft_t_err, "no drive polling interrupt!");
+       } else {        /* clear all disk-changed statuses */
+               for (i = 0; i < 4; ++i) {
+                       if(fdc_sense_interrupt_status(&st0, &dummy) != 0) {
+                               TRACE(ft_t_err, "sense failed for %d", i);
+                       }
+                       if (i == ft_drive_sel) {
+                               ftape_current_cylinder = dummy;
+                       }
+               }
+               TRACE(ft_t_noise, "drive polling completed");
+       }
+       /*
+         *     SPECIFY COMMAND
+        */
+       fdc_set_seek_rate(fdc_seek_rate);
+       /*
+        *      DRIVE SPECIFICATION COMMAND (if fdc type known)
+        */
+       if (fdc.type >= i82078_1) {
+               fdc_set_drive_specs();
+       }
+       TRACE_EXIT;
+}
+
+#if !defined(CLK_48MHZ)
+# define CLK_48MHZ 1
+#endif
+
+/*  When we're done, put the fdc into reset mode so that the regular
+ *  floppy disk driver will figure out that something is wrong and
+ *  initialize the controller the way it wants.
+ */
+void fdc_disable(void)
+{
+       __u8 cmd1[] = {FDC_CONFIGURE, 0x00, 0x00, 0x00};
+       __u8 cmd2[] = {FDC_LOCK};
+       __u8 cmd3[] = {FDC_UNLOCK};
+       __u8 stat[1];
+       TRACE_FUN(ft_t_flow);
+
+       if (!fdc_fifo_locked) {
+               fdc_reset();
+               TRACE_EXIT;
+       }
+       if (fdc_issue_command(cmd3, 1, stat, 1) < 0 || stat[0] != 0x00) {
+               fdc_dor_reset(0);
+               TRACE_ABORT(/**/, ft_t_bug, 
+               "couldn't unlock fifo, configuration remains changed");
+       }
+       fdc_fifo_locked = 0;
+       if (CLK_48MHZ && fdc.type >= i82078) {
+               cmd1[0] |= FDC_CLK48_BIT;
+       }
+       cmd1[2] = ((fdc_fifo_state) ? 0 : 0x20) + (fdc_fifo_thr - 1);
+       if (fdc_command(cmd1, NR_ITEMS(cmd1)) < 0) {
+               fdc_dor_reset(0);
+               TRACE_ABORT(/**/, ft_t_bug,
+               "couldn't reconfigure fifo to old state");
+       }
+       if (fdc_lock_state &&
+           fdc_issue_command(cmd2, 1, stat, 1) < 0) {
+               fdc_dor_reset(0);
+               TRACE_ABORT(/**/, ft_t_bug, "couldn't lock old state again");
+       }
+       TRACE(ft_t_noise, "fifo restored: %sabled, thr. %d, %slocked",
+             fdc_fifo_state ? "en" : "dis",
+             fdc_fifo_thr, (fdc_lock_state) ? "" : "not ");
+       fdc_dor_reset(0);
+       TRACE_EXIT;
+}
+
+/*      Specify FDC seek-rate (milliseconds)
+ */
+int fdc_set_seek_rate(int seek_rate)
+{
+       /* set step rate, dma mode, and minimal head load and unload times
+        */
+       __u8 in[3] = { FDC_SPECIFY, 1, (1 << 1)};
+       fdc_seek_rate = seek_rate;
+       in[1] |= (16 - (fdc_data_rate * fdc_seek_rate) / 500) << 4;
+
+       return fdc_command(in, 3);
+}
+
+/*      Sense drive status: get unit's drive status (ST3)
+ */
+int fdc_sense_drive_status(int *st3)
+{
+       __u8 out[2];
+       __u8 in[1];
+       TRACE_FUN(ft_t_any);
+
+       out[0] = FDC_SENSED;
+       out[1] = ft_drive_sel;
+       TRACE_CATCH(fdc_issue_command(out, 2, in, 1),);
+       *st3 = in[0];
+       TRACE_EXIT 0;
+}
+
+/*      Sense Interrupt Status command:
+ *      should be issued at the end of each seek.
+ *      get ST0 and current cylinder.
+ */
+int fdc_sense_interrupt_status(int *st0, int *current_cylinder)
+{
+       __u8 out[1];
+       __u8 in[2];
+       TRACE_FUN(ft_t_any);
+
+       out[0] = FDC_SENSEI;
+       TRACE_CATCH(fdc_issue_command(out, 1, in, 2),);
+       *st0 = in[0];
+       *current_cylinder = in[1];
+       TRACE_EXIT 0;
+}
+
+/*      step to track
+ */
+int fdc_seek(int track)
+{
+       __u8 out[3];
+       int st0, pcn;
+#ifdef TESTING
+       unsigned int time;
+#endif
+       TRACE_FUN(ft_t_any);
+
+       out[0] = FDC_SEEK;
+       out[1] = ft_drive_sel;
+       out[2] = track;
+#ifdef TESTING
+       time = ftape_timestamp();
+#endif
+       /*  We really need this command to work !
+        */
+       ft_seek_completed = 0;
+       TRACE_CATCH(fdc_command(out, 3),
+                   fdc_reset();
+                   TRACE(ft_t_noise, "destination was: %d, resetting FDC...",
+                         track));
+       /*    Handle interrupts until ft_seek_completed or timeout.
+        */
+       for (;;) {
+               TRACE_CATCH(fdc_interrupt_wait(2 * FT_SECOND),);
+               if (ft_seek_completed) {
+                       TRACE_CATCH(fdc_sense_interrupt_status(&st0, &pcn),);
+                       if ((st0 & ST0_SEEK_END) == 0) {
+                               TRACE_ABORT(-EIO, ft_t_err,
+                                     "no seek-end after seek completion !??");
+                       }
+                       break;
+               }
+       }
+#ifdef TESTING
+       time = ftape_timediff(time, ftape_timestamp()) / ABS(track - ftape_current_cylinder);
+       if ((time < 900 || time > 3100) && ABS(track - ftape_current_cylinder) > 5) {
+               TRACE(ft_t_warn, "Wrong FDC STEP interval: %d usecs (%d)",
+                         time, track - ftape_current_cylinder);
+       }
+#endif
+       /*    Verify whether we issued the right tape command.
+        */
+       /* Verify that we seek to the proper track. */
+       if (pcn != track) {
+               TRACE_ABORT(-EIO, ft_t_err, "bad seek..");
+       }
+       ftape_current_cylinder = track;
+       TRACE_EXIT 0;
+}
+
+/*      Recalibrate and wait until home.
+ */
+int fdc_recalibrate(void)
+{
+       __u8 out[2];
+       int st0;
+       int pcn;
+       int retry;
+       int old_seek_rate = fdc_seek_rate;
+       TRACE_FUN(ft_t_any);
+
+       TRACE_CATCH(fdc_set_seek_rate(6),);
+       out[0] = FDC_RECAL;
+       out[1] = ft_drive_sel;
+       ft_seek_completed = 0;
+       TRACE_CATCH(fdc_command(out, 2),);
+       /*    Handle interrupts until ft_seek_completed or timeout.
+        */
+       for (retry = 0;; ++retry) {
+               TRACE_CATCH(fdc_interrupt_wait(2 * FT_SECOND),);
+               if (ft_seek_completed) {
+                       TRACE_CATCH(fdc_sense_interrupt_status(&st0, &pcn),);
+                       if ((st0 & ST0_SEEK_END) == 0) {
+                               if (retry < 1) {
+                                       continue; /* some drives/fdc's
+                                                  * give an extra interrupt
+                                                  */
+                               } else {
+                                       TRACE_ABORT(-EIO, ft_t_err,
+                                   "no seek-end after seek completion !??");
+                               }
+                       }
+                       break;
+               }
+       }
+       ftape_current_cylinder = pcn;
+       if (pcn != 0) {
+               TRACE(ft_t_err, "failed: resulting track = %d", pcn);
+       }
+       TRACE_CATCH(fdc_set_seek_rate(old_seek_rate),);
+       TRACE_EXIT 0;
+}
+
+static int perpend_mode = 0; /* set if fdc is in perpendicular mode */
+
+static int perpend_off(void)
+{
+       __u8 perpend[] = {FDC_PERPEND, 0x00};
+       TRACE_FUN(ft_t_any);
+       
+       if (perpend_mode) {
+               /* Turn off perpendicular mode */
+               perpend[1] = 0x80;
+               TRACE_CATCH(fdc_command(perpend, 2),
+                           TRACE(ft_t_err,"Perpendicular mode exit failed!"));
+               perpend_mode = 0;
+       }
+       TRACE_EXIT 0;
+}
+
+static int handle_perpend(int segment_id)
+{
+       __u8 perpend[] = {FDC_PERPEND, 0x00};
+       TRACE_FUN(ft_t_any);
+
+       /* When writing QIC-3020 tapes, turn on perpendicular mode
+        * if tape is moving in forward direction (even tracks).
+        */
+       if (ft_qic_std == QIC_TAPE_QIC3020 &&
+           ((segment_id / ft_segments_per_track) & 1) == 0) {
+/*  FIXME: some i82077 seem to support perpendicular mode as
+ *  well. 
+ */
+#if 0
+               if (fdc.type < i82077AA) {}
+#else
+               if (fdc.type < i82077 && ft_data_rate < 1000) {
+#endif
+                       /*  fdc does not support perpendicular mode: complain 
+                        */
+                       TRACE_ABORT(-EIO, ft_t_err,
+                                   "Your FDC does not support QIC-3020.");
+               }
+               perpend[1] = 0x03 /* 0x83 + (0x4 << ft_drive_sel) */ ;
+               TRACE_CATCH(fdc_command(perpend, 2),
+                          TRACE(ft_t_err,"Perpendicular mode entry failed!"));
+               TRACE(ft_t_flow, "Perpendicular mode set");
+               perpend_mode = 1;
+               TRACE_EXIT 0;
+       }
+       TRACE_EXIT perpend_off();
+}
+
+static inline void fdc_setup_dma(char mode,
+                                volatile void *addr, unsigned int count)
+{
+       /* Program the DMA controller.
+        */
+       disable_dma(fdc.dma);
+       clear_dma_ff(fdc.dma);
+       set_dma_mode(fdc.dma, mode);
+       set_dma_addr(fdc.dma, virt_to_bus((void*)addr));
+       set_dma_count(fdc.dma, count);
+#ifdef GCC_2_4_5_BUG
+       /*  This seemingly stupid construction confuses the gcc-2.4.5
+        *  code generator enough to create correct code.
+        */
+       if (1) {
+               int i;
+               
+               for (i = 0; i < 1; ++i) {
+                       ftape_udelay(1);
+               }
+       }
+#endif
+       enable_dma(fdc.dma);
+}
+
+/*  Setup fdc and dma for formatting the next segment
+ */
+int fdc_setup_formatting(buffer_struct * buff)
+{
+       unsigned long flags;
+       __u8 out[6] = {
+               FDC_FORMAT, 0x00, 3, 4 * FT_SECTORS_PER_SEGMENT, 0x00, 0x6b
+       };
+       TRACE_FUN(ft_t_any);
+       
+       TRACE_CATCH(handle_perpend(buff->segment_id),);
+       /* Program the DMA controller.
+        */
+        TRACE(ft_t_fdc_dma,
+             "phys. addr. = %lx", virt_to_bus((void*) buff->ptr));
+       save_flags(flags);
+       cli();                  /* could be called from ISR ! */
+       fdc_setup_dma(DMA_MODE_WRITE, buff->ptr, FT_SECTORS_PER_SEGMENT * 4);
+       /* Issue FDC command to start reading/writing.
+        */
+       out[1] = ft_drive_sel;
+       out[4] = buff->gap3;
+       TRACE_CATCH(fdc_setup_error = fdc_command(out, sizeof(out)),
+                   restore_flags(flags); fdc_mode = fdc_idle);
+       restore_flags(flags);
+       TRACE_EXIT 0;
+}
+
+
+/*      Setup Floppy Disk Controller and DMA to read or write the next cluster
+ *      of good sectors from or to the current segment.
+ */
+int fdc_setup_read_write(buffer_struct * buff, __u8 operation)
+{
+       unsigned long flags;
+       __u8 out[9];
+       int dma_mode;
+       TRACE_FUN(ft_t_any);
+
+       switch(operation) {
+       case FDC_VERIFY:
+               if (fdc.type < i82077) {
+                       operation = FDC_READ;
+               }
+       case FDC_READ:
+       case FDC_READ_DELETED:
+               dma_mode = DMA_MODE_READ;
+               TRACE(ft_t_fdc_dma, "xfer %d sectors to 0x%p",
+                     buff->sector_count, buff->ptr);
+               TRACE_CATCH(perpend_off(),);
+               break;
+       case FDC_WRITE_DELETED:
+               TRACE(ft_t_noise, "deleting segment %d", buff->segment_id);
+       case FDC_WRITE:
+               dma_mode = DMA_MODE_WRITE;
+               /* When writing QIC-3020 tapes, turn on perpendicular mode
+                * if tape is moving in forward direction (even tracks).
+                */
+               TRACE_CATCH(handle_perpend(buff->segment_id),);
+               TRACE(ft_t_fdc_dma, "xfer %d sectors from 0x%p",
+                     buff->sector_count, buff->ptr);
+               break;
+       default:
+               TRACE_ABORT(-EIO,
+                           ft_t_bug, "bug: illegal operation parameter");
+       }
+       TRACE(ft_t_fdc_dma, "phys. addr. = %lx",virt_to_bus((void*)buff->ptr));
+       save_flags(flags);
+       cli();                  /* could be called from ISR ! */
+       if (operation != FDC_VERIFY) {
+               fdc_setup_dma(dma_mode, buff->ptr,
+                             FT_SECTOR_SIZE * buff->sector_count);
+       }
+       /* Issue FDC command to start reading/writing.
+        */
+       out[0] = operation;
+       out[1] = ft_drive_sel;
+       out[2] = buff->cyl;
+       out[3] = buff->head;
+       out[4] = buff->sect + buff->sector_offset;
+       out[5] = 3;             /* Sector size of 1K. */
+       out[6] = out[4] + buff->sector_count - 1;       /* last sector */
+       out[7] = 109;           /* Gap length. */
+       out[8] = 0xff;          /* No limit to transfer size. */
+       TRACE(ft_t_fdc_dma, "C: 0x%02x, H: 0x%02x, R: 0x%02x, cnt: 0x%02x",
+               out[2], out[3], out[4], out[6] - out[4] + 1);
+       restore_flags(flags);
+       TRACE_CATCH(fdc_setup_error = fdc_command(out, 9),fdc_mode = fdc_idle);
+       TRACE_EXIT 0;
+}
+
+int fdc_fifo_threshold(__u8 threshold,
+                      int *fifo_state, int *lock_state, int *fifo_thr)
+{
+       const __u8 cmd0[] = {FDC_DUMPREGS};
+       __u8 cmd1[] = {FDC_CONFIGURE, 0, (0x0f & (threshold - 1)), 0};
+       const __u8 cmd2[] = {FDC_LOCK};
+       const __u8 cmd3[] = {FDC_UNLOCK};
+       __u8 reg[10];
+       __u8 stat;
+       int i;
+       int result;
+       TRACE_FUN(ft_t_any);
+
+       if (CLK_48MHZ && fdc.type >= i82078) {
+               cmd1[0] |= FDC_CLK48_BIT;
+       }
+       /*  Dump fdc internal registers for examination
+        */
+       TRACE_CATCH(fdc_command(cmd0, NR_ITEMS(cmd0)),
+                   TRACE(ft_t_warn, "dumpreg cmd failed, fifo unchanged"));
+       /*  Now read fdc internal registers from fifo
+        */
+       for (i = 0; i < (int)NR_ITEMS(reg); ++i) {
+               fdc_read(&reg[i]);
+               TRACE(ft_t_fdc_dma, "Register %d = 0x%02x", i, reg[i]);
+       }
+       if (fifo_state && lock_state && fifo_thr) {
+               *fifo_state = (reg[8] & 0x20) == 0;
+               *lock_state = reg[7] & 0x80;
+               *fifo_thr = 1 + (reg[8] & 0x0f);
+       }
+       TRACE(ft_t_noise,
+             "original fifo state: %sabled, threshold %d, %slocked",
+             ((reg[8] & 0x20) == 0) ? "en" : "dis",
+             1 + (reg[8] & 0x0f), (reg[7] & 0x80) ? "" : "not ");
+       /*  If fdc is already locked, unlock it first ! */
+       if (reg[7] & 0x80) {
+               fdc_ready_wait(100);
+               TRACE_CATCH(fdc_issue_command(cmd3, NR_ITEMS(cmd3), &stat, 1),
+                           TRACE(ft_t_bug, "FDC unlock command failed, "
+                                 "configuration unchanged"));
+       }
+       fdc_fifo_locked = 0;
+       /*  Enable fifo and set threshold at xx bytes to allow a
+        *  reasonably large latency and reduce number of dma bursts.
+        */
+       fdc_ready_wait(100);
+       if ((result = fdc_command(cmd1, NR_ITEMS(cmd1))) < 0) {
+               TRACE(ft_t_bug, "configure cmd failed, fifo unchanged");
+       }
+       /*  Now lock configuration so reset will not change it
+        */
+        if(fdc_issue_command(cmd2, NR_ITEMS(cmd2), &stat, 1) < 0 ||
+          stat != 0x10) {
+               TRACE_ABORT(-EIO, ft_t_bug,
+                           "FDC lock command failed, stat = 0x%02x", stat);
+       }
+       fdc_fifo_locked = 1;
+       TRACE_EXIT result;
+}
+
+static int fdc_fifo_enable(void)
+{
+       TRACE_FUN(ft_t_any);
+
+       if (fdc_fifo_locked) {
+               TRACE_ABORT(0, ft_t_warn, "Fifo not enabled because locked");
+       }
+       TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */,
+                                      &fdc_fifo_state,
+                                      &fdc_lock_state,
+                                      &fdc_fifo_thr),);
+       TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */,
+                                      NULL, NULL, NULL),);
+       TRACE_EXIT 0;
+}
+
+/*   Determine fd controller type 
+ */
+static __u8 fdc_save_state[2] = {0, 0};
+
+int fdc_probe(void)
+{
+       __u8 cmd[1];
+       __u8 stat[16]; /* must be able to hold dumpregs & save results */
+       int i;
+       TRACE_FUN(ft_t_any);
+
+       /*  Try to find out what kind of fd controller we have to deal with
+        *  Scheme borrowed from floppy driver:
+        *  first try if FDC_DUMPREGS command works
+        *  (this indicates that we have a 82072 or better)
+        *  then try the FDC_VERSION command (82072 doesn't support this)
+        *  then try the FDC_UNLOCK command (some older 82077's don't support this)
+        *  then try the FDC_PARTID command (82078's support this)
+        */
+       cmd[0] = FDC_DUMPREGS;
+       if (fdc_issue_command(cmd, 1, stat, 1) != 0) {
+               TRACE_ABORT(no_fdc, ft_t_bug, "No FDC found");
+       }
+       if (stat[0] == 0x80) {
+               /* invalid command: must be pre 82072 */
+               TRACE_ABORT(i8272,
+                           ft_t_warn, "Type 8272A/765A compatible FDC found");
+       }
+       fdc_result(&stat[1], 9);
+       fdc_save_state[0] = stat[7];
+       fdc_save_state[1] = stat[8];
+       cmd[0] = FDC_VERSION;
+       if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) {
+               TRACE_ABORT(i8272, ft_t_warn, "Type 82072 FDC found");
+       }
+       if (*stat != 0x90) {
+               TRACE_ABORT(i8272, ft_t_warn, "Unknown FDC found");
+       }
+       cmd[0] = FDC_UNLOCK;
+       if(fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] != 0x00) {
+               TRACE_ABORT(i8272, ft_t_warn,
+                           "Type pre-1991 82077 FDC found, "
+                           "treating it like a 82072");
+       }
+       if (fdc_save_state[0] & 0x80) { /* was locked */
+               cmd[0] = FDC_LOCK; /* restore lock */
+               (void)fdc_issue_command(cmd, 1, stat, 1);
+               TRACE(ft_t_warn, "FDC is already locked");
+       }
+       /* Test for a i82078 FDC */
+       cmd[0] = FDC_PARTID;
+       if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) {
+               /* invalid command: not a i82078xx type FDC */
+               for (i = 0; i < 4; ++i) {
+                       outb_p(i, fdc.tdr);
+                       if ((inb_p(fdc.tdr) & 0x03) != i) {
+                               TRACE_ABORT(i82077,
+                                           ft_t_warn, "Type 82077 FDC found");
+                       }
+               }
+               TRACE_ABORT(i82077AA, ft_t_warn, "Type 82077AA FDC found");
+       }
+       /* FDC_PARTID cmd succeeded */
+       switch (stat[0] >> 5) {
+       case 0x0:
+               /* i82078SL or i82078-1.  The SL part cannot run at
+                * 2Mbps (the SL and -1 dies are identical; they are
+                * speed graded after production, according to Intel).
+                * Some SL's can be detected by doing a SAVE cmd and
+                * look at bit 7 of the first byte (the SEL3V# bit).
+                * If it is 0, the part runs off 3Volts, and hence it
+                * is a SL.
+                */
+               cmd[0] = FDC_SAVE;
+               if(fdc_issue_command(cmd, 1, stat, 16) < 0) {
+                       TRACE(ft_t_err, "FDC_SAVE failed. Dunno why");
+                       /* guess we better claim the fdc to be a i82078 */
+                       TRACE_ABORT(i82078,
+                                   ft_t_warn,
+                                   "Type i82078 FDC (i suppose) found");
+               }
+               if ((stat[0] & FDC_SEL3V_BIT)) {
+                       /* fdc running off 5Volts; Pray that it's a i82078-1
+                        */
+                       TRACE_ABORT(i82078_1, ft_t_warn,
+                                 "Type i82078-1 or 5Volt i82078SL FDC found");
+               }
+               TRACE_ABORT(i82078, ft_t_warn,
+                           "Type 3Volt i82078SL FDC (1Mbps) found");
+       case 0x1:
+       case 0x2: /* S82078B  */
+               /* The '78B  isn't '78 compatible.  Detect it as a '77AA */
+               TRACE_ABORT(i82077AA, ft_t_warn, "Type i82077AA FDC found");
+       case 0x3: /* NSC PC8744 core; used in several super-IO chips */
+               TRACE_ABORT(i82077AA,
+                           ft_t_warn, "Type 82077AA compatible FDC found");
+       default:
+               TRACE(ft_t_warn, "A previously undetected FDC found");
+               TRACE_ABORT(i82077AA, ft_t_warn,
+                         "Treating it as a 82077AA. Please report partid= %d",
+                           stat[0]);
+       } /* switch(stat[ 0] >> 5) */
+       TRACE_EXIT no_fdc;
+}
+
+static int fdc_request_regions(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (ft_mach2 || ft_probe_fc10) {
+               if (check_region(fdc.sra, 8) < 0) {
+#ifndef BROKEN_FLOPPY_DRIVER
+                       TRACE_EXIT -EBUSY;
+#else
+                       TRACE(ft_t_warn,
+"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra);
+#endif
+               }
+               request_region(fdc.sra, 8, "fdc (ft)");
+       } else {
+               if (check_region(fdc.sra, 6) < 0 || 
+                   check_region(fdc.dir, 1) < 0) {
+#ifndef BROKEN_FLOPPY_DRIVER
+                       TRACE_EXIT -EBUSY;
+#else
+                       TRACE(ft_t_warn,
+"address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra);
+#endif
+               }
+               request_region(fdc.sra, 6, "fdc (ft)");
+               request_region(fdc.sra + 7, 1, "fdc (ft)");
+       }
+       TRACE_EXIT 0;
+}
+
+void fdc_release_regions(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (fdc.sra != 0) {
+               if (fdc.dor2 != 0) {
+                       release_region(fdc.sra, 8);
+               } else {
+                       release_region(fdc.sra, 6);
+                       release_region(fdc.dir, 1);
+               }
+       }
+       TRACE_EXIT;
+}
+
+static int fdc_config_regs(unsigned int fdc_base, 
+                          unsigned int fdc_irq, 
+                          unsigned int fdc_dma)
+{
+       TRACE_FUN(ft_t_flow);
+
+       fdc.irq = fdc_irq;
+       fdc.dma = fdc_dma;
+       fdc.sra = fdc_base;
+       fdc.srb = fdc_base + 1;
+       fdc.dor = fdc_base + 2;
+       fdc.tdr = fdc_base + 3;
+       fdc.msr = fdc.dsr = fdc_base + 4;
+       fdc.fifo = fdc_base + 5;
+       fdc.dir = fdc.ccr = fdc_base + 7;
+       fdc.dor2 = (ft_mach2 || ft_probe_fc10) ? fdc_base + 6 : 0;
+       TRACE_CATCH(fdc_request_regions(), fdc.sra = 0);
+       TRACE_EXIT 0;
+}
+
+static int fdc_config(void)
+{
+       static int already_done = 0;
+       TRACE_FUN(ft_t_any);
+
+       if (already_done) {
+               TRACE_CATCH(fdc_request_regions(),);
+               *(fdc.hook) = fdc_isr;  /* hook our handler in */
+               TRACE_EXIT 0;
+       }
+       if (ft_probe_fc10) {
+               int fc_type;
+               
+               TRACE_CATCH(fdc_config_regs(ft_fdc_base,
+                                           ft_fdc_irq, ft_fdc_dma),);
+               fc_type = fc10_enable();
+               if (fc_type != 0) {
+                       TRACE(ft_t_warn, "FC-%c0 controller found", '0' + fc_type);
+                       fdc.type = fc10;
+                       fdc.hook = &do_ftape;
+                       *(fdc.hook) = fdc_isr;  /* hook our handler in */
+                       already_done = 1;
+                       TRACE_EXIT 0;
+               } else {
+                       TRACE(ft_t_warn, "FC-10/20 controller not found");
+                       fdc_release_regions();
+                       fdc.type = no_fdc;
+                       ft_probe_fc10 = 0;
+                       ft_fdc_base   = 0x3f0;
+                       ft_fdc_irq    = 6;
+                       ft_fdc_dma    = 2;
+               }
+       }
+       TRACE(ft_t_warn, "fdc base: 0x%x, irq: %d, dma: %d", 
+             ft_fdc_base, ft_fdc_irq, ft_fdc_dma);
+       TRACE_CATCH(fdc_config_regs(ft_fdc_base, ft_fdc_irq, ft_fdc_dma),);
+       fdc.hook = &do_ftape;
+       *(fdc.hook) = fdc_isr;  /* hook our handler in */
+       already_done = 1;
+       TRACE_EXIT 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
+static void ftape_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+#else
+static void ftape_interrupt(int irq, struct pt_regs *regs)
+#endif
+{
+       void (*handler) (void) = *fdc.hook;
+       TRACE_FUN(ft_t_any);
+
+       *fdc.hook = NULL;
+       if (handler) {
+               handler();
+       } else {
+               TRACE(ft_t_bug, "Unexpected ftape interrupt");
+       }
+       TRACE_EXIT;
+}
+
+int fdc_grab_irq_and_dma(void)
+{
+       TRACE_FUN(ft_t_any);
+
+       if (fdc.hook == &do_ftape) {
+               /*  Get fast interrupt handler.
+                */
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
+               if (request_irq(fdc.irq, ftape_interrupt,
+                               SA_INTERRUPT, "ft", ftape_id)) {
+                       TRACE_ABORT(-EIO, ft_t_bug,
+                                   "Unable to grab IRQ%d for ftape driver",
+                                   fdc.irq);
+               }
+#else
+               if (request_irq(fdc.irq, ftape_interrupt, SA_INTERRUPT,
+                               ftape_id)) {
+                       TRACE_ABORT(-EIO, ft_t_bug,
+                                   "Unable to grab IRQ%d for ftape driver",
+                                   fdc.irq);
+               }
+#endif
+               if (request_dma(fdc.dma, ftape_id)) {
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
+                       free_irq(fdc.irq, ftape_id);
+#else
+                       free_irq(fdc.irq);
+#endif
+                       TRACE_ABORT(-EIO, ft_t_bug,
+                             "Unable to grab DMA%d for ftape driver",
+                             fdc.dma);
+               }
+               enable_irq(fdc.irq);
+       }
+       if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) {
+               /* Using same dma channel or irq as standard fdc, need
+                * to disable the dma-gate on the std fdc. This
+                * couldn't be done in the floppy driver as some
+                * laptops are using the dma-gate to enter a low power
+                * or even suspended state :-(
+                */
+               outb_p(FDC_RESET_NOT, 0x3f2);
+               TRACE(ft_t_noise, "DMA-gate on standard fdc disabled");
+       }
+       TRACE_EXIT 0;
+}
+
+int fdc_release_irq_and_dma(void)
+{
+       TRACE_FUN(ft_t_any);
+
+       if (fdc.hook == &do_ftape) {
+               disable_dma(fdc.dma);   /* just in case... */
+               free_dma(fdc.dma);
+               disable_irq(fdc.irq);
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,70)
+               free_irq(fdc.irq, ftape_id);
+#else
+                free_irq(fdc.irq);
+#endif
+       }
+       if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) {
+               /* Using same dma channel as standard fdc, need to
+                * disable the dma-gate on the std fdc. This couldn't
+                * be done in the floppy driver as some laptops are
+                * using the dma-gate to enter a low power or even
+                * suspended state :-(
+                */
+               outb_p(FDC_RESET_NOT | FDC_DMA_MODE, 0x3f2);
+               TRACE(ft_t_noise, "DMA-gate on standard fdc enabled again");
+       }
+       TRACE_EXIT 0;
+}
+
+int fdc_init(void)
+{
+       TRACE_FUN(ft_t_any);
+
+       /* find a FDC to use */
+       TRACE_CATCH(fdc_config(),);
+       TRACE_CATCH(fdc_grab_irq_and_dma(), fdc_release_regions());
+       ftape_motor = 0;
+       fdc_catch_stray_interrupts(0);  /* clear number of awainted
+                                        * stray interrupte 
+                                        */
+       fdc_catch_stray_interrupts(1);  /* one always comes (?) */
+       TRACE(ft_t_flow, "resetting fdc");
+       fdc_set_seek_rate(2);           /* use nominal QIC step rate */
+       fdc_reset();                    /* init fdc & clear track counters */
+       if (fdc.type == no_fdc) {       /* no FC-10 or FC-20 found */
+               fdc.type = fdc_probe();
+               fdc_reset();            /* update with new knowledge */
+       }
+       if (fdc.type == no_fdc) {
+               fdc_release_irq_and_dma();
+               fdc_release_regions();
+               TRACE_EXIT -ENXIO;
+       }
+       if (fdc.type >= i82077) {
+               if (fdc_fifo_enable() < 0) {
+                       TRACE(ft_t_warn, "couldn't enable fdc fifo !");
+               } else {
+                       TRACE(ft_t_flow, "fdc fifo enabled and locked");
+               }
+       }
+       TRACE_EXIT 0;
+}
diff --git a/drivers/char/ftape/lowlevel/fdc-io.h b/drivers/char/ftape/lowlevel/fdc-io.h
new file mode 100644 (file)
index 0000000..77b2f5b
--- /dev/null
@@ -0,0 +1,257 @@
+#ifndef _FDC_IO_H
+#define _FDC_IO_H
+
+/*
+ *    Copyright (C) 1993-1996 Bas Laarhoven,
+ *              (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-io.h,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:18:06 $
+ *
+ *      This file contains the declarations for the low level
+ *      functions that communicate with the floppy disk controller,
+ *      for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
+ *      Linux.
+ */
+
+#include <linux/fdreg.h>
+
+#include "../lowlevel/ftape-bsm.h"
+
+#define FDC_SK_BIT      (0x20)
+#define FDC_MT_BIT      (0x80)
+
+#define FDC_READ        (FD_READ & ~(FDC_SK_BIT | FDC_MT_BIT))
+#define FDC_WRITE       (FD_WRITE & ~FDC_MT_BIT)
+#define FDC_READ_DELETED  (0x4c)
+#define FDC_WRITE_DELETED (0x49)
+#define FDC_VERIFY        (0x56)
+#define FDC_READID      (0x4a)
+#define FDC_SENSED      (0x04)
+#define FDC_SENSEI      (FD_SENSEI)
+#define FDC_FORMAT      (FD_FORMAT)
+#define FDC_RECAL       (FD_RECALIBRATE)
+#define FDC_SEEK        (FD_SEEK)
+#define FDC_SPECIFY     (FD_SPECIFY)
+#define FDC_RECALIBR    (FD_RECALIBRATE)
+#define FDC_VERSION     (FD_VERSION)
+#define FDC_PERPEND     (FD_PERPENDICULAR)
+#define FDC_DUMPREGS    (FD_DUMPREGS)
+#define FDC_LOCK        (FD_LOCK)
+#define FDC_UNLOCK      (FD_UNLOCK)
+#define FDC_CONFIGURE   (FD_CONFIGURE)
+#define FDC_DRIVE_SPEC  (0x8e) /* i82078 has this (any others?) */
+#define FDC_PARTID      (0x18) /* i82078 has this */
+#define FDC_SAVE        (0x2e) /* i82078 has this (any others?) */
+#define FDC_RESTORE     (0x4e) /* i82078 has this (any others?) */
+
+#define FDC_STATUS_MASK (STATUS_BUSY | STATUS_DMA | STATUS_DIR | STATUS_READY)
+#define FDC_DATA_READY  (STATUS_READY)
+#define FDC_DATA_OUTPUT (STATUS_DIR)
+#define FDC_DATA_READY_MASK (STATUS_READY | STATUS_DIR)
+#define FDC_DATA_OUT_READY  (STATUS_READY | STATUS_DIR)
+#define FDC_DATA_IN_READY   (STATUS_READY)
+#define FDC_BUSY        (STATUS_BUSY)
+#define FDC_CLK48_BIT   (0x80)
+#define FDC_SEL3V_BIT   (0x40)
+
+#define ST0_INT_MASK    (ST0_INTR)
+#define FDC_INT_NORMAL  (ST0_INTR & 0x00)
+#define FDC_INT_ABNORMAL (ST0_INTR & 0x40)
+#define FDC_INT_INVALID (ST0_INTR & 0x80)
+#define FDC_INT_READYCH (ST0_INTR & 0xC0)
+#define ST0_SEEK_END    (ST0_SE)
+#define ST3_TRACK_0     (ST3_TZ)
+
+#define FDC_RESET_NOT   (0x04)
+#define FDC_DMA_MODE    (0x08)
+#define FDC_MOTOR_0     (0x10)
+#define FDC_MOTOR_1     (0x20)
+
+typedef struct {
+       void (**hook) (void);   /* our wedge into the isr */
+       enum {
+               no_fdc, i8272, i82077, i82077AA, fc10,
+               i82078, i82078_1
+       } type;                 /* FDC type */
+       unsigned int irq; /* FDC irq nr */
+       unsigned int dma; /* FDC dma channel nr */
+       __u16 sra;        /* Status register A (PS/2 only) */
+       __u16 srb;        /* Status register B (PS/2 only) */
+       __u16 dor;        /* Digital output register */
+       __u16 tdr;        /* Tape Drive Register (82077SL-1 &
+                            82078 only) */
+       __u16 msr;        /* Main Status Register */
+       __u16 dsr;        /* Datarate Select Register (8207x only) */
+       __u16 fifo;       /* Data register / Fifo on 8207x */
+       __u16 dir;        /* Digital Input Register */
+       __u16 ccr;        /* Configuration Control Register */
+       __u16 dor2;       /* Alternate dor on MACH-2 controller,
+                            also used with FC-10, meaning unknown */
+} fdc_config_info;
+
+typedef enum {
+       fdc_data_rate_250  = 2,
+       fdc_data_rate_300  = 1, /* any fdc in default configuration */
+       fdc_data_rate_500  = 0,
+       fdc_data_rate_1000 = 3,
+       fdc_data_rate_2000 = 1, /* i82078-1: when using Data Rate Table #2 */
+} fdc_data_rate_type;
+
+typedef enum {
+       fdc_idle          = 0,
+       fdc_reading_data  = FDC_READ,
+       fdc_seeking       = FDC_SEEK,
+       fdc_writing_data  = FDC_WRITE,
+       fdc_deleting      = FDC_WRITE_DELETED,
+       fdc_reading_id    = FDC_READID,
+       fdc_recalibrating = FDC_RECAL,
+       fdc_formatting    = FDC_FORMAT,
+       fdc_verifying     = FDC_VERIFY
+} fdc_mode_enum;
+
+typedef enum {
+       waiting = 0,
+       reading,
+       writing,
+       formatting,
+       verifying,
+       deleting,
+       done,
+       error,
+       mmapped,
+} buffer_state_enum;
+
+typedef struct {
+       __u8 *address;
+       volatile buffer_state_enum status;
+       volatile __u8 *ptr;
+       volatile unsigned int bytes;
+       volatile unsigned int segment_id;
+
+       /* bitmap for remainder of segment not yet handled.
+        * one bit set for each bad sector that must be skipped.
+        */
+       volatile SectorMap bad_sector_map;
+
+       /* bitmap with bad data blocks in data buffer.
+        * the errors in this map may be retried.
+        */
+       volatile SectorMap soft_error_map;
+
+       /* bitmap with bad data blocks in data buffer
+        * the errors in this map may not be retried.
+        */
+       volatile SectorMap hard_error_map;
+
+       /* retry counter for soft errors.
+        */
+       volatile int retry;
+
+       /* sectors to skip on retry ???
+        */
+       volatile unsigned int skip;
+
+       /* nr of data blocks in data buffer
+        */
+       volatile unsigned int data_offset;
+
+       /* offset in segment for first sector to be handled.
+        */
+       volatile unsigned int sector_offset;
+
+       /* size of cluster of good sectors to be handled.
+        */
+       volatile unsigned int sector_count;
+
+       /* size of remaining part of segment to be handled.
+        */
+       volatile unsigned int remaining;
+
+       /* points to next segment (contiguous) to be handled,
+        * or is zero if no read-ahead is allowed.
+        */
+       volatile unsigned int next_segment;
+
+       /* flag being set if deleted data was read.
+        */
+       volatile int deleted;
+
+       /* floppy coordinates of first sector in segment */
+       volatile __u8 head;
+       volatile __u8 cyl;
+       volatile __u8 sect;
+
+       /* gap to use when formatting */
+       __u8 gap3;
+       /* flag set when buffer is mmaped */
+       int mmapped;
+} buffer_struct;
+
+/*
+ *      fdc-io.c defined public variables
+ */
+extern volatile fdc_mode_enum fdc_mode;
+extern int fdc_setup_error;    /* outdated ??? */
+extern struct wait_queue *ftape_wait_intr;
+extern int ftape_motor;                /* fdc motor line state */
+extern volatile int ftape_current_cylinder; /* track nr FDC thinks we're on */
+extern volatile __u8 fdc_head; /* FDC head */
+extern volatile __u8 fdc_cyl;  /* FDC track */
+extern volatile __u8 fdc_sect; /* FDC sector */
+extern fdc_config_info fdc;    /* FDC hardware configuration */
+
+extern unsigned int ft_fdc_base;
+extern unsigned int ft_fdc_irq;
+extern unsigned int ft_fdc_dma;
+extern unsigned int ft_fdc_threshold;
+extern unsigned int ft_fdc_rate_limit;
+extern int ft_probe_fc10;
+extern int ft_mach2;
+/*
+ *      fdc-io.c defined public functions
+ */
+extern void fdc_catch_stray_interrupts(int count);
+extern int fdc_ready_wait(unsigned int timeout);
+extern int fdc_command(const __u8 * cmd_data, int cmd_len);
+extern int fdc_result(__u8 * res_data, int res_len);
+extern int fdc_issue_command(const __u8 * out_data, int out_count,
+                            __u8 * in_data, int in_count);
+extern int fdc_interrupt_wait(unsigned int time);
+extern int fdc_set_seek_rate(int seek_rate);
+extern int fdc_seek(int track);
+extern int fdc_sense_drive_status(int *st3);
+extern void fdc_motor(int motor);
+extern void fdc_reset(void);
+extern int fdc_recalibrate(void);
+extern void fdc_disable(void);
+extern int fdc_fifo_threshold(__u8 threshold,
+                             int *fifo_state, int *lock_state, int *fifo_thr);
+extern void fdc_wait_calibrate(void);
+extern int fdc_sense_interrupt_status(int *st0, int *current_cylinder);
+extern void fdc_save_drive_specs(void);
+extern void fdc_restore_drive_specs(void);
+extern int fdc_set_data_rate(int rate);
+extern void fdc_set_write_precomp(int precomp);
+extern int fdc_release_irq_and_dma(void);
+extern void fdc_release_regions(void);
+extern int fdc_init(void);
+extern int fdc_setup_read_write(buffer_struct * buff, __u8 operation);
+extern int fdc_setup_formatting(buffer_struct * buff);
+#endif
diff --git a/drivers/char/ftape/lowlevel/fdc-isr.c b/drivers/char/ftape/lowlevel/fdc-isr.c
new file mode 100644 (file)
index 0000000..4c3975a
--- /dev/null
@@ -0,0 +1,1185 @@
+/*
+ *      Copyright (C) 1994-1996 Bas Laarhoven,
+ *                (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-isr.c,v $
+ * $Revision: 1.9 $
+ * $Date: 1997/10/17 23:01:53 $
+ *
+ *      This file contains the interrupt service routine and
+ *      associated code for the QIC-40/80/3010/3020 floppy-tape driver
+ *      "ftape" for Linux.
+ */
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#define volatile               /* */
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-isr.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-calibr.h"
+#include "../lowlevel/ftape-bsm.h"
+
+/*      Global vars.
+ */
+volatile int ft_expected_stray_interrupts = 0;
+volatile int ft_interrupt_seen = 0;
+volatile int ft_seek_completed = 0;
+volatile int ft_hide_interrupt = 0;
+/*      Local vars.
+ */
+typedef enum {
+       no_error = 0, id_am_error = 0x01, id_crc_error = 0x02,
+       data_am_error = 0x04, data_crc_error = 0x08,
+       no_data_error = 0x10, overrun_error = 0x20,
+} error_cause;
+static int stop_read_ahead = 0;
+
+
+static void print_error_cause(int cause)
+{
+       TRACE_FUN(ft_t_any);
+
+       switch (cause) {
+       case no_data_error:
+               TRACE(ft_t_noise, "no data error");
+               break;
+       case id_am_error:
+               TRACE(ft_t_noise, "id am error");
+               break;
+       case id_crc_error:
+               TRACE(ft_t_noise, "id crc error");
+               break;
+       case data_am_error:
+               TRACE(ft_t_noise, "data am error");
+               break;
+       case data_crc_error:
+               TRACE(ft_t_noise, "data crc error");
+               break;
+       case overrun_error:
+               TRACE(ft_t_noise, "overrun error");
+               break;
+       default:
+       }
+       TRACE_EXIT;
+}
+
+static char *fdc_mode_txt(fdc_mode_enum mode)
+{
+       switch (mode) {
+       case fdc_idle:
+               return "fdc_idle";
+       case fdc_reading_data:
+               return "fdc_reading_data";
+       case fdc_seeking:
+               return "fdc_seeking";
+       case fdc_writing_data:
+               return "fdc_writing_data";
+       case fdc_reading_id:
+               return "fdc_reading_id";
+       case fdc_recalibrating:
+               return "fdc_recalibrating";
+       case fdc_formatting:
+               return "fdc_formatting";
+       case fdc_verifying:
+               return "fdc_verifying";
+       default:
+               return "unknown";
+       }
+}
+
+static inline error_cause decode_irq_cause(fdc_mode_enum mode, __u8 st[])
+{
+       error_cause cause = no_error;
+       TRACE_FUN(ft_t_any);
+
+       /*  Valid st[], decode cause of interrupt.
+        */
+       switch (st[0] & ST0_INT_MASK) {
+       case FDC_INT_NORMAL:
+               TRACE(ft_t_fdc_dma,"normal completion: %s",fdc_mode_txt(mode));
+               break;
+       case FDC_INT_ABNORMAL:
+               TRACE(ft_t_flow, "abnormal completion %s", fdc_mode_txt(mode));
+               TRACE(ft_t_fdc_dma, "ST0: 0x%02x, ST1: 0x%02x, ST2: 0x%02x",
+                     st[0], st[1], st[2]);
+               TRACE(ft_t_fdc_dma,
+                     "C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x",
+                     st[3], st[4], st[5], st[6]);
+               if (st[1] & 0x01) {
+                       if (st[2] & 0x01) {
+                               cause = data_am_error;
+                       } else {
+                               cause = id_am_error;
+                       }
+               } else if (st[1] & 0x20) {
+                       if (st[2] & 0x20) {
+                               cause = data_crc_error;
+                       } else {
+                               cause = id_crc_error;
+                       }
+               } else if (st[1] & 0x04) {
+                       cause = no_data_error;
+               } else if (st[1] & 0x10) {
+                       cause = overrun_error;
+               }
+               print_error_cause(cause);
+               break;
+       case FDC_INT_INVALID:
+               TRACE(ft_t_flow, "invalid completion %s", fdc_mode_txt(mode));
+               break;
+       case FDC_INT_READYCH:
+               if (st[0] & ST0_SEEK_END) {
+                       TRACE(ft_t_flow, "drive poll completed");
+               } else {
+                       TRACE(ft_t_flow, "ready change %s",fdc_mode_txt(mode));
+               }
+               break;
+       default:
+               break;
+       }
+       TRACE_EXIT cause;
+}
+
+static void update_history(error_cause cause)
+{
+       switch (cause) {
+       case id_am_error:
+               ft_history.id_am_errors++;
+               break;
+       case id_crc_error:
+               ft_history.id_crc_errors++;
+               break;
+       case data_am_error:
+               ft_history.data_am_errors++;
+               break;
+       case data_crc_error:
+               ft_history.data_crc_errors++;
+               break;
+       case overrun_error:
+               ft_history.overrun_errors++;
+               break;
+       case no_data_error:
+               ft_history.no_data_errors++;
+               break;
+       default:
+       }
+}
+
+static void skip_bad_sector(buffer_struct * buff)
+{
+       TRACE_FUN(ft_t_any);
+
+       /*  Mark sector as soft error and skip it
+        */
+       if (buff->remaining > 0) {
+               ++buff->sector_offset;
+               ++buff->data_offset;
+               --buff->remaining;
+               buff->ptr += FT_SECTOR_SIZE;
+               buff->bad_sector_map >>= 1;
+       } else {
+               /*  Hey, what is this????????????? C code: if we shift 
+                *  more than 31 bits, we get no shift. That's bad!!!!!!
+                */
+               ++buff->sector_offset;  /* hack for error maps */
+               TRACE(ft_t_warn, "skipping last sector in segment");
+       }
+       TRACE_EXIT;
+}
+
+static void update_error_maps(buffer_struct * buff, unsigned int error_offset)
+{
+       int hard = 0;
+       TRACE_FUN(ft_t_any);
+
+       if (buff->retry < FT_SOFT_RETRIES) {
+               buff->soft_error_map |= (1 << error_offset);
+       } else {
+               buff->hard_error_map |= (1 << error_offset);
+               buff->soft_error_map &= ~buff->hard_error_map;
+               buff->retry = -1;  /* will be set to 0 in setup_segment */
+               hard = 1;
+       }
+       TRACE(ft_t_noise, "sector %d : %s error\n"
+             KERN_INFO "hard map: 0x%08lx\n"
+             KERN_INFO "soft map: 0x%08lx",
+             FT_SECTOR(error_offset), hard ? "hard" : "soft",
+             (long) buff->hard_error_map, (long) buff->soft_error_map);
+       TRACE_EXIT;
+}
+
+static void print_progress(buffer_struct *buff, error_cause cause)
+{
+       TRACE_FUN(ft_t_any);
+
+       switch (cause) {
+       case no_error: 
+               TRACE(ft_t_flow,"%d Sector(s) transfered", buff->sector_count);
+               break;
+       case no_data_error:
+               TRACE(ft_t_flow, "Sector %d not found",
+                     FT_SECTOR(buff->sector_offset));
+               break;
+       case overrun_error:
+               /*  got an overrun error on the first byte, must be a
+                *  hardware problem
+                */
+               TRACE(ft_t_bug,
+                     "Unexpected error: failing DMA or FDC controller ?");
+               break;
+       case data_crc_error:
+               TRACE(ft_t_flow, "Error in sector %d",
+                     FT_SECTOR(buff->sector_offset - 1));
+               break;
+       case id_crc_error:
+       case id_am_error:
+       case data_am_error:
+               TRACE(ft_t_flow, "Error in sector %d",
+                     FT_SECTOR(buff->sector_offset));
+               break;
+       default:
+               TRACE(ft_t_flow, "Unexpected error at sector %d",
+                     FT_SECTOR(buff->sector_offset));
+               break;
+       }
+       TRACE_EXIT;
+}
+
+/*
+ *  Error cause:   Amount xferred:  Action:
+ *
+ *  id_am_error         0           mark bad and skip
+ *  id_crc_error        0           mark bad and skip
+ *  data_am_error       0           mark bad and skip
+ *  data_crc_error    % 1024        mark bad and skip
+ *  no_data_error       0           retry on write
+ *                                  mark bad and skip on read
+ *  overrun_error  [ 0..all-1 ]     mark bad and skip
+ *  no_error           all          continue
+ */
+
+/*  the arg `sector' is returned by the fdc and tells us at which sector we
+ *  are positioned at (relative to starting sector of segment)
+ */
+static void determine_verify_progress(buffer_struct *buff,
+                                     error_cause cause,
+                                     __u8 sector)
+{
+       TRACE_FUN(ft_t_any);
+
+       if (cause == no_error && sector == 1) {
+               buff->sector_offset = FT_SECTORS_PER_SEGMENT;
+               buff->remaining     = 0;
+               if (TRACE_LEVEL >= ft_t_flow) {
+                       print_progress(buff, cause);
+               }
+       } else {
+               buff->sector_offset = sector - buff->sect;
+               buff->remaining = FT_SECTORS_PER_SEGMENT - buff->sector_offset;
+               TRACE(ft_t_noise, "%ssector offset: 0x%04x", 
+                     (cause == no_error) ? "unexpected " : "",
+                     buff->sector_offset);
+               switch (cause) {
+               case overrun_error:
+                       break;
+#if 0
+               case no_data_error:
+                       buff->retry = FT_SOFT_RETRIES;
+                       if (buff->hard_error_map    &&
+                           buff->sector_offset > 1 &&
+                           (buff->hard_error_map & 
+                            (1 << (buff->sector_offset-2)))) {
+                               buff->retry --;
+                       }
+                       break;
+#endif
+               default:
+                       buff->retry = FT_SOFT_RETRIES;
+                       break;
+               }
+               if (TRACE_LEVEL >= ft_t_flow) {
+                       print_progress(buff, cause);
+               }
+               /*  Sector_offset points to the problem area Now adjust
+                *  sector_offset so it always points one past he failing
+                *  sector. I.e. skip the bad sector.
+                */
+               ++buff->sector_offset;
+               --buff->remaining;
+               update_error_maps(buff, buff->sector_offset - 1);
+       }
+       TRACE_EXIT;
+}
+
+static void determine_progress(buffer_struct *buff,
+                              error_cause cause,
+                              __u8 sector)
+{
+       unsigned int dma_residue;
+       TRACE_FUN(ft_t_any);
+
+       /*  Using less preferred order of disable_dma and
+        *  get_dma_residue because this seems to fail on at least one
+        *  system if reversed!
+        */
+       dma_residue = get_dma_residue(fdc.dma);
+       disable_dma(fdc.dma);
+       if (cause != no_error || dma_residue != 0) {
+               TRACE(ft_t_noise, "%sDMA residue: 0x%04x", 
+                     (cause == no_error) ? "unexpected " : "",
+                     dma_residue);
+               /* adjust to actual value: */
+               if (dma_residue == 0) {
+                       /* this happens sometimes with overrun errors.
+                        * I don't know whether we could ignore the
+                        * overrun error. Play save.
+                        */
+                       buff->sector_count --;
+               } else {
+                       buff->sector_count -= ((dma_residue + 
+                                               (FT_SECTOR_SIZE - 1)) /
+                                              FT_SECTOR_SIZE);
+               }
+       }
+       /*  Update var's influenced by the DMA operation.
+        */
+       if (buff->sector_count > 0) {
+               buff->sector_offset   += buff->sector_count;
+               buff->data_offset     += buff->sector_count;
+               buff->ptr             += (buff->sector_count *
+                                         FT_SECTOR_SIZE);
+               buff->remaining       -= buff->sector_count;
+               buff->bad_sector_map >>= buff->sector_count;
+       }
+       if (TRACE_LEVEL >= ft_t_flow) {
+               print_progress(buff, cause);
+       }
+       if (cause != no_error) {
+               if (buff->remaining == 0) {
+                       TRACE(ft_t_warn, "foo?\n"
+                             KERN_INFO "count : %d\n"
+                             KERN_INFO "offset: %d\n"
+                             KERN_INFO "soft  : %08x\n"
+                             KERN_INFO "hard  : %08x",
+                             buff->sector_count,
+                             buff->sector_offset,
+                             buff->soft_error_map,
+                             buff->hard_error_map);
+               }
+               /*  Sector_offset points to the problem area, except if we got
+                *  a data_crc_error. In that case it points one past the
+                *  failing sector.
+                *
+                *  Now adjust sector_offset so it always points one past he
+                *  failing sector. I.e. skip the bad sector.  
+                */
+               if (cause != data_crc_error) {
+                       skip_bad_sector(buff);
+               }
+               update_error_maps(buff, buff->sector_offset - 1);
+       }
+       TRACE_EXIT;
+}
+
+static int calc_steps(int cmd)
+{
+       if (ftape_current_cylinder > cmd) {
+               return ftape_current_cylinder - cmd;
+       } else {
+               return ftape_current_cylinder + cmd;
+       }
+}
+
+static void pause_tape(int retry, int mode)
+{
+       int result;
+       __u8 out[3] = {FDC_SEEK, ft_drive_sel, 0};
+       TRACE_FUN(ft_t_any);
+
+       /*  We'll use a raw seek command to get the tape to rewind and
+        *  stop for a retry.
+        */
+       ++ft_history.rewinds;
+       if (qic117_cmds[ftape_current_command].non_intr) {
+               TRACE(ft_t_warn, "motion command may be issued too soon");
+       }
+       if (retry && (mode == fdc_reading_data ||
+                     mode == fdc_reading_id   ||
+                     mode == fdc_verifying)) {
+               ftape_current_command = QIC_MICRO_STEP_PAUSE;
+               ftape_might_be_off_track = 1;
+       } else {
+               ftape_current_command = QIC_PAUSE;
+       }
+       out[2] = calc_steps(ftape_current_command);
+       result = fdc_command(out, 3); /* issue QIC_117 command */
+       ftape_current_cylinder = out[ 2];
+       if (result < 0) {
+               TRACE(ft_t_noise, "qic-pause failed, status = %d", result);
+       } else {
+               ft_location.known  = 0;
+               ft_runner_status   = idle;
+               ft_hide_interrupt     = 1;
+               ftape_tape_running = 0;
+       }
+       TRACE_EXIT;
+}
+
+static void continue_xfer(buffer_struct *buff,
+                         fdc_mode_enum mode, 
+                         unsigned int skip)
+{
+       int write = 0;
+       TRACE_FUN(ft_t_any);
+
+       if (mode == fdc_writing_data || mode == fdc_deleting) {
+               write = 1;
+       }
+       /*  This part can be removed if it never happens
+        */
+       if (skip > 0 &&
+           (ft_runner_status != running ||
+            (write && (buff->status != writing)) ||
+            (!write && (buff->status != reading && 
+                        buff->status != verifying)))) {
+               TRACE(ft_t_err, "unexpected runner/buffer state %d/%d",
+                     ft_runner_status, buff->status);
+               buff->status = error;
+               /* finish this buffer: */
+               (void)ftape_next_buffer(ft_queue_head);
+               ft_runner_status = aborting;
+               fdc_mode         = fdc_idle;
+       } else if (buff->remaining > 0 && ftape_calc_next_cluster(buff) > 0) {
+               /*  still sectors left in current segment, continue
+                *  with this segment
+                */
+               if (fdc_setup_read_write(buff, mode) < 0) {
+                       /* failed, abort operation
+                        */
+                       buff->bytes = buff->ptr - buff->address;
+                       buff->status = error;
+                       /* finish this buffer: */
+                       (void)ftape_next_buffer(ft_queue_head);
+                       ft_runner_status = aborting;
+                       fdc_mode         = fdc_idle;
+               }
+       } else {
+               /* current segment completed
+                */
+               unsigned int last_segment = buff->segment_id;
+               int eot = ((last_segment + 1) % ft_segments_per_track) == 0;
+               unsigned int next = buff->next_segment; /* 0 means stop ! */
+
+               buff->bytes = buff->ptr - buff->address;
+               buff->status = done;
+               buff = ftape_next_buffer(ft_queue_head);
+               if (eot) {
+                       /*  finished last segment on current track,
+                        *  can't continue
+                        */
+                       ft_runner_status = logical_eot;
+                       fdc_mode         = fdc_idle;
+                       TRACE_EXIT;
+               }
+               if (next <= 0) {
+                       /*  don't continue with next segment
+                        */
+                       TRACE(ft_t_noise, "no %s allowed, stopping tape",
+                             (write) ? "write next" : "read ahead");
+                       pause_tape(0, mode);
+                       ft_runner_status = idle;  /*  not quite true until
+                                                  *  next irq 
+                                                  */
+                       TRACE_EXIT;
+               }
+               /*  continue with next segment
+                */
+               if (buff->status != waiting) {
+                       TRACE(ft_t_noise, "all input buffers %s, pausing tape",
+                             (write) ? "empty" : "full");
+                       pause_tape(0, mode);
+                       ft_runner_status = idle;  /*  not quite true until
+                                                  *  next irq 
+                                                  */
+                       TRACE_EXIT;
+               }
+               if (write && next != buff->segment_id) {
+                       TRACE(ft_t_noise, 
+                             "segments out of order, aborting write");
+                       ft_runner_status = do_abort;
+                       fdc_mode         = fdc_idle;
+                       TRACE_EXIT;
+               }
+               ftape_setup_new_segment(buff, next, 0);
+               if (stop_read_ahead) {
+                       buff->next_segment = 0;
+                       stop_read_ahead = 0;
+               }
+               if (ftape_calc_next_cluster(buff) == 0 ||
+                   fdc_setup_read_write(buff, mode) != 0) {
+                       TRACE(ft_t_err, "couldn't start %s-ahead",
+                             write ? "write" : "read");
+                       ft_runner_status = do_abort;
+                       fdc_mode         = fdc_idle;
+               } else {
+                       /* keep on going */
+                       switch (ft_driver_state) {
+                       case   reading: buff->status = reading;   break;
+                       case verifying: buff->status = verifying; break;
+                       case   writing: buff->status = writing;   break;
+                       case  deleting: buff->status = deleting;  break;
+                       default:
+                               TRACE(ft_t_err, 
+                     "BUG: ft_driver_state %d should be one out of "
+                     "{reading, writing, verifying, deleting}",
+                                     ft_driver_state);
+                               buff->status = write ? writing : reading;
+                               break;
+                       }
+               }
+       }
+       TRACE_EXIT;
+}
+
+static void retry_sector(buffer_struct *buff, 
+                        int mode,
+                        unsigned int skip)
+{
+       TRACE_FUN(ft_t_any);
+
+       TRACE(ft_t_noise, "%s error, will retry",
+             (mode == fdc_writing_data || mode == fdc_deleting) ? "write" : "read");
+       pause_tape(1, mode);
+       ft_runner_status = aborting;
+       buff->status     = error;
+       buff->skip       = skip;
+       TRACE_EXIT;
+}
+
+static unsigned int find_resume_point(buffer_struct *buff)
+{
+       int i = 0;
+       SectorMap mask;
+       SectorMap map;
+       TRACE_FUN(ft_t_any);
+
+       /*  This function is to be called after all variables have been
+        *  updated to point past the failing sector.
+        *  If there are any soft errors before the failing sector,
+        *  find the first soft error and return the sector offset.
+        *  Otherwise find the last hard error.
+        *  Note: there should always be at least one hard or soft error !
+        */
+       if (buff->sector_offset < 1 || buff->sector_offset > 32) {
+               TRACE(ft_t_bug, "BUG: sector_offset = %d",
+                     buff->sector_offset);
+               TRACE_EXIT 0;
+       }
+       if (buff->sector_offset >= 32) { /* C-limitation on shift ! */
+               mask = 0xffffffff;
+       } else {
+               mask = (1 << buff->sector_offset) - 1;
+       }
+       map = buff->soft_error_map & mask;
+       if (map) {
+               while ((map & (1 << i)) == 0) {
+                       ++i;
+               }
+               TRACE(ft_t_noise, "at sector %d", FT_SECTOR(i));
+       } else {
+               map = buff->hard_error_map & mask;
+               i = buff->sector_offset - 1;
+               if (map) {
+                       while ((map & (1 << i)) == 0) {
+                               --i;
+                       }
+                       TRACE(ft_t_noise, "after sector %d", FT_SECTOR(i));
+                       ++i; /* first sector after last hard error */
+               } else {
+                       TRACE(ft_t_bug, "BUG: no soft or hard errors");
+               }
+       }
+       TRACE_EXIT i;
+}
+
+/*  check possible dma residue when formatting, update position record in
+ *  buffer struct. This is, of course, modelled after determine_progress(), but
+ *  we don't need to set up for retries because the format process cannot be
+ *  interrupted (except at the end of the tape track).
+ */
+static int determine_fmt_progress(buffer_struct *buff, error_cause cause)
+{
+       unsigned int dma_residue;
+       TRACE_FUN(ft_t_any);
+
+       /*  Using less preferred order of disable_dma and
+        *  get_dma_residue because this seems to fail on at least one
+        *  system if reversed!
+        */
+       dma_residue = get_dma_residue(fdc.dma);
+       disable_dma(fdc.dma);
+       if (cause != no_error || dma_residue != 0) {
+               TRACE(ft_t_info, "DMA residue = 0x%04x", dma_residue);
+               fdc_mode = fdc_idle;
+               switch(cause) {
+               case no_error:
+                       ft_runner_status = aborting;
+                       buff->status = idle;
+                       break;
+               case overrun_error:
+                       /*  got an overrun error on the first byte, must be a
+                        *  hardware problem 
+                        */
+                       TRACE(ft_t_bug, 
+                             "Unexpected error: failing DMA controller ?");
+                       ft_runner_status = do_abort;
+                       buff->status = error;
+                       break;
+               default:
+                       TRACE(ft_t_noise, "Unexpected error at segment %d",
+                             buff->segment_id);
+                       ft_runner_status = do_abort;
+                       buff->status = error;
+                       break;
+               }
+               TRACE_EXIT -EIO; /* can only retry entire track in format mode
+                                 */
+       }
+       /*  Update var's influenced by the DMA operation.
+        */
+       buff->ptr             += FT_SECTORS_PER_SEGMENT * 4;
+       buff->bytes           -= FT_SECTORS_PER_SEGMENT * 4;
+       buff->remaining       -= FT_SECTORS_PER_SEGMENT;
+       buff->segment_id ++; /* done with segment */
+       TRACE_EXIT 0;
+}
+
+/*
+ *  Continue formatting, switch buffers if there is no data left in
+ *  current buffer. This is, of course, modelled after
+ *  continue_xfer(), but we don't need to set up for retries because
+ *  the format process cannot be interrupted (except at the end of the
+ *  tape track).
+ */
+static void continue_formatting(buffer_struct *buff)
+{
+       TRACE_FUN(ft_t_any);
+
+       if (buff->remaining <= 0) { /*  no space left in dma buffer */
+               unsigned int next = buff->next_segment; 
+
+               if (next == 0) { /* end of tape track */
+                       buff->status     = done;
+                       ft_runner_status = logical_eot;
+                       fdc_mode         = fdc_idle;
+                       TRACE(ft_t_noise, "Done formatting track %d",
+                             ft_location.track);
+                       TRACE_EXIT;
+               }
+               /*
+                *  switch to next buffer!
+                */
+               buff->status   = done;
+               buff = ftape_next_buffer(ft_queue_head);
+
+               if (buff->status != waiting  || next != buff->segment_id) {
+                       goto format_setup_error;
+               }
+       }
+       if (fdc_setup_formatting(buff) < 0) {
+               goto format_setup_error;
+       }
+       buff->status = formatting;
+       TRACE(ft_t_fdc_dma, "Formatting segment %d on track %d",
+             buff->segment_id, ft_location.track);
+       TRACE_EXIT;
+ format_setup_error:
+       ft_runner_status = do_abort;
+       fdc_mode         = fdc_idle;
+       buff->status     = error;
+       TRACE(ft_t_err, "Error setting up for segment %d on track %d",
+             buff->segment_id, ft_location.track);
+       TRACE_EXIT;
+
+}
+
+/*  this handles writing, read id, reading and formatting
+ */
+static void handle_fdc_busy(buffer_struct *buff)
+{
+       static int no_data_error_count = 0;
+       int retry = 0;
+       error_cause cause;
+       __u8 in[7];
+       int skip;
+       fdc_mode_enum fmode = fdc_mode;
+       TRACE_FUN(ft_t_any);
+
+       if (fdc_result(in, 7) < 0) { /* better get it fast ! */
+               TRACE(ft_t_err, 
+                     "Probably fatal error during FDC Result Phase\n"
+                     KERN_INFO
+                     "drive may hang until (power on) reset :-(");
+               /*  what to do next ????
+                */
+               TRACE_EXIT;
+       }
+       cause = decode_irq_cause(fdc_mode, in);
+#ifdef TESTING
+       { int i;
+       for (i = 0; i < (int)ft_nr_buffers; ++i)
+               TRACE(ft_t_any, "buffer[%d] status: %d, segment_id: %d",
+                     i, ft_buffer[i]->status, ft_buffer[i]->segment_id);
+       }
+#endif
+       if (fmode == fdc_reading_data && ft_driver_state == verifying) {
+               fmode = fdc_verifying;
+       }
+       switch (fmode) {
+       case fdc_verifying:
+               if (ft_runner_status == aborting ||
+                   ft_runner_status == do_abort) {
+                       TRACE(ft_t_noise,"aborting %s",fdc_mode_txt(fdc_mode));
+                       break;
+               }
+               if (buff->retry > 0) {
+                       TRACE(ft_t_flow, "this is retry nr %d", buff->retry);
+               }
+               switch (cause) {
+               case no_error:
+                       no_data_error_count = 0;
+                       determine_verify_progress(buff, cause, in[5]);
+                       if (in[2] & 0x40) {
+                               /*  This should not happen when verifying
+                                */
+                               TRACE(ft_t_warn,
+                                     "deleted data in segment %d/%d",
+                                     buff->segment_id,
+                                     FT_SECTOR(buff->sector_offset - 1));
+                               buff->remaining = 0; /* abort transfer */
+                               buff->hard_error_map = EMPTY_SEGMENT;
+                               skip = 1;
+                       } else {
+                               skip = 0;
+                       }
+                       continue_xfer(buff, fdc_mode, skip);
+                       break;
+               case no_data_error:
+                       no_data_error_count ++;
+               case overrun_error:
+                       retry ++;
+               case id_am_error:
+               case id_crc_error:
+               case data_am_error:
+               case data_crc_error:
+                       determine_verify_progress(buff, cause, in[5]); 
+                       if (cause == no_data_error) {
+                               if (no_data_error_count >= 2) {
+                                       TRACE(ft_t_warn,
+                                             "retrying because of successive "
+                                             "no data errors");
+                                       no_data_error_count = 0;
+                               } else {
+                                       retry --;
+                               }
+                       } else {
+                               no_data_error_count = 0;
+                       }
+                       if (retry) {
+                               skip = find_resume_point(buff);
+                       } else {
+                               skip = buff->sector_offset;
+                       }
+                       if (retry && skip < 32) {
+                               retry_sector(buff, fdc_mode, skip);
+                       } else {
+                               continue_xfer(buff, fdc_mode, skip);
+                       }
+                       update_history(cause);
+                       break;
+               default:
+                       /*  Don't know why this could happen 
+                        *  but find out.
+                        */
+                       determine_verify_progress(buff, cause, in[5]);
+                       retry_sector(buff, fdc_mode, 0);
+                       TRACE(ft_t_err, "Error: unexpected error");
+                       break;
+               }
+               break;
+       case fdc_reading_data:
+#ifdef TESTING
+               /* I'm sorry, but: NOBODY ever used this trace
+                * messages for ages. I guess that Bas was the last person
+                * that ever really used this (thank you, between the lines)
+                */
+               if (cause == no_error) {
+                       TRACE(ft_t_flow,"reading segment %d",buff->segment_id);
+               } else {
+                       TRACE(ft_t_noise, "error reading segment %d",
+                             buff->segment_id);
+                       TRACE(ft_t_noise, "\n"
+                             KERN_INFO
+                            "IRQ:C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x\n"
+                             KERN_INFO
+                             "BUF:C: 0x%02x, H: 0x%02x, R: 0x%02x",
+                             in[3], in[4], in[5], in[6],
+                             buff->cyl, buff->head, buff->sect);
+               }
+#endif
+               if (ft_runner_status == aborting ||
+                   ft_runner_status == do_abort) {
+                       TRACE(ft_t_noise,"aborting %s",fdc_mode_txt(fdc_mode));
+                       break;
+               }
+               if (buff->bad_sector_map == FAKE_SEGMENT) {
+                       /* This condition occurs when reading a `fake'
+                        * sector that's not accessible. Doesn't
+                        * really matter as we would have ignored it
+                        * anyway !
+                        *
+                        * Chance is that we're past the next segment
+                        * now, so the next operation may fail and
+                        * result in a retry.  
+                        */
+                       buff->remaining = 0;    /* skip failing sector */
+                       /* buff->ptr       = buff->address; */
+                       /* fake success: */
+                       continue_xfer(buff, fdc_mode, 1);
+                       /*  trace calls are expensive: place them AFTER
+                        *  the real stuff has been done.
+                        *  
+                        */
+                       TRACE(ft_t_noise, "skipping empty segment %d (read), size? %d",
+                             buff->segment_id, buff->ptr - buff->address);
+                       TRACE_EXIT;
+               }
+               if (buff->retry > 0) {
+                       TRACE(ft_t_flow, "this is retry nr %d", buff->retry);
+               }
+               switch (cause) {
+               case no_error:
+                       determine_progress(buff, cause, in[5]);
+                       if (in[2] & 0x40) {
+                               /*  Handle deleted data in header segments.
+                                *  Skip segment and force read-ahead.
+                                */
+                               TRACE(ft_t_warn,
+                                     "deleted data in segment %d/%d",
+                                     buff->segment_id,
+                                     FT_SECTOR(buff->sector_offset - 1));
+                               buff->deleted = 1;
+                               buff->remaining = 0;/*abort transfer */
+                               buff->soft_error_map |=
+                                               (-1L << buff->sector_offset);
+                               if (buff->segment_id == 0) {
+                                       /* stop on next segment */
+                                       stop_read_ahead = 1;
+                               }
+                               /* force read-ahead: */
+                               buff->next_segment = 
+                                       buff->segment_id + 1;
+                               skip = (FT_SECTORS_PER_SEGMENT - 
+                                       buff->sector_offset);
+                       } else {
+                               skip = 0;
+                       }
+                       continue_xfer(buff, fdc_mode, skip);
+                       break;
+               case no_data_error:
+                       /* Tape started too far ahead of or behind the
+                        * right sector.  This may also happen in the
+                        * middle of a segment !
+                        *
+                        * Handle no-data as soft error. If next
+                        * sector fails too, a retry (with needed
+                        * reposition) will follow.
+                        */
+                       retry ++;
+               case id_am_error:
+               case id_crc_error:
+               case data_am_error:
+               case data_crc_error:
+               case overrun_error:
+                       retry += (buff->soft_error_map != 0 ||
+                                 buff->hard_error_map != 0);
+                       determine_progress(buff, cause, in[5]); 
+#if 1 || defined(TESTING)
+                       if (cause == overrun_error) retry ++;
+#endif
+                       if (retry) {
+                               skip = find_resume_point(buff);
+                       } else {
+                               skip = buff->sector_offset;
+                       }
+                       /*  Try to resume with next sector on single
+                        *  errors (let ecc correct it), but retry on
+                        *  no_data (we'll be past the target when we
+                        *  get here so we cannot retry) or on
+                        *  multiple errors (reduce chance on ecc
+                        *  failure).
+                        */
+                       /*  cH: 23/02/97: if the last sector in the 
+                        *  segment was a hard error, then there is 
+                        *  no sense in a retry. This occasion seldom
+                        *  occurs but ... @:³²¸`@%&§$
+                        */
+                       if (retry && skip < 32) {
+                               retry_sector(buff, fdc_mode, skip);
+                       } else {
+                               continue_xfer(buff, fdc_mode, skip);
+                       }
+                       update_history(cause);
+                       break;
+               default:
+                       /*  Don't know why this could happen 
+                        *  but find out.
+                        */
+                       determine_progress(buff, cause, in[5]);
+                       retry_sector(buff, fdc_mode, 0);
+                       TRACE(ft_t_err, "Error: unexpected error");
+                       break;
+               }
+               break;
+       case fdc_reading_id:
+               if (cause == no_error) {
+                       fdc_cyl = in[3];
+                       fdc_head = in[4];
+                       fdc_sect = in[5];
+                       TRACE(ft_t_fdc_dma,
+                             "id read: C: 0x%02x, H: 0x%02x, R: 0x%02x",
+                             fdc_cyl, fdc_head, fdc_sect);
+               } else {        /* no valid information, use invalid sector */
+                       fdc_cyl = fdc_head = fdc_sect = 0;
+                       TRACE(ft_t_flow, "Didn't find valid sector Id");
+               }
+               fdc_mode = fdc_idle;
+               break;
+       case fdc_deleting:
+       case fdc_writing_data:
+#ifdef TESTING
+               if (cause == no_error) {
+                       TRACE(ft_t_flow, "writing segment %d", buff->segment_id);
+               } else {
+                       TRACE(ft_t_noise, "error writing segment %d",
+                             buff->segment_id);
+               }
+#endif
+               if (ft_runner_status == aborting ||
+                   ft_runner_status == do_abort) {
+                       TRACE(ft_t_flow, "aborting %s",fdc_mode_txt(fdc_mode));
+                       break;
+               }
+               if (buff->retry > 0) {
+                       TRACE(ft_t_flow, "this is retry nr %d", buff->retry);
+               }
+               if (buff->bad_sector_map == FAKE_SEGMENT) {
+                       /* This condition occurs when trying to write to a
+                        * `fake' sector that's not accessible. Doesn't really
+                        * matter as it isn't used anyway ! Might be located
+                        * at wrong segment, then we'll fail on the next
+                        * segment.
+                        */
+                       TRACE(ft_t_noise, "skipping empty segment (write)");
+                       buff->remaining = 0;    /* skip failing sector */
+                       /* fake success: */
+                       continue_xfer(buff, fdc_mode, 1);
+                       break;
+               }
+               switch (cause) {
+               case no_error:
+                       determine_progress(buff, cause, in[5]);
+                       continue_xfer(buff, fdc_mode, 0);
+                       break;
+               case no_data_error:
+               case id_am_error:
+               case id_crc_error:
+               case data_am_error:
+               case overrun_error:
+                       update_history(cause);
+                       determine_progress(buff, cause, in[5]);
+                       skip = find_resume_point(buff);
+                       retry_sector(buff, fdc_mode, skip);
+                       break;
+               default:
+                       if (in[1] & 0x02) {
+                               TRACE(ft_t_err, "media not writable");
+                       } else {
+                               TRACE(ft_t_bug, "unforeseen write error");
+                       }
+                       fdc_mode = fdc_idle;
+                       break;
+               }
+               break; /* fdc_deleting || fdc_writing_data */
+       case fdc_formatting:
+               /*  The interrupt comes after formatting a segment. We then
+                *  have to set up QUICKLY for the next segment. But
+                *  afterwards, there is plenty of time.
+                */
+               switch (cause) {
+               case no_error:
+                       /*  would like to keep most of the formatting stuff
+                        *  outside the isr code, but timing is too critical
+                        */
+                       if (determine_fmt_progress(buff, cause) >= 0) {
+                               continue_formatting(buff);
+                       }
+                       break;
+               case no_data_error:
+               case id_am_error:
+               case id_crc_error:
+               case data_am_error:
+               case overrun_error:
+               default:
+                       determine_fmt_progress(buff, cause);
+                       update_history(cause);
+                       if (in[1] & 0x02) {
+                               TRACE(ft_t_err, "media not writable");
+                       } else {
+                               TRACE(ft_t_bug, "unforeseen write error");
+                       }
+                       break;
+               } /* cause */
+               break;
+       default:
+               TRACE(ft_t_warn, "Warning: unexpected irq during: %s",
+                     fdc_mode_txt(fdc_mode));
+               fdc_mode = fdc_idle;
+               break;
+       }
+       TRACE_EXIT;
+}
+
+/*      FDC interrupt service routine.
+ */
+void fdc_isr(void)
+{
+       static int isr_active = 0;
+#ifdef TESTING
+       unsigned int t0 = ftape_timestamp();
+#endif
+       TRACE_FUN(ft_t_any);
+
+       if (isr_active++) {
+               --isr_active;
+               TRACE(ft_t_bug, "BUG: nested interrupt, not good !");
+               *fdc.hook = fdc_isr; /*  hook our handler into the fdc
+                                     *  code again 
+                                     */
+               TRACE_EXIT;
+       }
+       sti();
+       if (inb_p(fdc.msr) & FDC_BUSY) {        /*  Entering Result Phase */
+               ft_hide_interrupt = 0;
+               handle_fdc_busy(ftape_get_buffer(ft_queue_head));
+               if (ft_runner_status == do_abort) {
+                       /*      cease operation, remember tape position
+                        */
+                       TRACE(ft_t_flow, "runner aborting");
+                       ft_runner_status = aborting;
+                       ++ft_expected_stray_interrupts;
+               }
+       } else { /* !FDC_BUSY */
+               /*  clear interrupt, cause should be gotten by issuing
+                *  a Sense Interrupt Status command.
+                */
+               if (fdc_mode == fdc_recalibrating || fdc_mode == fdc_seeking) {
+                       if (ft_hide_interrupt) {
+                               int st0;
+                               int pcn;
+
+                               if (fdc_sense_interrupt_status(&st0, &pcn) < 0)
+                                       TRACE(ft_t_err,
+                                             "sense interrupt status failed");
+                               ftape_current_cylinder = pcn;
+                               TRACE(ft_t_flow, "handled hidden interrupt");
+                       }
+                       ft_seek_completed = 1;
+                       fdc_mode = fdc_idle;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
+               } else if (!waitqueue_active(&ftape_wait_intr)) {
+#else
+               } else if (!ftape_wait_intr) {
+#endif
+                       if (ft_expected_stray_interrupts == 0) {
+                               TRACE(ft_t_warn, "unexpected stray interrupt");
+                       } else {
+                               TRACE(ft_t_flow, "expected stray interrupt");
+                               --ft_expected_stray_interrupts;
+                       }
+               } else {
+                       if (fdc_mode == fdc_reading_data ||
+                           fdc_mode == fdc_verifying    ||
+                           fdc_mode == fdc_writing_data ||
+                           fdc_mode == fdc_deleting     ||
+                           fdc_mode == fdc_formatting   ||
+                           fdc_mode == fdc_reading_id) {
+                               if (inb_p(fdc.msr) & FDC_BUSY) {
+                                       TRACE(ft_t_bug,
+                                       "***** FDC failure, busy too late");
+                               } else {
+                                       TRACE(ft_t_bug,
+                                             "***** FDC failure, no busy");
+                               }
+                       } else {
+                               TRACE(ft_t_fdc_dma, "awaited stray interrupt");
+                       }
+               }
+               ft_hide_interrupt = 0;
+       }
+       /*    Handle sleep code.
+        */
+       if (!ft_hide_interrupt) {
+               ft_interrupt_seen ++;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
+               if (waitqueue_active(&ftape_wait_intr)) {
+                       wake_up_interruptible(&ftape_wait_intr);
+               }
+#else
+               if (ftape_wait_intr) {
+                       wake_up_interruptible(&ftape_wait_intr);
+               }
+#endif
+       } else {
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,16)
+               TRACE(ft_t_flow, "hiding interrupt while %s", 
+                     waitqueue_active(&ftape_wait_intr) ? "waiting":"active");
+#else
+               TRACE(ft_t_flow, "hiding interrupt while %s", 
+                     ftape_wait_intr ? "waiting" : "active");
+#endif
+       }
+#ifdef TESTING
+       t0 = ftape_timediff(t0, ftape_timestamp());
+       if (t0 >= 1000) {
+               /* only tell us about long calls */
+               TRACE(ft_t_noise, "isr() duration: %5d usec", t0);
+       }
+#endif
+       *fdc.hook = fdc_isr;    /* hook our handler into the fdc code again */
+       --isr_active;
+       TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/lowlevel/fdc-isr.h b/drivers/char/ftape/lowlevel/fdc-isr.h
new file mode 100644 (file)
index 0000000..065aa97
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef _FDC_ISR_H
+#define _FDC_ISR_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ *           (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-isr.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:07 $
+ *
+ *      This file declares the global variables necessary to
+ *      synchronize the interrupt service routine (isr) with the
+ *      remainder of the QIC-40/80/3010/3020 floppy-tape driver
+ *      "ftape" for Linux.
+ */
+
+/*
+ *      fdc-isr.c defined public variables
+ */
+extern volatile int ft_expected_stray_interrupts; /* masks stray interrupts */
+extern volatile int ft_seek_completed;           /* flag set by isr */
+extern volatile int ft_interrupt_seen;           /* flag set by isr */
+extern volatile int ft_hide_interrupt;            /* flag set by isr */
+
+/*
+ *      fdc-io.c defined public functions
+ */
+extern void fdc_isr(void);
+
+/*
+ *      A kernel hook that steals one interrupt from the floppy
+ *      driver (Should be fixed when the new fdc driver gets ready)
+ *      See the linux kernel source files:
+ *          drivers/block/floppy.c & drivers/block/blk.h
+ *      for the details.
+ */
+extern void (*do_floppy) (void);
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-bsm.c b/drivers/char/ftape/lowlevel/ftape-bsm.c
new file mode 100644 (file)
index 0000000..a79c468
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+ *      Copyright (C) 1994-1996 Bas Laarhoven,
+ *                (C) 1996-1997 Claus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:15:15 $
+ *
+ *      This file contains the bad-sector map handling code for
+ *      the QIC-117 floppy tape driver for Linux.
+ *      QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented.
+ */
+
+#include <linux/string.h>
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+
+/*      Global vars.
+ */
+
+/*      Local vars.
+ */
+static __u8 *bad_sector_map = NULL;
+static SectorCount *bsm_hash_ptr = NULL; 
+
+typedef enum {
+       forward, backward
+} mode_type;
+
+#if 0
+/*  fix_tape converts a normal QIC-80 tape into a 'wide' tape.
+ *  For testing purposes only !
+ */
+void fix_tape(__u8 * buffer, ft_format_type new_code)
+{
+       static __u8 list[BAD_SECTOR_MAP_SIZE];
+       SectorMap *src_ptr = (SectorMap *) list;
+       __u8 *dst_ptr = bad_sector_map;
+       SectorMap map;
+       unsigned int sector = 1;
+       int i;
+
+       if (format_code != fmt_var && format_code != fmt_big) {
+               memcpy(list, bad_sector_map, sizeof(list));
+               memset(bad_sector_map, 0, sizeof(bad_sector_map));
+               while ((__u8 *) src_ptr - list < sizeof(list)) {
+                       map = *src_ptr++;
+                       if (map == EMPTY_SEGMENT) {
+                               *(SectorMap *) dst_ptr = 0x800000 + sector;
+                               dst_ptr += 3;
+                               sector += SECTORS_PER_SEGMENT;
+                       } else {
+                               for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
+                                       if (map & 1) {
+                                               *(SewctorMap *) dst_ptr = sector;
+                                               dst_ptr += 3;
+                                       }
+                                       map >>= 1;
+                                       ++sector;
+                               }
+                       }
+               }
+       }
+       bad_sector_map_changed = 1;
+       *(buffer + 4) = new_code;       /* put new format code */
+       if (format_code != fmt_var && new_code == fmt_big) {
+               PUT4(buffer, FT_6_HSEG_1,   (__u32)GET2(buffer, 6));
+               PUT4(buffer, FT_6_HSEG_2,   (__u32)GET2(buffer, 8));
+               PUT4(buffer, FT_6_FRST_SEG, (__u32)GET2(buffer, 10));
+               PUT4(buffer, FT_6_LAST_SEG, (__u32)GET2(buffer, 12));
+               memset(buffer+6, '\0', 8);
+       }
+       format_code = new_code;
+}
+
+#endif
+
+/*   given buffer that contains a header segment, find the end of
+ *   of the bsm list
+ */
+__u8 * ftape_find_end_of_bsm_list(__u8 * address)
+{
+       __u8 *ptr   = address + FT_HEADER_END; /* start of bsm list */
+       __u8 *limit = address + FT_SEGMENT_SIZE;
+       while (ptr + 2 < limit) {
+               if (ptr[0] || ptr[1] || ptr[2]) {
+                       ptr += 3;
+               } else {
+                       return ptr;
+               }
+       }
+       return NULL;
+}
+
+static inline void put_sector(SectorCount *ptr, unsigned int sector)
+{
+       ptr->bytes[0] = sector & 0xff;
+       sector >>= 8;
+       ptr->bytes[1] = sector & 0xff;
+       sector >>= 8;
+       ptr->bytes[2] = sector & 0xff;
+}
+
+static inline unsigned int get_sector(SectorCount *ptr)
+{
+#if 1
+       unsigned int sector;
+
+       sector  = ptr->bytes[0];
+       sector += ptr->bytes[1] <<  8;
+       sector += ptr->bytes[2] << 16;
+
+       return sector;
+#else
+       /*  GET4 gets the next four bytes in Intel little endian order
+        *  and converts them to host byte order and handles unaligned
+        *  access.
+        */
+       return (GET4(ptr, 0) & 0x00ffffff); /* back to host byte order */
+#endif
+}
+
+static void bsm_debug_fake(void)
+{
+       /* for testing of bad sector handling at end of tape
+        */
+#if 0
+       ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 3,
+                                  0x000003e0;
+       ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 2,
+                                  0xff3fffff;
+       ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 1,
+                                  0xffffe000;
+#endif
+       /*  Enable to test bad sector handling
+        */
+#if 0
+       ftape_put_bad_sector_entry(30, 0xfffffffe)
+       ftape_put_bad_sector_entry(32, 0x7fffffff);
+       ftape_put_bad_sector_entry(34, 0xfffeffff);
+       ftape_put_bad_sector_entry(36, 0x55555555);
+       ftape_put_bad_sector_entry(38, 0xffffffff);
+       ftape_put_bad_sector_entry(50, 0xffff0000);
+       ftape_put_bad_sector_entry(51, 0xffffffff);
+       ftape_put_bad_sector_entry(52, 0xffffffff);
+       ftape_put_bad_sector_entry(53, 0x0000ffff);
+#endif
+       /*  Enable when testing multiple volume tar dumps.
+        */
+#if 0
+       {
+               int i;
+
+               for (i = ft_first_data_segment;
+                    i <= ft_last_data_segment - 7; ++i) {
+                       ftape_put_bad_sector_entry(i, EMPTY_SEGMENT);
+               }
+       }
+#endif
+       /*  Enable when testing bit positions in *_error_map
+        */
+#if 0
+       {
+               int i;
+               
+               for (i = first_data_segment; i <= last_data_segment; ++i) {
+                       ftape_put_bad_sector_entry(i,
+                                          ftape_get_bad_sector_entry(i) 
+                                          | 0x00ff00ff);
+               }
+       }
+#endif
+}
+
+static void print_bad_sector_map(void)
+{
+       unsigned int good_sectors;
+       unsigned int total_bad = 0;
+       int i;
+       TRACE_FUN(ft_t_flow);
+
+       if (ft_format_code == fmt_big || 
+           ft_format_code == fmt_var || 
+           ft_format_code == fmt_1100ft) {
+               SectorCount *ptr = (SectorCount *)bad_sector_map;
+               unsigned int sector;
+
+               while((sector = get_sector(ptr++)) != 0) {
+                       if ((ft_format_code == fmt_big || 
+                            ft_format_code == fmt_var) &&
+                           sector & 0x800000) {
+                               total_bad += FT_SECTORS_PER_SEGMENT - 3;
+                               TRACE(ft_t_noise, "bad segment at sector: %6d",
+                                     sector & 0x7fffff);
+                       } else {
+                               ++total_bad;
+                               TRACE(ft_t_noise, "bad sector: %6d", sector);
+                       }
+               }
+               /*  Display old ftape's end-of-file marks
+                */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0)
+               while ((sector = get_unaligned(((__u16*)ptr)++)) != 0) {
+                       TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
+                             sector, get_unaligned(((__u16*)ptr)++));
+               }
+#else
+               while ((sector = *((__u16*)ptr)++) != 0) {
+                       TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
+                             sector, *((__u16*)ptr)++);
+               }
+#endif                 
+       } else { /* fixed size format */
+               for (i = ft_first_data_segment;
+                    i < (int)(ft_segments_per_track * ft_tracks_per_tape); ++i) {
+                       SectorMap map = ((SectorMap *) bad_sector_map)[i];
+
+                       if (map) {
+                               TRACE(ft_t_noise,
+                                     "bsm for segment %4d: 0x%08x", i, (unsigned int)map);
+                               total_bad += ((map == EMPTY_SEGMENT)
+                                              ? FT_SECTORS_PER_SEGMENT - 3
+                                              : count_ones(map));
+                       }
+               }
+       }
+       good_sectors =
+               ((ft_segments_per_track * ft_tracks_per_tape - ft_first_data_segment)
+                * (FT_SECTORS_PER_SEGMENT - 3)) - total_bad;
+       TRACE(ft_t_info, "%d Kb usable on this tape", good_sectors);
+       if (total_bad == 0) {
+               TRACE(ft_t_info,
+                     "WARNING: this tape has no bad blocks registered !");
+       } else {
+               TRACE(ft_t_info, "%d bad sectors", total_bad);
+       }
+       TRACE_EXIT;
+}
+
+
+void ftape_extract_bad_sector_map(__u8 * buffer)
+{
+       TRACE_FUN(ft_t_any);
+
+       /*  Fill the bad sector map with the contents of buffer.
+        */
+       if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
+               /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
+                * sector log but use this area to extend the bad sector map.
+                */
+               bad_sector_map = &buffer[FT_HEADER_END];
+       } else {
+               /* non-wide QIC-80 tapes have a failed sector log area that
+                * mustn't be included in the bad sector map.
+                */
+               bad_sector_map = &buffer[FT_FSL + FT_FSL_SIZE];
+       }
+       if (ft_format_code == fmt_1100ft || 
+           ft_format_code == fmt_var    ||
+           ft_format_code == fmt_big) {
+               bsm_hash_ptr = (SectorCount *)bad_sector_map;
+       } else {
+               bsm_hash_ptr = NULL;
+       }
+       bsm_debug_fake();
+       if (TRACE_LEVEL >= ft_t_info) {
+               print_bad_sector_map();
+       }
+       TRACE_EXIT;
+}
+
+static inline SectorMap cvt2map(unsigned int sector)
+{
+       return 1 << (((sector & 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT);
+}
+
+static inline int cvt2segment(unsigned int sector)
+{
+       return ((sector & 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT;
+}
+
+static int forward_seek_entry(int segment_id, 
+                             SectorCount **ptr, 
+                             SectorMap *map)
+{
+       unsigned int sector;
+       int segment;
+
+       do {
+               sector = get_sector((*ptr)++);
+               segment = cvt2segment(sector);
+       } while (sector != 0 && segment < segment_id);
+       (*ptr) --; /* point to first sector >= segment_id */
+       /*  Get all sectors in segment_id
+        */
+       if (sector == 0 || segment != segment_id) {
+               *map = 0;
+               return 0;
+       } else if ((sector & 0x800000) &&
+                  (ft_format_code == fmt_var || ft_format_code == fmt_big)) {
+               *map = EMPTY_SEGMENT;
+               return FT_SECTORS_PER_SEGMENT;
+       } else {
+               int count = 1;
+               SectorCount *tmp_ptr = (*ptr) + 1;
+               
+               *map = cvt2map(sector);
+               while ((sector = get_sector(tmp_ptr++)) != 0 &&
+                      (segment = cvt2segment(sector)) == segment_id) {
+                       *map |= cvt2map(sector);
+                       ++count;
+               }
+               return count;
+       }
+}
+
+static int backwards_seek_entry(int segment_id,
+                               SectorCount **ptr,
+                               SectorMap *map)
+{
+       unsigned int sector;
+       int segment; /* max unsigned int */
+
+       if (*ptr <= (SectorCount *)bad_sector_map) {
+               *map = 0;
+               return 0;
+       }
+       do {
+               sector  = get_sector(--(*ptr));
+               segment = cvt2segment(sector);
+       } while (*ptr > (SectorCount *)bad_sector_map && segment > segment_id);
+       if (segment > segment_id) { /*  at start of list, no entry found */
+               *map = 0;
+               return 0;
+       } else if (segment < segment_id) {
+               /*  before smaller entry, adjust for overshoot */
+               (*ptr) ++;
+               *map = 0;
+               return 0;
+       } else if ((sector & 0x800000) &&
+                  (ft_format_code == fmt_big || ft_format_code == fmt_var)) {
+               *map = EMPTY_SEGMENT;
+               return FT_SECTORS_PER_SEGMENT;
+       } else { /*  get all sectors in segment_id */
+               int count = 1;
+
+               *map = cvt2map(sector);
+               while(*ptr > (SectorCount *)bad_sector_map) {
+                       sector = get_sector(--(*ptr));
+                       segment = cvt2segment(sector);
+                       if (segment != segment_id) {
+                               break;
+                       }
+                       *map |= cvt2map(sector);
+                       ++count;
+               }
+               if (segment < segment_id) {
+                       (*ptr) ++;
+               }
+               return count;
+       }
+}
+
+void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map)
+{
+       SectorCount *ptr = (SectorCount *)bad_sector_map;
+       int count;
+       int new_count;
+       SectorMap map;
+       TRACE_FUN(ft_t_any);
+
+       if (ft_format_code == fmt_1100ft || 
+           ft_format_code == fmt_var || 
+           ft_format_code == fmt_big) {
+               count = forward_seek_entry(segment_id, &ptr, &map);
+               new_count = count_ones(new_map);
+               /* If format code == 4 put empty segment instead of 32
+                * bad sectors.
+                */
+               if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
+                       if (new_count == FT_SECTORS_PER_SEGMENT) {
+                               new_count = 1;
+                       }
+                       if (count == FT_SECTORS_PER_SEGMENT) {
+                               count = 1;
+                       }
+               }
+               if (count != new_count) {
+                       /* insert (or delete if < 0) new_count - count
+                        * entries.  Move trailing part of list
+                        * including terminating 0.
+                        */
+                       SectorCount *hi_ptr = ptr;
+
+                       do {
+                       } while (get_sector(hi_ptr++) != 0);
+                       /*  Note: ptr is of type byte *, and each bad sector
+                        *  consumes 3 bytes.
+                        */
+                       memmove(ptr + new_count, ptr + count,
+                               (size_t)(hi_ptr - (ptr + count))*sizeof(SectorCount));
+               }
+               TRACE(ft_t_noise, "putting map 0x%08x at %p, segment %d",
+                     (unsigned int)new_map, ptr, segment_id);
+               if (new_count == 1 && new_map == EMPTY_SEGMENT) {
+                       put_sector(ptr++, (0x800001 + 
+                                         segment_id * 
+                                         FT_SECTORS_PER_SEGMENT));
+               } else {
+                       int i = 0;
+
+                       while (new_map) {
+                               if (new_map & 1) {
+                                       put_sector(ptr++, 
+                                                  1 + segment_id * 
+                                                  FT_SECTORS_PER_SEGMENT + i);
+                               }
+                               ++i;
+                               new_map >>= 1;
+                       }
+               }
+       } else {
+               ((SectorMap *) bad_sector_map)[segment_id] = new_map;
+       }
+       TRACE_EXIT;
+}
+
+SectorMap ftape_get_bad_sector_entry(int segment_id)
+{
+       if (ft_used_header_segment == -1) {
+               /*  When reading header segment we'll need a blank map.
+                */
+               return 0;
+       } else if (bsm_hash_ptr != NULL) {
+               /*  Invariants:
+                *    map - mask value returned on last call.
+                *    bsm_hash_ptr - points to first sector greater or equal to
+                *          first sector in last_referenced segment.
+                *    last_referenced - segment id used in the last call,
+                *                      sector and map belong to this id.
+                *  This code is designed for sequential access and retries.
+                *  For true random access it may have to be redesigned.
+                */
+               static int last_reference = -1;
+               static SectorMap map = 0;
+
+               if (segment_id > last_reference) {
+                       /*  Skip all sectors before segment_id
+                        */
+                       forward_seek_entry(segment_id, &bsm_hash_ptr, &map);
+               } else if (segment_id < last_reference) {
+                       /* Skip backwards until begin of buffer or
+                        * first sector in segment_id 
+                        */
+                       backwards_seek_entry(segment_id, &bsm_hash_ptr, &map);
+               }               /* segment_id == last_reference : keep map */
+               last_reference = segment_id;
+               return map;
+       } else {
+               return ((SectorMap *) bad_sector_map)[segment_id];
+       }
+}
+
+/*  This is simply here to prevent us from overwriting other kernel
+ *  data. Writes will result in NULL Pointer dereference.
+ */
+void ftape_init_bsm(void)
+{
+       bad_sector_map = NULL;
+       bsm_hash_ptr   = NULL;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-bsm.h b/drivers/char/ftape/lowlevel/ftape-bsm.h
new file mode 100644 (file)
index 0000000..0dc7805
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef _FTAPE_BSM_H
+#define _FTAPE_BSM_H
+
+/*
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ *           (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:07 $
+ *
+ *      This file contains definitions for the bad sector map handling
+ *      routines for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/ftape.h>
+#include <linux/ftape-header-segment.h>
+
+#define EMPTY_SEGMENT           (0xffffffff)
+#define FAKE_SEGMENT            (0xfffffffe)
+
+/*  maximum (format code 4) bad sector map size (bytes).
+ */
+#define BAD_SECTOR_MAP_SIZE     (29 * SECTOR_SIZE - 256)
+
+/*  format code 4 bad sector entry, ftape uses this
+ *  internally for all format codes
+ */
+typedef __u32 SectorMap;
+/*  variable and 1100 ft bad sector map entry. These three bytes represent
+ *  a single sector address measured from BOT. 
+ */
+typedef struct NewSectorMap {          
+       __u8 bytes[3];
+} SectorCount __attribute__((packed));
+
+
+/*
+ *      ftape-bsm.c defined global vars.
+ */
+
+/*
+ *      ftape-bsm.c defined global functions.
+ */
+extern void update_bad_sector_map(__u8 * buffer);
+extern void ftape_extract_bad_sector_map(__u8 * buffer);
+extern SectorMap ftape_get_bad_sector_entry(int segment_id);
+extern void      ftape_put_bad_sector_entry(int segment_id, SectorMap mask);
+extern __u8 *ftape_find_end_of_bsm_list(__u8 * address);
+extern void ftape_init_bsm(void);
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-buffer.c b/drivers/char/ftape/lowlevel/ftape-buffer.c
new file mode 100644 (file)
index 0000000..8de9805
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ *      Copyright (C) 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-buffer.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/16 23:33:11 $
+ *
+ *  This file contains the allocator/dealloctor for ftape's dynamic dma
+ *  buffer.
+ */
+
+#include <asm/segment.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/wrapper.h>
+#include <asm/dma.h>
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-tracing.h"
+
+/*  DMA'able memory allocation stuff.
+ */
+
+/* Pure 2^n version of get_order */
+static inline int __get_order(size_t size)
+{
+       unsigned long order;
+
+       size = (size-1) >> (PAGE_SHIFT-1);
+       order = -1;
+       do {
+               size >>= 1;
+               order++;
+       } while (size);
+       return order;
+}
+
+static inline void *dmaalloc(size_t size)
+{
+       unsigned long addr;
+
+       if (size == 0) {
+               return NULL;
+       }
+       addr = __get_dma_pages(GFP_KERNEL, __get_order(size));
+       if (addr) {
+               int i;
+
+               for (i = MAP_NR(addr); i < MAP_NR(addr+size); i++) {
+                       mem_map_reserve(i);
+               }
+       }
+       return (void *)addr;
+}
+
+static inline void dmafree(void *addr, size_t size)
+{
+       if (size > 0) {
+               int i;
+
+               for (i = MAP_NR((unsigned long)addr);
+                    i < MAP_NR((unsigned long)addr+size); i++) {
+                       mem_map_unreserve (i);
+               }
+               free_pages((unsigned long) addr, __get_order(size));
+       }
+}
+
+static int add_one_buffer(void)
+{
+       TRACE_FUN(ft_t_flow);
+       
+       if (ft_nr_buffers >= FT_MAX_NR_BUFFERS) {
+               TRACE_EXIT -ENOMEM;
+       }
+       ft_buffer[ft_nr_buffers] = kmalloc(sizeof(buffer_struct), GFP_KERNEL);
+       if (ft_buffer[ft_nr_buffers] == NULL) {
+               TRACE_EXIT -ENOMEM;
+       }
+       memset(ft_buffer[ft_nr_buffers], 0, sizeof(buffer_struct));
+       ft_buffer[ft_nr_buffers]->address = dmaalloc(FT_BUFF_SIZE);
+       if (ft_buffer[ft_nr_buffers]->address == NULL) {
+               kfree(ft_buffer[ft_nr_buffers]);
+               ft_buffer[ft_nr_buffers] = NULL;
+               TRACE_EXIT -ENOMEM;
+       }
+       ft_nr_buffers ++;
+       TRACE(ft_t_info, "buffer nr #%d @ %p, dma area @ %p",
+             ft_nr_buffers,
+             ft_buffer[ft_nr_buffers-1],
+             ft_buffer[ft_nr_buffers-1]->address);
+       TRACE_EXIT 0;
+}
+
+static void del_one_buffer(void)
+{
+       TRACE_FUN(ft_t_flow);
+       if (ft_nr_buffers > 0) {
+               TRACE(ft_t_info, "releasing buffer nr #%d @ %p, dma area @ %p",
+                     ft_nr_buffers,
+                     ft_buffer[ft_nr_buffers-1],
+                     ft_buffer[ft_nr_buffers-1]->address);
+               ft_nr_buffers --;
+               dmafree(ft_buffer[ft_nr_buffers]->address, FT_BUFF_SIZE);
+               kfree(ft_buffer[ft_nr_buffers]);
+               ft_buffer[ft_nr_buffers] = NULL;
+       }
+       TRACE_EXIT;
+}
+
+int ftape_set_nr_buffers(int cnt)
+{
+       int delta = cnt - ft_nr_buffers;
+       TRACE_FUN(ft_t_flow);
+
+       if (delta > 0) {
+               while (delta--) {
+                       if (add_one_buffer() < 0) {
+                               TRACE_EXIT -ENOMEM;
+                       }
+               }
+       } else if (delta < 0) {
+               while (delta++) {
+                       del_one_buffer();
+               }
+       }
+       ftape_zap_read_buffers();
+       TRACE_EXIT 0;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-buffer.h b/drivers/char/ftape/lowlevel/ftape-buffer.h
new file mode 100644 (file)
index 0000000..eec99ce
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _FTAPE_BUFFER_H
+#define _FTAPE_BUFFER_H
+
+/*
+ *      Copyright (C) 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-buffer.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:08 $
+ *
+ *  This file contains the allocator/dealloctor for ftape's dynamic dma
+ *  buffer.
+ */
+
+extern int  ftape_set_nr_buffers(int cnt);
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-calibr.c b/drivers/char/ftape/lowlevel/ftape-calibr.c
new file mode 100644 (file)
index 0000000..77ee01e
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ *      Copyright (C) 1993-1996 Bas Laarhoven.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-calibr.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:08 $
+ *
+ *      GP calibration routine for processor speed dependent
+ *      functions.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#if defined(__alpha__)
+# include <asm/hwrpb.h>
+#elif defined(__i386__)
+# include <linux/timex.h>
+#endif
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-calibr.h"
+#include "../lowlevel/fdc-io.h"
+
+#undef DEBUG
+
+#if !defined(__alpha__) && !defined(__i386__)
+# error Ftape is not implemented for this architecture!
+#endif
+
+#if defined(__alpha__)
+static unsigned long ps_per_cycle = 0;
+#endif
+
+/*
+ * Note: On Intel PCs, the clock ticks at 100 Hz (HZ==100) which is
+ * too slow for certain timeouts (and that clock doesn't even tick
+ * when interrupts are disabled).  For that reason, the 8254 timer is
+ * used directly to implement fine-grained timeouts.  However, on
+ * Alpha PCs, the 8254 is *not* used to implement the clock tick
+ * (which is 1024 Hz, normally) and the 8254 timer runs at some
+ * "random" frequency (it seems to run at 18Hz, but its not safe to
+ * rely on this value).  Instead, we use the Alpha's "rpcc"
+ * instruction to read cycle counts.  As this is a 32 bit counter,
+ * it will overflow only once per 30 seconds (on a 200MHz machine),
+ * which is plenty.
+ */
+
+unsigned int ftape_timestamp(void)
+{
+#if defined(__alpha__)
+       unsigned long r;
+
+       asm volatile ("rpcc %0" : "=r" (r));
+       return r;
+#elif defined(__i386__)
+       unsigned long flags;
+       __u16 lo;
+       __u16 hi;
+
+       save_flags(flags);
+       cli();
+       outb_p(0x00, 0x43);     /* latch the count ASAP */
+       lo = inb_p(0x40);       /* read the latched count */
+       lo |= inb(0x40) << 8;
+       hi = jiffies;
+       restore_flags(flags);
+       return ((hi + 1) * (unsigned int) LATCH) - lo;  /* downcounter ! */
+#endif
+}
+
+static unsigned int short_ftape_timestamp(void)
+{
+#if defined(__alpha__)
+       return ftape_timestamp();
+#elif defined(__i386__)
+       unsigned int count;
+       unsigned long flags;
+       save_flags(flags);
+       cli();
+       outb_p(0x00, 0x43);     /* latch the count ASAP */
+       count = inb_p(0x40);    /* read the latched count */
+       count |= inb(0x40) << 8;
+       restore_flags(flags);
+       return (LATCH - count); /* normal: downcounter */
+#endif
+}
+
+static unsigned int diff(unsigned int t0, unsigned int t1)
+{
+#if defined(__alpha__)
+       return (t1 <= t0) ? t1 + (1UL << 32) - t0 : t1 - t0;
+#elif defined(__i386__)
+       /*
+        * This is tricky: to work for both short and full ftape_timestamps
+        * we'll have to discriminate between these.
+        * If it _looks_ like short stamps with wrapping around we'll
+        * asume it are. This will generate a small error if it really
+        * was a (very large) delta from full ftape_timestamps.
+        */
+       return (t1 <= t0 && t0 <= LATCH) ? t1 + LATCH - t0 : t1 - t0;
+#endif
+}
+
+static unsigned int usecs(unsigned int count)
+{
+#if defined(__alpha__)
+       return (ps_per_cycle * count) / 1000000UL;
+#elif defined(__i386__)
+       return (10000 * count) / ((CLOCK_TICK_RATE + 50) / 100);
+#endif
+}
+
+unsigned int ftape_timediff(unsigned int t0, unsigned int t1)
+{
+       /*
+        *  Calculate difference in usec for ftape_timestamp results t0 & t1.
+        *  Note that on the i386 platform with short time-stamps, the
+        *  maximum allowed timespan is 1/HZ or we'll lose ticks!
+        */
+       return usecs(diff(t0, t1));
+}
+
+/*      To get an indication of the I/O performance,
+ *      measure the duration of the inb() function.
+ */
+static void time_inb(void)
+{
+       int i;
+       int t0, t1;
+       unsigned long flags;
+       int status;
+       TRACE_FUN(ft_t_any);
+
+       save_flags(flags);
+       cli();
+       t0 = short_ftape_timestamp();
+       for (i = 0; i < 1000; ++i) {
+               status = inb(fdc.msr);
+       }
+       t1 = short_ftape_timestamp();
+       restore_flags(flags);
+       TRACE(ft_t_info, "inb() duration: %d nsec", ftape_timediff(t0, t1));
+       TRACE_EXIT;
+}
+
+static void init_clock(void)
+{
+#if defined(__i386__)
+       unsigned int t;
+       int i;
+       TRACE_FUN(ft_t_any);
+
+       /*  Haven't studied on why, but there sometimes is a problem
+        *  with the tick timer readout. The two bytes get swapped.
+        *  This hack solves that problem by doing one extra input.
+        */
+       for (i = 0; i < 1000; ++i) {
+               t = short_ftape_timestamp();
+               if (t > LATCH) {
+                       inb_p(0x40);    /* get in sync again */
+                       TRACE(ft_t_warn, "clock counter fixed");
+                       break;
+               }
+       }
+#elif defined(__alpha__)
+#if CONFIG_FT_ALPHA_CLOCK == 0
+#error You must define and set CONFIG_FT_ALPHA_CLOCK in the Makefile !
+#endif
+       extern struct hwrpb_struct *hwrpb;
+       TRACE_FUN(ft_t_any);
+
+       if (hwrpb->cycle_freq != 0) {
+               ps_per_cycle = (1000*1000*1000*1000UL) / hwrpb->cycle_freq;
+       } else {
+               /*
+                * HELP:  Linux 2.0.x doesn't set cycle_freq on my noname !
+                */
+               ps_per_cycle = (1000*1000*1000*1000UL) / CONFIG_FT_ALPHA_CLOCK;
+       }
+#endif
+       TRACE_EXIT;
+}
+
+/*
+ *      Input:  function taking int count as parameter.
+ *              pointers to calculated calibration variables.
+ */
+void ftape_calibrate(char *name,
+                   void (*fun) (unsigned int), 
+                   unsigned int *calibr_count, 
+                   unsigned int *calibr_time)
+{
+       static int first_time = 1;
+       int i;
+       unsigned int tc = 0;
+       unsigned int count;
+       unsigned int time;
+#if defined(__i386__)
+       unsigned int old_tc = 0;
+       unsigned int old_count = 1;
+       unsigned int old_time = 1;
+#endif
+       TRACE_FUN(ft_t_flow);
+
+       if (first_time) {             /* get idea of I/O performance */
+               init_clock();
+               time_inb();
+               first_time = 0;
+       }
+       /*    value of timeout must be set so that on very slow systems
+        *    it will give a time less than one jiffy, and on
+        *    very fast systems it'll give reasonable precision.
+        */
+
+       count = 40;
+       for (i = 0; i < 15; ++i) {
+               unsigned int t0;
+               unsigned int t1;
+               unsigned int once;
+               unsigned int multiple;
+               unsigned long flags;
+
+               *calibr_count =
+               *calibr_time = count;   /* set TC to 1 */
+               save_flags(flags);
+               cli();
+               fun(0);         /* dummy, get code into cache */
+               t0 = short_ftape_timestamp();
+               fun(0);         /* overhead + one test */
+               t1 = short_ftape_timestamp();
+               once = diff(t0, t1);
+               t0 = short_ftape_timestamp();
+               fun(count);             /* overhead + count tests */
+               t1 = short_ftape_timestamp();
+               multiple = diff(t0, t1);
+               restore_flags(flags);
+               time = ftape_timediff(0, multiple - once);
+               tc = (1000 * time) / (count - 1);
+               TRACE(ft_t_any, "once:%3d us,%6d times:%6d us, TC:%5d ns",
+                       usecs(once), count - 1, usecs(multiple), tc);
+#if defined(__alpha__)
+               /*
+                * Increase the calibration count exponentially until the
+                * calibration time exceeds 100 ms.
+                */
+               if (time >= 100*1000) {
+                       break;
+               }
+#elif defined(__i386__)
+               /*
+                * increase the count until the resulting time nears 2/HZ,
+                * then the tc will drop sharply because we lose LATCH counts.
+                */
+               if (tc <= old_tc / 2) {
+                       time = old_time;
+                       count = old_count;
+                       break;
+               }
+               old_tc = tc;
+               old_count = count;
+               old_time = time;
+#endif
+               count *= 2;
+       }
+       *calibr_count = count - 1;
+       *calibr_time  = time;
+       TRACE(ft_t_info, "TC for `%s()' = %d nsec (at %d counts)",
+            name, (1000 * *calibr_time) / *calibr_count, *calibr_count);
+       TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-calibr.h b/drivers/char/ftape/lowlevel/ftape-calibr.h
new file mode 100644 (file)
index 0000000..0c7e752
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _FTAPE_CALIBR_H
+#define _FTAPE_CALIBR_H
+
+/*
+ *      Copyright (C) 1993-1996 Bas Laarhoven.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-calibr.h,v $
+ * $Revision: 1.1 $
+ * $Date: 1997/09/19 09:05:26 $
+ *
+ *      This file contains a gp calibration routine for
+ *      hardware dependent timeout functions.
+ */
+
+extern void ftape_calibrate(char *name,
+                           void (*fun) (unsigned int),
+                           unsigned int *calibr_count,
+                           unsigned int *calibr_time);
+extern unsigned int ftape_timestamp(void);
+extern unsigned int ftape_timediff(unsigned int t0, unsigned int t1);
+
+#endif /* _FTAPE_CALIBR_H */
diff --git a/drivers/char/ftape/lowlevel/ftape-ctl.c b/drivers/char/ftape/lowlevel/ftape-ctl.c
new file mode 100644 (file)
index 0000000..1ce4c28
--- /dev/null
@@ -0,0 +1,901 @@
+/*
+ *      Copyright (C) 1993-1996 Bas Laarhoven,
+ *                    1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ctl.c,v $
+ * $Revision: 1.4 $
+ * $Date: 1997/11/11 14:37:44 $
+ *
+ *      This file contains the non-read/write ftape functions for the
+ *      QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/wrapper.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+#include <asm/io.h>
+
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-bsm.h"
+
+/*      Global vars.
+ */
+ftape_info ftape_status = {
+/*  vendor information */
+       { 0, },     /* drive type */
+/*  data rates */
+       500,        /* used data rate */
+       500,        /* drive max rate */
+       500,        /* fdc max rate   */
+/*  drive selection, either FTAPE_SEL_A/B/C/D */
+       -1,     /* drive selection */
+/*  flags set after decode the drive and tape status   */
+       0,          /* formatted */
+       1,          /* no tape */
+       1,          /* write protected */
+       1,          /* new tape */
+/*  values of last queried drive/tape status and error */
+       {{0,}},     /* last error code */
+       {{0,}},     /* drive status, configuration, tape status */
+/*  cartridge geometry */
+        20,         /* tracks_per_tape */
+        102,        /* segments_per_track */
+/*  location of header segments, etc. */
+       -1,     /* used_header_segment */
+       -1,     /* header_segment_1 */
+       -1,     /* header_segment_2 */
+       -1,     /* first_data_segment */
+        -1,     /* last_data_segment */
+/*  the format code as stored in the header segment  */
+       fmt_normal, /* format code */
+/*  the default for the qic std: unknown */
+       -1,
+/*  is tape running? */
+       idle,       /* runner_state */
+/*  is tape reading/writing/verifying/formatting/deleting */
+       idle,       /* driver state */
+/*  flags fatal hardware error */
+       1,          /* failure */
+/*  history record */
+       { 0, }      /* history record */
+};
+       
+int ftape_segments_per_head     = 1020;
+int ftape_segments_per_cylinder = 4;
+int ftape_init_drive_needed = 1; /* need to be global for ftape_reset_drive()
+                                 * in ftape-io.c
+                                 */
+
+/*      Local vars.
+ */
+static const vendor_struct vendors[] = QIC117_VENDORS;
+static const wakeup_method methods[] = WAKEUP_METHODS;
+
+const ftape_info *ftape_get_status(void)
+{
+#if defined(STATUS_PARANOYA)
+       static ftape_info get_status;
+
+       get_status = ftape_status;
+       return &get_status;
+#else
+       return &ftape_status; /*  maybe return only a copy of it to assure 
+                              *  read only access
+                              */
+#endif
+}
+
+void ftape_set_status(const ftape_info *status)
+{
+       ftape_status = *status;
+}
+
+static int ftape_not_operational(int status)
+{
+       /* return true if status indicates tape can not be used.
+        */
+       return ((status ^ QIC_STATUS_CARTRIDGE_PRESENT) &
+               (QIC_STATUS_ERROR |
+                QIC_STATUS_CARTRIDGE_PRESENT |
+                QIC_STATUS_NEW_CARTRIDGE));
+}
+
+int ftape_seek_to_eot(void)
+{
+       int status;
+       TRACE_FUN(ft_t_any);
+
+       TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),);
+       while ((status & QIC_STATUS_AT_EOT) == 0) {
+               if (ftape_not_operational(status)) {
+                       TRACE_EXIT -EIO;
+               }
+               TRACE_CATCH(ftape_command_wait(QIC_PHYSICAL_FORWARD,
+                                              ftape_timeout.rewind,&status),);
+       }
+       TRACE_EXIT 0;
+}
+
+int ftape_seek_to_bot(void)
+{
+       int status;
+       TRACE_FUN(ft_t_any);
+
+       TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),);
+       while ((status & QIC_STATUS_AT_BOT) == 0) {
+               if (ftape_not_operational(status)) {
+                       TRACE_EXIT -EIO;
+               }
+               TRACE_CATCH(ftape_command_wait(QIC_PHYSICAL_REVERSE,
+                                              ftape_timeout.rewind,&status),);
+       }
+       TRACE_EXIT 0;
+}
+
+static int ftape_new_cartridge(void)
+{
+       ft_location.track = -1; /* force seek on first access */
+       ftape_zap_read_buffers();
+       ftape_zap_write_buffers();
+       return 0;
+}
+
+int ftape_abort_operation(void)
+{
+       int result = 0;
+       int status;
+       TRACE_FUN(ft_t_flow);
+
+       if (ft_runner_status == running) {
+               TRACE(ft_t_noise, "aborting runner, waiting");
+               
+               ft_runner_status = do_abort;
+               /* set timeout so that the tape will run to logical EOT
+                * if we missed the last sector and there are no queue pulses.
+                */
+               result = ftape_dumb_stop();
+       }
+       if (ft_runner_status != idle) {
+               if (ft_runner_status == do_abort) {
+                       TRACE(ft_t_noise, "forcing runner abort");
+               }
+               TRACE(ft_t_noise, "stopping tape");
+               result = ftape_stop_tape(&status);
+               ft_location.known = 0;
+               ft_runner_status  = idle;
+       }
+       ftape_reset_buffer();
+       ftape_zap_read_buffers();
+       ftape_set_state(idle);
+       TRACE_EXIT result;
+}
+
+static int lookup_vendor_id(unsigned int vendor_id)
+{
+       int i = 0;
+
+       while (vendors[i].vendor_id != vendor_id) {
+               if (++i >= NR_ITEMS(vendors)) {
+                       return -1;
+               }
+       }
+       return i;
+}
+
+void ftape_detach_drive(void)
+{
+       TRACE_FUN(ft_t_any);
+
+       TRACE(ft_t_flow, "disabling tape drive and fdc");
+       ftape_put_drive_to_sleep(ft_drive_type.wake_up);
+       fdc_catch_stray_interrupts(1);  /* one always comes */
+       fdc_disable();
+       fdc_release_irq_and_dma();
+       fdc_release_regions();
+       TRACE_EXIT;
+}
+
+static void clear_history(void)
+{
+       ft_history.used = 0;
+       ft_history.id_am_errors =
+               ft_history.id_crc_errors =
+               ft_history.data_am_errors =
+               ft_history.data_crc_errors =
+               ft_history.overrun_errors =
+               ft_history.no_data_errors =
+               ft_history.retries =
+               ft_history.crc_errors =
+               ft_history.crc_failures =
+               ft_history.ecc_failures =
+               ft_history.corrected =
+               ft_history.defects =
+               ft_history.rewinds = 0;
+}
+
+int ftape_activate_drive(vendor_struct * drive_type)
+{
+       int result = 0;
+       TRACE_FUN(ft_t_flow);
+
+       /* If we already know the drive type, wake it up.
+        * Else try to find out what kind of drive is attached.
+        */
+       if (drive_type->wake_up != unknown_wake_up) {
+               TRACE(ft_t_flow, "enabling tape drive and fdc");
+               result = ftape_wakeup_drive(drive_type->wake_up);
+               if (result < 0) {
+                       TRACE(ft_t_err, "known wakeup method failed");
+               }
+       } else {
+               wake_up_types method;
+               const ft_trace_t old_tracing = TRACE_LEVEL;
+               if (TRACE_LEVEL < ft_t_flow) {
+                       SET_TRACE_LEVEL(ft_t_bug);
+               }
+
+               /*  Try to awaken the drive using all known methods.
+                *  Lower tracing for a while.
+                */
+               for (method=no_wake_up; method < NR_ITEMS(methods); ++method) {
+                       drive_type->wake_up = method;
+#ifdef CONFIG_FT_TWO_DRIVES
+                       /*  Test setup for dual drive configuration.
+                        *  /dev/rft2 uses mountain wakeup
+                        *  /dev/rft3 uses colorado wakeup
+                        *  Other systems will use the normal scheme.
+                        */
+                       if ((ft_drive_sel < 2)                            ||
+                           (ft_drive_sel == 2 && method == FT_WAKE_UP_1) ||
+                           (ft_drive_sel == 3 && method == FT_WAKE_UP_2)) {
+                               result=ftape_wakeup_drive(drive_type->wake_up);
+                       } else {
+                               result = -EIO;
+                       }
+#else
+                       result = ftape_wakeup_drive(drive_type->wake_up);
+#endif
+                       if (result >= 0) {
+                               TRACE(ft_t_warn, "drive wakeup method: %s",
+                                     methods[drive_type->wake_up].name);
+                               break;
+                       }
+               }
+               SET_TRACE_LEVEL(old_tracing);
+
+               if (method >= NR_ITEMS(methods)) {
+                       /* no response at all, cannot open this drive */
+                       drive_type->wake_up = unknown_wake_up;
+                       TRACE(ft_t_err, "no tape drive found !");
+                       result = -ENODEV;
+               }
+       }
+       TRACE_EXIT result;
+}
+
+int ftape_get_drive_status(void)
+{
+       int result;
+       int status;
+       TRACE_FUN(ft_t_flow);
+
+       ft_no_tape = ft_write_protected = 0;
+       /*    Tape drive is activated now.
+        *    First clear error status if present.
+        */
+       do {
+               result = ftape_ready_wait(ftape_timeout.reset, &status);
+               if (result < 0) {
+                       if (result == -ETIME) {
+                               TRACE(ft_t_err, "ftape_ready_wait timeout");
+                       } else if (result == -EINTR) {
+                               TRACE(ft_t_err, "ftape_ready_wait aborted");
+                       } else {
+                               TRACE(ft_t_err, "ftape_ready_wait failed");
+                       }
+                       TRACE_EXIT -EIO;
+               }
+               /*  Clear error condition (drive is ready !)
+                */
+               if (status & QIC_STATUS_ERROR) {
+                       unsigned int error;
+                       qic117_cmd_t command;
+
+                       TRACE(ft_t_err, "error status set");
+                       result = ftape_report_error(&error, &command, 1);
+                       if (result < 0) {
+                               TRACE(ft_t_err,
+                                     "report_error_code failed: %d", result);
+                               /* hope it's working next time */
+                               ftape_reset_drive();
+                               TRACE_EXIT -EIO;
+                       } else if (error != 0) {
+                               TRACE(ft_t_noise, "error code   : %d", error);
+                               TRACE(ft_t_noise, "error command: %d", command);
+                       }
+               }
+               if (status & QIC_STATUS_NEW_CARTRIDGE) {
+                       unsigned int error;
+                       qic117_cmd_t command;
+                       const ft_trace_t old_tracing = TRACE_LEVEL;
+                       SET_TRACE_LEVEL(ft_t_bug);
+
+                       /*  Undocumented feature: Must clear (not present!)
+                        *  error here or we'll fail later.
+                        */
+                       ftape_report_error(&error, &command, 1);
+
+                       SET_TRACE_LEVEL(old_tracing);
+                       TRACE(ft_t_info, "status: new cartridge");
+                       ft_new_tape = 1;
+               } else {
+                       ft_new_tape = 0;
+               }
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+       } while (status & QIC_STATUS_ERROR);
+       
+       ft_no_tape = !(status & QIC_STATUS_CARTRIDGE_PRESENT);
+       ft_write_protected = (status & QIC_STATUS_WRITE_PROTECT) != 0;
+       if (ft_no_tape) {
+               TRACE(ft_t_warn, "no cartridge present");
+       } else {
+               if (ft_write_protected) {
+                       TRACE(ft_t_noise, "Write protected cartridge");
+               }
+       }
+       TRACE_EXIT 0;
+}
+
+void ftape_log_vendor_id(void)
+{
+       int vendor_index;
+       TRACE_FUN(ft_t_flow);
+
+       ftape_report_vendor_id(&ft_drive_type.vendor_id);
+       vendor_index = lookup_vendor_id(ft_drive_type.vendor_id);
+       if (ft_drive_type.vendor_id == UNKNOWN_VENDOR &&
+           ft_drive_type.wake_up == wake_up_colorado) {
+               vendor_index = 0;
+               /* hack to get rid of all this mail */
+               ft_drive_type.vendor_id = 0;
+       }
+       if (vendor_index < 0) {
+               /* Unknown vendor id, first time opening device.  The
+                * drive_type remains set to type found at wakeup
+                * time, this will probably keep the driver operating
+                * for this new vendor.  
+                */
+               TRACE(ft_t_warn, "\n"
+                     KERN_INFO "============ unknown vendor id ===========\n"
+                     KERN_INFO "A new, yet unsupported tape drive is found\n"
+                     KERN_INFO "Please report the following values:\n"
+                     KERN_INFO "   Vendor id     : 0x%04x\n"
+                     KERN_INFO "   Wakeup method : %s\n"
+                     KERN_INFO "And a description of your tape drive\n"
+                     KERN_INFO "to "THE_FTAPE_MAINTAINER"\n"
+                     KERN_INFO "==========================================",
+                     ft_drive_type.vendor_id,
+                     methods[ft_drive_type.wake_up].name);
+               ft_drive_type.speed = 0;                /* unknown */
+       } else {
+               ft_drive_type.name  = vendors[vendor_index].name;
+               ft_drive_type.speed = vendors[vendor_index].speed;
+               TRACE(ft_t_info, "tape drive type: %s", ft_drive_type.name);
+               /* scan all methods for this vendor_id in table */
+               while(ft_drive_type.wake_up != vendors[vendor_index].wake_up) {
+                       if (vendor_index < NR_ITEMS(vendors) - 1 &&
+                           vendors[vendor_index + 1].vendor_id 
+                           == 
+                           ft_drive_type.vendor_id) {
+                               ++vendor_index;
+                       } else {
+                               break;
+                       }
+               }
+               if (ft_drive_type.wake_up != vendors[vendor_index].wake_up) {
+                       TRACE(ft_t_warn, "\n"
+                    KERN_INFO "==========================================\n"
+                    KERN_INFO "wakeup type mismatch:\n"
+                    KERN_INFO "found: %s, expected: %s\n"
+                    KERN_INFO "please report this to "THE_FTAPE_MAINTAINER"\n"
+                    KERN_INFO "==========================================",
+                             methods[ft_drive_type.wake_up].name,
+                             methods[vendors[vendor_index].wake_up].name);
+               }
+       }
+       TRACE_EXIT;
+}
+
+void ftape_calc_timeouts(unsigned int qic_std,
+                        unsigned int data_rate,
+                        unsigned int tape_len)
+{
+       int speed;              /* deci-ips ! */
+       int ff_speed;
+       int length;
+       TRACE_FUN(ft_t_any);
+
+       /*                           tape transport speed
+        *  data rate:        QIC-40   QIC-80   QIC-3010 QIC-3020
+        *
+        *    250 Kbps        25 ips     n/a      n/a      n/a
+        *    500 Kbps        50 ips   34 ips   22.6 ips   n/a
+        *      1 Mbps          n/a    68 ips   45.2 ips 22.6 ips
+        *      2 Mbps          n/a      n/a      n/a    45.2 ips
+        *
+        *  fast tape transport speed is at least 68 ips.
+        */
+       switch (qic_std) {
+       case QIC_TAPE_QIC40:
+               speed = (data_rate == 250) ? 250 : 500;
+               break;
+       case QIC_TAPE_QIC80:
+               speed = (data_rate == 500) ? 340 : 680;
+               break;
+       case QIC_TAPE_QIC3010:
+               speed = (data_rate == 500) ? 226 : 452;
+               break;
+       case QIC_TAPE_QIC3020:
+               speed = (data_rate == 1000) ? 226 : 452;
+               break;
+       default:
+               TRACE(ft_t_bug, "Unknown qic_std (bug) ?");
+               speed = 500;
+               break;
+       }
+       if (ft_drive_type.speed == 0) {
+               unsigned long t0;
+               static int dt = 0;     /* keep gcc from complaining */
+               static int first_time = 1;
+
+               /*  Measure the time it takes to wind to EOT and back to BOT.
+                *  If the tape length is known, calculate the rewind speed.
+                *  Else keep the time value for calculation of the rewind
+                *  speed later on, when the length _is_ known.
+                *  Ask for a report only when length and speed are both known.
+                */
+               if (first_time) {
+                       ftape_seek_to_bot();
+                       t0 = jiffies;
+                       ftape_seek_to_eot();
+                       ftape_seek_to_bot();
+                       dt = (int) (((jiffies - t0) * FT_USPT) / 1000);
+                       if (dt < 1) {
+                               dt = 1; /* prevent div by zero on failures */
+                       }
+                       first_time = 0;
+                       TRACE(ft_t_info,
+                             "trying to determine seek timeout, got %d msec",
+                             dt);
+               }
+               if (tape_len != 0) {
+                       ft_drive_type.speed = 
+                               (2 * 12 * tape_len * 1000) / dt;
+                       TRACE(ft_t_warn, "\n"
+                    KERN_INFO "==========================================\n"
+                    KERN_INFO "drive type: %s\n"
+                    KERN_INFO "delta time = %d ms, length = %d ft\n"
+                    KERN_INFO "has a maximum tape speed of %d ips\n"
+                    KERN_INFO "please report this to "THE_FTAPE_MAINTAINER"\n"
+                    KERN_INFO "==========================================",
+                             ft_drive_type.name, dt, tape_len, 
+                             ft_drive_type.speed);
+               }
+       }
+       /*  Handle unknown length tapes as very long ones. We'll
+        *  determine the actual length from a header segment later.
+        *  This is normal for all modern (Wide,TR1/2/3) formats.
+        */
+       if (tape_len <= 0) {
+               TRACE(ft_t_noise,
+                     "Unknown tape length, using maximal timeouts");
+               length = QIC_TOP_TAPE_LEN;      /* use worst case values */
+       } else {
+               length = tape_len;              /* use actual values */
+       }
+       if (ft_drive_type.speed == 0) {
+               ff_speed = speed; 
+       } else {
+               ff_speed = ft_drive_type.speed;
+       }
+       /*  time to go from bot to eot at normal speed (data rate):
+        *  time = (1+delta) * length (ft) * 12 (inch/ft) / speed (ips)
+        *  delta = 10 % for seek speed, 20 % for rewind speed.
+        */
+       ftape_timeout.seek = (length * 132 * FT_SECOND) / speed;
+       ftape_timeout.rewind = (length * 144 * FT_SECOND) / (10 * ff_speed);
+       ftape_timeout.reset = 20 * FT_SECOND + ftape_timeout.rewind;
+       TRACE(ft_t_noise, "timeouts for speed = %d, length = %d\n"
+             KERN_INFO "seek timeout  : %d sec\n"
+             KERN_INFO "rewind timeout: %d sec\n"
+             KERN_INFO "reset timeout : %d sec",
+             speed, length,
+             (ftape_timeout.seek + 500) / 1000,
+             (ftape_timeout.rewind + 500) / 1000,
+             (ftape_timeout.reset + 500) / 1000);
+       TRACE_EXIT;
+}
+
+/* This function calibrates the datarate (i.e. determines the maximal
+ * usable data rate) and sets the global variable ft_qic_std to qic_std
+ *
+ */
+int ftape_calibrate_data_rate(unsigned int qic_std)
+{
+       int rate = ft_fdc_rate_limit;
+       int result;
+       TRACE_FUN(ft_t_flow);
+
+       ft_qic_std = qic_std;
+
+       if (ft_qic_std == -1) {
+               TRACE_ABORT(-EIO, ft_t_err,
+               "Unable to determine data rate if QIC standard is unknown");
+       }
+
+       /*  Select highest rate supported by both fdc and drive.
+        *  Start with highest rate supported by the fdc.
+        */
+       while (fdc_set_data_rate(rate) < 0 && rate > 250) {
+               rate /= 2;
+       }
+       TRACE(ft_t_info,
+             "Highest FDC supported data rate: %d Kbps", rate);
+       ft_fdc_max_rate = rate;
+       do {
+               result = ftape_set_data_rate(rate, ft_qic_std);
+       } while (result == -EINVAL && (rate /= 2) > 250);
+       if (result < 0) {
+               TRACE_ABORT(-EIO, ft_t_err, "set datarate failed");
+       }
+       ft_data_rate = rate;
+       TRACE_EXIT 0;
+}
+
+int ftape_init_drive(void)
+{
+       int status;
+       qic_model model;
+       unsigned int qic_std;
+       unsigned int data_rate;
+       TRACE_FUN(ft_t_flow);
+
+       ftape_init_drive_needed = 0; /* don't retry if this fails ? */
+       TRACE_CATCH(ftape_report_raw_drive_status(&status),);
+       if (status & QIC_STATUS_CARTRIDGE_PRESENT) {
+               if (!(status & QIC_STATUS_AT_BOT)) {
+                       /*  Antique drives will get here after a soft reset,
+                        *  modern ones only if the driver is loaded when the
+                        *  tape wasn't rewound properly.
+                        */
+                       /* Tape should be at bot if new cartridge ! */
+                       ftape_seek_to_bot();
+               }
+               if (!(status & QIC_STATUS_REFERENCED)) {
+                       TRACE(ft_t_flow, "starting seek_load_point");
+                       TRACE_CATCH(ftape_command_wait(QIC_SEEK_LOAD_POINT,
+                                                      ftape_timeout.reset,
+                                                      &status),);
+               }
+       }
+       ft_formatted = (status & QIC_STATUS_REFERENCED) != 0;
+       if (!ft_formatted) {
+               TRACE(ft_t_warn, "Warning: tape is not formatted !");
+       }
+
+       /*  report configuration aborts when ftape_tape_len == -1
+        *  unknown qic_std is okay if not formatted.
+        */
+       TRACE_CATCH(ftape_report_configuration(&model,
+                                              &data_rate,
+                                              &qic_std,
+                                              &ftape_tape_len),);
+
+       /*  Maybe add the following to the /proc entry
+        */
+       TRACE(ft_t_info, "%s drive @ %d Kbps",
+             (model == prehistoric) ? "prehistoric" :
+             ((model == pre_qic117c) ? "pre QIC-117C" :
+              ((model == post_qic117b) ? "post QIC-117B" :
+               "post QIC-117D")), data_rate);
+
+       if (ft_formatted) {
+               /*  initialize ft_used_data_rate to maximum value 
+                *  and set ft_qic_std
+                */
+               TRACE_CATCH(ftape_calibrate_data_rate(qic_std),);
+               if (ftape_tape_len == 0) {
+                       TRACE(ft_t_info, "unknown length QIC-%s tape",
+                             (ft_qic_std == QIC_TAPE_QIC40) ? "40" :
+                             ((ft_qic_std == QIC_TAPE_QIC80) ? "80" :
+                              ((ft_qic_std == QIC_TAPE_QIC3010) 
+                               ? "3010" : "3020")));
+               } else {
+                       TRACE(ft_t_info, "%d ft. QIC-%s tape", ftape_tape_len,
+                             (ft_qic_std == QIC_TAPE_QIC40) ? "40" :
+                             ((ft_qic_std == QIC_TAPE_QIC80) ? "80" :
+                              ((ft_qic_std == QIC_TAPE_QIC3010)
+                               ? "3010" : "3020")));
+               }
+               ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len);
+               /* soft write-protect QIC-40/QIC-80 cartridges used with a
+                * Colorado T3000 drive. Buggy hardware!
+                */
+               if ((ft_drive_type.vendor_id == 0x011c6) &&
+                   ((ft_qic_std == QIC_TAPE_QIC40 ||
+                     ft_qic_std == QIC_TAPE_QIC80) &&
+                    !ft_write_protected)) {
+                       TRACE(ft_t_warn, "\n"
+       KERN_INFO "The famous Colorado T3000 bug:\n"
+       KERN_INFO "%s drives can't write QIC40 and QIC80\n"
+       KERN_INFO "cartridges but don't set the write-protect flag!",
+                             ft_drive_type.name);
+                       ft_write_protected = 1;
+               }
+       } else {
+               /*  Doesn't make too much sense to set the data rate
+                *  because we don't know what to use for the write
+                *  precompensation.
+                *  Need to do this again when formatting the cartridge.
+                */
+               ft_data_rate = data_rate;
+               ftape_calc_timeouts(QIC_TAPE_QIC40,
+                                   data_rate,
+                                   ftape_tape_len);
+       }
+       ftape_new_cartridge();
+       TRACE_EXIT 0;
+}
+
+static void ftape_munmap(void)
+{
+       int i;
+       TRACE_FUN(ft_t_flow);
+       
+       for (i = 0; i < ft_nr_buffers; i++) {
+               ft_buffer[i]->mmapped = 0;
+       }
+       TRACE_EXIT;
+}
+
+/*   Map the dma buffers into the virtual address range given by vma.
+ *   We only check the caller doesn't map non-existent buffers. We
+ *   don't check for multiple mappings.
+ */
+int ftape_mmap(struct vm_area_struct *vma)
+{
+       int num_buffers;
+       int i;
+       TRACE_FUN(ft_t_flow);
+       
+       if (ft_failure) {
+               TRACE_EXIT -ENODEV;
+       }
+       if ((vma_get_flags(vma) & (VM_READ|VM_WRITE)) == 0) {
+               TRACE_ABORT(-EINVAL, ft_t_err, "Undefined mmap() access");
+       }
+       if (vma_get_offset (vma) != 0) {
+               TRACE_ABORT(-EINVAL, ft_t_err, "offset must be 0");
+       }
+       if ((vma_get_end (vma) - vma_get_start (vma)) % FT_BUFF_SIZE != 0) {
+               TRACE_ABORT(-EINVAL, ft_t_err,
+                           "size = %ld, should be a multiple of %d",
+                           vma_get_end (vma) - vma_get_start (vma),
+                           FT_BUFF_SIZE);
+       }
+       num_buffers = (vma_get_end (vma) - vma_get_start (vma)) / FT_BUFF_SIZE;
+       if (num_buffers > ft_nr_buffers) {
+               TRACE_ABORT(-EINVAL,
+                           ft_t_err, "size = %ld, should be less than %d",
+                           vma_get_end (vma) - vma_get_start (vma),
+                           ft_nr_buffers * FT_BUFF_SIZE);
+       }
+       if (ft_driver_state != idle) {
+               /* this also clears the buffer states 
+                */
+               ftape_abort_operation();
+       } else {
+               ftape_reset_buffer();
+       }
+       for (i = 0; i < num_buffers; i++) {
+               TRACE_CATCH(remap_page_range(vma_get_start (vma) +
+                                            i * FT_BUFF_SIZE,
+                                       virt_to_phys(ft_buffer[i]->address),
+                                            FT_BUFF_SIZE,
+                                            vma_get_page_prot (vma)),
+                           _res = -EAGAIN);
+               TRACE(ft_t_noise, "remapped dma buffer @ %p to location @ %p",
+                     ft_buffer[i]->address,
+                     (void *)(vma_get_start(vma) + i * FT_BUFF_SIZE));
+       }
+       for (i = 0; i < num_buffers; i++) {
+               memset(ft_buffer[i]->address, 0xAA, FT_BUFF_SIZE);
+               ft_buffer[i]->mmapped++;
+       }       
+       TRACE_EXIT 0;
+}
+
+static void ftape_init_driver(void); /* forward declaration */
+
+/*      OPEN routine called by kernel-interface code
+ */
+int ftape_enable(int drive_selection)
+{
+       TRACE_FUN(ft_t_any);
+
+       if (ft_drive_sel == -1 || ft_drive_sel != drive_selection) {
+               /* Other selection than last time
+                */
+               ftape_init_driver();
+       }
+       ft_drive_sel = FTAPE_SEL(drive_selection);
+       ft_failure = 0;
+       TRACE_CATCH(fdc_init(),); /* init & detect fdc */
+       TRACE_CATCH(ftape_activate_drive(&ft_drive_type),
+                   fdc_disable();
+                   fdc_release_irq_and_dma();
+                   fdc_release_regions());
+       TRACE_CATCH(ftape_get_drive_status(), ftape_detach_drive());
+       if (ft_drive_type.vendor_id == UNKNOWN_VENDOR) {
+               ftape_log_vendor_id();
+       }
+       if (ft_new_tape) {
+               ftape_init_drive_needed = 1;
+       }
+       if (!ft_no_tape && ftape_init_drive_needed) {
+               TRACE_CATCH(ftape_init_drive(), ftape_detach_drive());
+       }
+       ftape_munmap(); /* clear the mmap flag */
+       clear_history();
+       TRACE_EXIT 0;
+}
+
+/*   release routine called by the high level interface modules
+ *   zftape or sftape.
+ */
+void ftape_disable(void)
+{
+       int i;
+       TRACE_FUN(ft_t_any);
+
+       for (i = 0; i < ft_nr_buffers; i++) {
+               if (ft_buffer[i]->mmapped) {
+                       TRACE(ft_t_noise, "first byte of buffer %d: 0x%02x",
+                             i, *ft_buffer[i]->address);
+               }
+       }
+       if ((current->signal & _DONT_BLOCK) && 
+           !(current->signal & _NEVER_BLOCK) &&
+           ftape_tape_running) {
+               TRACE(ft_t_warn,
+                     "Interrupted by fatal signal and tape still running");
+               ftape_dumb_stop();
+               ftape_abort_operation(); /* it's annoying */
+       } else {
+               ftape_set_state(idle);
+       }
+       ftape_detach_drive();
+       if (ft_history.used) {
+               TRACE(ft_t_info, "== Non-fatal errors this run: ==");
+               TRACE(ft_t_info, "fdc isr statistics:\n"
+                     KERN_INFO " id_am_errors     : %3d\n"
+                     KERN_INFO " id_crc_errors    : %3d\n"
+                     KERN_INFO " data_am_errors   : %3d\n"
+                     KERN_INFO " data_crc_errors  : %3d\n"
+                     KERN_INFO " overrun_errors   : %3d\n"
+                     KERN_INFO " no_data_errors   : %3d\n"
+                     KERN_INFO " retries          : %3d",
+                     ft_history.id_am_errors,   ft_history.id_crc_errors,
+                     ft_history.data_am_errors, ft_history.data_crc_errors,
+                     ft_history.overrun_errors, ft_history.no_data_errors,
+                     ft_history.retries);
+               if (ft_history.used & 1) {
+                       TRACE(ft_t_info, "ecc statistics:\n"
+                             KERN_INFO " crc_errors       : %3d\n"
+                             KERN_INFO " crc_failures     : %3d\n"
+                             KERN_INFO " ecc_failures     : %3d\n"
+                             KERN_INFO " sectors corrected: %3d",
+                             ft_history.crc_errors,   ft_history.crc_failures,
+                             ft_history.ecc_failures, ft_history.corrected);
+               }
+               if (ft_history.defects > 0) {
+                       TRACE(ft_t_warn, "Warning: %d media defects!",
+                             ft_history.defects);
+               }
+               if (ft_history.rewinds > 0) {
+                       TRACE(ft_t_info, "tape motion statistics:\n"
+                             KERN_INFO "repositions       : %3d",
+                             ft_history.rewinds);
+               }
+       }
+       ft_failure = 1;
+       TRACE_EXIT;
+}
+
+static void ftape_init_driver(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       ft_drive_type.vendor_id = UNKNOWN_VENDOR;
+       ft_drive_type.speed     = 0;
+       ft_drive_type.wake_up   = unknown_wake_up;
+       ft_drive_type.name      = "Unknown";
+
+       ftape_timeout.seek      = 650 * FT_SECOND;
+       ftape_timeout.reset     = 670 * FT_SECOND;
+       ftape_timeout.rewind    = 650 * FT_SECOND;
+       ftape_timeout.head_seek =  15 * FT_SECOND;
+       ftape_timeout.stop      =   5 * FT_SECOND;
+       ftape_timeout.pause     =  16 * FT_SECOND;
+
+       ft_qic_std             = -1;
+       ftape_tape_len         = 0;  /* unknown */
+       ftape_current_command  = 0;
+       ftape_current_cylinder = -1;
+
+       ft_segments_per_track       = 102;
+       ftape_segments_per_head     = 1020;
+       ftape_segments_per_cylinder = 4;
+       ft_tracks_per_tape          = 20;
+
+       ft_failure = 1;
+
+       ft_formatted       = 0;
+       ft_no_tape         = 1;
+       ft_write_protected = 1;
+       ft_new_tape        = 1;
+
+       ft_driver_state = idle;
+
+       ft_data_rate = 
+               ft_fdc_max_rate   = 500;
+       ft_drive_max_rate = 0; /* triggers set_rate_test() */
+
+       ftape_init_drive_needed = 1;
+
+       ft_header_segment_1    = -1;
+       ft_header_segment_2    = -1;
+       ft_used_header_segment = -1;
+       ft_first_data_segment  = -1;
+       ft_last_data_segment   = -1;
+
+       ft_location.track = -1;
+       ft_location.known = 0;
+
+       ftape_tape_running = 0;
+       ftape_might_be_off_track = 1;
+
+       ftape_new_cartridge();  /* init some tape related variables */
+       ftape_init_bsm();
+       TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-ctl.h b/drivers/char/ftape/lowlevel/ftape-ctl.h
new file mode 100644 (file)
index 0000000..ab7c535
--- /dev/null
@@ -0,0 +1,172 @@
+#ifndef _FTAPE_CTL_H
+#define _FTAPE_CTL_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ *           (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ctl.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:09 $
+ *
+ *      This file contains the non-standard IOCTL related definitions
+ *      for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
+ *      Linux.
+ */
+
+#include <linux/ioctl.h>
+#include <linux/mtio.h>
+#include <linux/ftape-vendors.h>
+
+#include "../lowlevel/ftape-rw.h"
+#include <linux/ftape-header-segment.h>
+
+typedef struct {
+       int used;               /* any reading or writing done */
+       /* isr statistics */
+       unsigned int id_am_errors;      /* id address mark not found */
+       unsigned int id_crc_errors;     /* crc error in id address mark */
+       unsigned int data_am_errors;    /* data address mark not found */
+       unsigned int data_crc_errors;   /* crc error in data field */
+       unsigned int overrun_errors;    /* fdc access timing problem */
+       unsigned int no_data_errors;    /* sector not found */
+       unsigned int retries;   /* number of tape retries */
+       /* ecc statistics */
+       unsigned int crc_errors;        /* crc error in data */
+       unsigned int crc_failures;      /* bad data without crc error */
+       unsigned int ecc_failures;      /* failed to correct */
+       unsigned int corrected; /* total sectors corrected */
+       /* general statistics */
+       unsigned int rewinds;   /* number of tape rewinds */
+       unsigned int defects;   /* bad sectors due to media defects */
+} history_record;
+
+/* this structure contains * ALL * information that we want
+ * our child modules to know about, but don't want them to
+ * modify. 
+ */
+typedef struct {
+       /*  vendor information */
+       vendor_struct fti_drive_type;
+       /*  data rates */
+       unsigned int fti_used_data_rate;
+       unsigned int fti_drive_max_rate;
+       unsigned int fti_fdc_max_rate;
+       /*  drive selection, either FTAPE_SEL_A/B/C/D */
+       int fti_drive_sel;      
+       /*  flags set after decode the drive and tape status   */
+       unsigned int fti_formatted      :1;
+       unsigned int fti_no_tape        :1;
+       unsigned int fti_write_protected:1;
+       unsigned int fti_new_tape       :1;
+       /*  values of last queried drive/tape status and error */
+       ft_drive_error  fti_last_error;
+       ft_drive_status fti_last_status;
+       /*  cartridge geometry */
+       unsigned int fti_tracks_per_tape;
+       unsigned int fti_segments_per_track;
+       /*  location of header segments, etc. */
+       int fti_used_header_segment;
+       int fti_header_segment_1;
+       int fti_header_segment_2;
+       int fti_first_data_segment;
+       int fti_last_data_segment;
+       /*  the format code as stored in the header segment  */
+       ft_format_type  fti_format_code;
+       /*  the following is the sole reason for the ftape_set_status() call */
+       unsigned int fti_qic_std;
+       /*  is tape running? */
+       volatile enum runner_status_enum fti_runner_status;
+       /*  is tape reading/writing/verifying/formatting/deleting */
+       buffer_state_enum fti_state;
+       /*  flags fatal hardware error */
+       unsigned int fti_failure:1;
+       /*  history record */
+       history_record fti_history;
+} ftape_info;
+
+/* vendor information */
+#define ft_drive_type          ftape_status.fti_drive_type
+/*  data rates */
+#define ft_data_rate           ftape_status.fti_used_data_rate
+#define ft_drive_max_rate      ftape_status.fti_drive_max_rate
+#define ft_fdc_max_rate        ftape_status.fti_fdc_max_rate
+/*  drive selection, either FTAPE_SEL_A/B/C/D */
+#define ft_drive_sel           ftape_status.fti_drive_sel
+/*  flags set after decode the drive and tape status   */
+#define ft_formatted           ftape_status.fti_formatted
+#define ft_no_tape             ftape_status.fti_no_tape
+#define ft_write_protected     ftape_status.fti_write_protected
+#define ft_new_tape            ftape_status.fti_new_tape
+/*  values of last queried drive/tape status and error */
+#define ft_last_error          ftape_status.fti_last_error
+#define ft_last_status         ftape_status.fti_last_status
+/*  cartridge geometry */
+#define ft_tracks_per_tape     ftape_status.fti_tracks_per_tape
+#define ft_segments_per_track  ftape_status.fti_segments_per_track
+/*  the format code as stored in the header segment  */
+#define ft_format_code         ftape_status.fti_format_code
+/*  the qic status as returned by report drive configuration */
+#define ft_qic_std             ftape_status.fti_qic_std
+#define ft_used_header_segment ftape_status.fti_used_header_segment
+#define ft_header_segment_1    ftape_status.fti_header_segment_1
+#define ft_header_segment_2    ftape_status.fti_header_segment_2
+#define ft_first_data_segment  ftape_status.fti_first_data_segment
+#define ft_last_data_segment   ftape_status.fti_last_data_segment
+/*  is tape running? */
+#define ft_runner_status       ftape_status.fti_runner_status
+/*  is tape reading/writing/verifying/formatting/deleting */
+#define ft_driver_state        ftape_status.fti_state
+/*  flags fatal hardware error */
+#define ft_failure             ftape_status.fti_failure
+/*  history record */
+#define ft_history             ftape_status.fti_history
+
+/*  compatibility with old kernel versions
+ */
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
+#define _IOC_SIZE(cmd) (((cmd) & IOCSIZE_MASK) >> IOCSIZE_SHIFT)
+#define _IOC_DIR(cmd)  (cmd)
+#define _IOC_WRITE IOC_IN
+#define _IOC_READ  IOC_OUT
+#endif
+
+/*
+ *      ftape-ctl.c defined global vars.
+ */
+extern ftape_info ftape_status;
+extern int ftape_segments_per_head;
+extern int ftape_segments_per_cylinder;
+extern int ftape_init_drive_needed;
+
+/*
+ *      ftape-ctl.c defined global functions.
+ */
+extern int  ftape_mmap(struct vm_area_struct *vma);
+extern int  ftape_enable(int drive_selection);
+extern void ftape_disable(void);
+extern int  ftape_seek_to_bot(void);
+extern int  ftape_seek_to_eot(void);
+extern int  ftape_abort_operation(void);
+extern void ftape_calc_timeouts(unsigned int qic_std,
+                                unsigned int data_rate,
+                                unsigned int tape_len);
+extern int  ftape_calibrate_data_rate(unsigned int qic_std);
+extern int  ftape_init_drive(void);
+extern const ftape_info *ftape_get_status(void);
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-ecc.c b/drivers/char/ftape/lowlevel/ftape-ecc.c
new file mode 100644 (file)
index 0000000..e5632f6
--- /dev/null
@@ -0,0 +1,853 @@
+/*
+ *
+ *      Copyright (c) 1993 Ning and David Mosberger.
+ This is based on code originally written by Bas Laarhoven (bas@vimec.nl)
+ and David L. Brown, Jr., and incorporates improvements suggested by
+ Kai Harrekilde-Petersen.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ecc.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:18:10 $
+ *
+ *      This file contains the Reed-Solomon error correction code 
+ *      for the QIC-40/80 floppy-tape driver for Linux.
+ */
+
+#include <linux/ftape.h>
+
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-ecc.h"
+
+/* Machines that are big-endian should define macro BIG_ENDIAN.
+ * Unfortunately, there doesn't appear to be a standard include file
+ * that works for all OSs.
+ */
+
+#if defined(__sparc__) || defined(__hppa)
+#define BIG_ENDIAN
+#endif                         /* __sparc__ || __hppa */
+
+#if defined(__mips__)
+#error Find a smart way to determine the Endianness of the MIPS CPU
+#endif
+
+/* Notice: to minimize the potential for confusion, we use r to
+ *         denote the independent variable of the polynomials in the
+ *         Galois Field GF(2^8).  We reserve x for polynomials that
+ *         that have coefficients in GF(2^8).
+ *         
+ * The Galois Field in which coefficient arithmetic is performed are
+ * the polynomials over Z_2 (i.e., 0 and 1) modulo the irreducible
+ * polynomial f(r), where f(r)=r^8 + r^7 + r^2 + r + 1.  A polynomial
+ * is represented as a byte with the MSB as the coefficient of r^7 and
+ * the LSB as the coefficient of r^0.  For example, the binary
+ * representation of f(x) is 0x187 (of course, this doesn't fit into 8
+ * bits).  In this field, the polynomial r is a primitive element.
+ * That is, r^i with i in 0,...,255 enumerates all elements in the
+ * field.
+ *
+ * The generator polynomial for the QIC-80 ECC is
+ *
+ *      g(x) = x^3 + r^105*x^2 + r^105*x + 1
+ *
+ * which can be factored into:
+ *
+ *      g(x) = (x-r^-1)(x-r^0)(x-r^1)
+ *
+ * the byte representation of the coefficients are:
+ *
+ *      r^105 = 0xc0
+ *      r^-1  = 0xc3
+ *      r^0   = 0x01
+ *      r^1   = 0x02
+ *
+ * Notice that r^-1 = r^254 as exponent arithmetic is performed
+ * modulo 2^8-1 = 255.
+ *
+ * For more information on Galois Fields and Reed-Solomon codes, refer
+ * to any good book.  I found _An Introduction to Error Correcting
+ * Codes with Applications_ by S. A. Vanstone and P. C. van Oorschot
+ * to be a good introduction into the former.  _CODING THEORY: The
+ * Essentials_ I found very useful for its concise description of
+ * Reed-Solomon encoding/decoding.
+ *
+ */
+
+typedef __u8 Matrix[3][3];
+
+/*
+ * gfpow[] is defined such that gfpow[i] returns r^i if
+ * i is in the range [0..255].
+ */
+static const __u8 gfpow[] =
+{
+       0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
+       0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4,
+       0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb,
+       0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd,
+       0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31,
+       0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67,
+       0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc,
+       0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b,
+       0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4,
+       0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26,
+       0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21,
+       0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba,
+       0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30,
+       0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0,
+       0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3,
+       0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a,
+       0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9,
+       0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44,
+       0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef,
+       0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85,
+       0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6,
+       0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf,
+       0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff,
+       0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58,
+       0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a,
+       0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24,
+       0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8,
+       0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64,
+       0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2,
+       0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda,
+       0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77,
+       0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3, 0x01
+};
+
+/*
+ * This is a log table.  That is, gflog[r^i] returns i (modulo f(r)).
+ * gflog[0] is undefined and the first element is therefore not valid.
+ */
+static const __u8 gflog[256] =
+{
+       0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a,
+       0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a,
+       0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1,
+       0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3,
+       0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83,
+       0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4,
+       0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35,
+       0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38,
+       0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70,
+       0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48,
+       0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24,
+       0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15,
+       0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f,
+       0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10,
+       0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7,
+       0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b,
+       0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08,
+       0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a,
+       0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91,
+       0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb,
+       0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2,
+       0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf,
+       0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52,
+       0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86,
+       0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc,
+       0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc,
+       0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8,
+       0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44,
+       0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1,
+       0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97,
+       0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5,
+       0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7
+};
+
+/* This is a multiplication table for the factor 0xc0 (i.e., r^105 (mod f(r)).
+ * gfmul_c0[f] returns r^105 * f(r) (modulo f(r)).
+ */
+static const __u8 gfmul_c0[256] =
+{
+       0x00, 0xc0, 0x07, 0xc7, 0x0e, 0xce, 0x09, 0xc9,
+       0x1c, 0xdc, 0x1b, 0xdb, 0x12, 0xd2, 0x15, 0xd5,
+       0x38, 0xf8, 0x3f, 0xff, 0x36, 0xf6, 0x31, 0xf1,
+       0x24, 0xe4, 0x23, 0xe3, 0x2a, 0xea, 0x2d, 0xed,
+       0x70, 0xb0, 0x77, 0xb7, 0x7e, 0xbe, 0x79, 0xb9,
+       0x6c, 0xac, 0x6b, 0xab, 0x62, 0xa2, 0x65, 0xa5,
+       0x48, 0x88, 0x4f, 0x8f, 0x46, 0x86, 0x41, 0x81,
+       0x54, 0x94, 0x53, 0x93, 0x5a, 0x9a, 0x5d, 0x9d,
+       0xe0, 0x20, 0xe7, 0x27, 0xee, 0x2e, 0xe9, 0x29,
+       0xfc, 0x3c, 0xfb, 0x3b, 0xf2, 0x32, 0xf5, 0x35,
+       0xd8, 0x18, 0xdf, 0x1f, 0xd6, 0x16, 0xd1, 0x11,
+       0xc4, 0x04, 0xc3, 0x03, 0xca, 0x0a, 0xcd, 0x0d,
+       0x90, 0x50, 0x97, 0x57, 0x9e, 0x5e, 0x99, 0x59,
+       0x8c, 0x4c, 0x8b, 0x4b, 0x82, 0x42, 0x85, 0x45,
+       0xa8, 0x68, 0xaf, 0x6f, 0xa6, 0x66, 0xa1, 0x61,
+       0xb4, 0x74, 0xb3, 0x73, 0xba, 0x7a, 0xbd, 0x7d,
+       0x47, 0x87, 0x40, 0x80, 0x49, 0x89, 0x4e, 0x8e,
+       0x5b, 0x9b, 0x5c, 0x9c, 0x55, 0x95, 0x52, 0x92,
+       0x7f, 0xbf, 0x78, 0xb8, 0x71, 0xb1, 0x76, 0xb6,
+       0x63, 0xa3, 0x64, 0xa4, 0x6d, 0xad, 0x6a, 0xaa,
+       0x37, 0xf7, 0x30, 0xf0, 0x39, 0xf9, 0x3e, 0xfe,
+       0x2b, 0xeb, 0x2c, 0xec, 0x25, 0xe5, 0x22, 0xe2,
+       0x0f, 0xcf, 0x08, 0xc8, 0x01, 0xc1, 0x06, 0xc6,
+       0x13, 0xd3, 0x14, 0xd4, 0x1d, 0xdd, 0x1a, 0xda,
+       0xa7, 0x67, 0xa0, 0x60, 0xa9, 0x69, 0xae, 0x6e,
+       0xbb, 0x7b, 0xbc, 0x7c, 0xb5, 0x75, 0xb2, 0x72,
+       0x9f, 0x5f, 0x98, 0x58, 0x91, 0x51, 0x96, 0x56,
+       0x83, 0x43, 0x84, 0x44, 0x8d, 0x4d, 0x8a, 0x4a,
+       0xd7, 0x17, 0xd0, 0x10, 0xd9, 0x19, 0xde, 0x1e,
+       0xcb, 0x0b, 0xcc, 0x0c, 0xc5, 0x05, 0xc2, 0x02,
+       0xef, 0x2f, 0xe8, 0x28, 0xe1, 0x21, 0xe6, 0x26,
+       0xf3, 0x33, 0xf4, 0x34, 0xfd, 0x3d, 0xfa, 0x3a
+};
+
+
+/* Returns V modulo 255 provided V is in the range -255,-254,...,509.
+ */
+static inline __u8 mod255(int v)
+{
+       if (v > 0) {
+               if (v < 255) {
+                       return v;
+               } else {
+                       return v - 255;
+               }
+       } else {
+               return v + 255;
+       }
+}
+
+
+/* Add two numbers in the field.  Addition in this field is equivalent
+ * to a bit-wise exclusive OR operation---subtraction is therefore
+ * identical to addition.
+ */
+static inline __u8 gfadd(__u8 a, __u8 b)
+{
+       return a ^ b;
+}
+
+
+/* Add two vectors of numbers in the field.  Each byte in A and B gets
+ * added individually.
+ */
+static inline unsigned long gfadd_long(unsigned long a, unsigned long b)
+{
+       return a ^ b;
+}
+
+
+/* Multiply two numbers in the field:
+ */
+static inline __u8 gfmul(__u8 a, __u8 b)
+{
+       if (a && b) {
+               return gfpow[mod255(gflog[a] + gflog[b])];
+       } else {
+               return 0;
+       }
+}
+
+
+/* Just like gfmul, except we have already looked up the log of the
+ * second number.
+ */
+static inline __u8 gfmul_exp(__u8 a, int b)
+{
+       if (a) {
+               return gfpow[mod255(gflog[a] + b)];
+       } else {
+               return 0;
+       }
+}
+
+
+/* Just like gfmul_exp, except that A is a vector of numbers.  That
+ * is, each byte in A gets multiplied by gfpow[mod255(B)].
+ */
+static inline unsigned long gfmul_exp_long(unsigned long a, int b)
+{
+       __u8 t;
+
+       if (sizeof(long) == 4) {
+               return (
+               ((t = (__u32)a >> 24 & 0xff) ?
+                (((__u32) gfpow[mod255(gflog[t] + b)]) << 24) : 0) |
+               ((t = (__u32)a >> 16 & 0xff) ?
+                (((__u32) gfpow[mod255(gflog[t] + b)]) << 16) : 0) |
+               ((t = (__u32)a >> 8 & 0xff) ?
+                (((__u32) gfpow[mod255(gflog[t] + b)]) << 8) : 0) |
+               ((t = (__u32)a >> 0 & 0xff) ?
+                (((__u32) gfpow[mod255(gflog[t] + b)]) << 0) : 0));
+       } else if (sizeof(long) == 8) {
+               return (
+               ((t = (__u64)a >> 56 & 0xff) ?
+                (((__u64) gfpow[mod255(gflog[t] + b)]) << 56) : 0) |
+               ((t = (__u64)a >> 48 & 0xff) ?
+                (((__u64) gfpow[mod255(gflog[t] + b)]) << 48) : 0) |
+               ((t = (__u64)a >> 40 & 0xff) ?
+                (((__u64) gfpow[mod255(gflog[t] + b)]) << 40) : 0) |
+               ((t = (__u64)a >> 32 & 0xff) ?
+                (((__u64) gfpow[mod255(gflog[t] + b)]) << 32) : 0) |
+               ((t = (__u64)a >> 24 & 0xff) ?
+                (((__u64) gfpow[mod255(gflog[t] + b)]) << 24) : 0) |
+               ((t = (__u64)a >> 16 & 0xff) ?
+                (((__u64) gfpow[mod255(gflog[t] + b)]) << 16) : 0) |
+               ((t = (__u64)a >> 8 & 0xff) ?
+                (((__u64) gfpow[mod255(gflog[t] + b)]) << 8) : 0) |
+               ((t = (__u64)a >> 0 & 0xff) ?
+                (((__u64) gfpow[mod255(gflog[t] + b)]) << 0) : 0));
+       } else {
+               TRACE_FUN(ft_t_any);
+               TRACE_ABORT(-1, ft_t_err, "Error: size of long is %d bytes",
+                           (int)sizeof(long));
+       }
+}
+
+
+/* Divide two numbers in the field.  Returns a/b (modulo f(x)).
+ */
+static inline __u8 gfdiv(__u8 a, __u8 b)
+{
+       if (!b) {
+               TRACE_FUN(ft_t_any);
+               TRACE_ABORT(0xff, ft_t_bug, "Error: division by zero");
+       } else if (a == 0) {
+               return 0;
+       } else {
+               return gfpow[mod255(gflog[a] - gflog[b])];
+       }
+}
+
+
+/* The following functions return the inverse of the matrix of the
+ * linear system that needs to be solved to determine the error
+ * magnitudes.  The first deals with matrices of rank 3, while the
+ * second deals with matrices of rank 2.  The error indices are passed
+ * in arguments L0,..,L2 (0=first sector, 31=last sector).  The error
+ * indices must be sorted in ascending order, i.e., L0<L1<L2.
+ *
+ * The linear system that needs to be solved for the error magnitudes
+ * is A * b = s, where s is the known vector of syndromes, b is the
+ * vector of error magnitudes and A in the ORDER=3 case:
+ *
+ *    A_3 = {{1/r^L[0], 1/r^L[1], 1/r^L[2]},
+ *          {        1,        1,        1},
+ *          { r^L[0], r^L[1], r^L[2]}} 
+ */
+static inline int gfinv3(__u8 l0,
+                        __u8 l1, 
+                        __u8 l2, 
+                        Matrix Ainv)
+{
+       __u8 det;
+       __u8 t20, t10, t21, t12, t01, t02;
+       int log_det;
+
+       /* compute some intermediate results: */
+       t20 = gfpow[l2 - l0];           /* t20 = r^l2/r^l0 */
+       t10 = gfpow[l1 - l0];           /* t10 = r^l1/r^l0 */
+       t21 = gfpow[l2 - l1];           /* t21 = r^l2/r^l1 */
+       t12 = gfpow[l1 - l2 + 255];     /* t12 = r^l1/r^l2 */
+       t01 = gfpow[l0 - l1 + 255];     /* t01 = r^l0/r^l1 */
+       t02 = gfpow[l0 - l2 + 255];     /* t02 = r^l0/r^l2 */
+       /* Calculate the determinant of matrix A_3^-1 (sometimes
+        * called the Vandermonde determinant):
+        */
+       det = gfadd(t20, gfadd(t10, gfadd(t21, gfadd(t12, gfadd(t01, t02)))));
+       if (!det) {
+               TRACE_FUN(ft_t_any);
+               TRACE_ABORT(0, ft_t_err,
+                          "Inversion failed (3 CRC errors, >0 CRC failures)");
+       }
+       log_det = 255 - gflog[det];
+
+       /* Now, calculate all of the coefficients:
+        */
+       Ainv[0][0]= gfmul_exp(gfadd(gfpow[l1], gfpow[l2]), log_det);
+       Ainv[0][1]= gfmul_exp(gfadd(t21, t12), log_det);
+       Ainv[0][2]= gfmul_exp(gfadd(gfpow[255 - l1], gfpow[255 - l2]),log_det);
+
+       Ainv[1][0]= gfmul_exp(gfadd(gfpow[l0], gfpow[l2]), log_det);
+       Ainv[1][1]= gfmul_exp(gfadd(t20, t02), log_det);
+       Ainv[1][2]= gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l2]),log_det);
+
+       Ainv[2][0]= gfmul_exp(gfadd(gfpow[l0], gfpow[l1]), log_det);
+       Ainv[2][1]= gfmul_exp(gfadd(t10, t01), log_det);
+       Ainv[2][2]= gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l1]),log_det);
+
+       return 1;
+}
+
+
+static inline int gfinv2(__u8 l0, __u8 l1, Matrix Ainv)
+{
+       __u8 det;
+       __u8 t1, t2;
+       int log_det;
+
+       t1 = gfpow[255 - l0];
+       t2 = gfpow[255 - l1];
+       det = gfadd(t1, t2);
+       if (!det) {
+               TRACE_FUN(ft_t_any);
+               TRACE_ABORT(0, ft_t_err,
+                          "Inversion failed (2 CRC errors, >0 CRC failures)");
+       }
+       log_det = 255 - gflog[det];
+
+       /* Now, calculate all of the coefficients:
+        */
+       Ainv[0][0] = Ainv[1][0] = gfpow[log_det];
+
+       Ainv[0][1] = gfmul_exp(t2, log_det);
+       Ainv[1][1] = gfmul_exp(t1, log_det);
+
+       return 1;
+}
+
+
+/* Multiply matrix A by vector S and return result in vector B.  M is
+ * assumed to be of order NxN, S and B of order Nx1.
+ */
+static inline void gfmat_mul(int n, Matrix A, 
+                            __u8 *s, __u8 *b)
+{
+       int i, j;
+       __u8 dot_prod;
+
+       for (i = 0; i < n; ++i) {
+               dot_prod = 0;
+               for (j = 0; j < n; ++j) {
+                       dot_prod = gfadd(dot_prod, gfmul(A[i][j], s[j]));
+               }
+               b[i] = dot_prod;
+       }
+}
+\f
+
+
+/* The Reed Solomon ECC codes are computed over the N-th byte of each
+ * block, where N=SECTOR_SIZE.  There are up to 29 blocks of data, and
+ * 3 blocks of ECC.  The blocks are stored contiguously in memory.  A
+ * segment, consequently, is assumed to have at least 4 blocks: one or
+ * more data blocks plus three ECC blocks.
+ *
+ * Notice: In QIC-80 speak, a CRC error is a sector with an incorrect
+ *         CRC.  A CRC failure is a sector with incorrect data, but
+ *         a valid CRC.  In the error control literature, the former
+ *         is usually called "erasure", the latter "error."
+ */
+/* Compute the parity bytes for C columns of data, where C is the
+ * number of bytes that fit into a long integer.  We use a linear
+ * feed-back register to do this.  The parity bytes P[0], P[STRIDE],
+ * P[2*STRIDE] are computed such that:
+ *
+ *              x^k * p(x) + m(x) = 0 (modulo g(x))
+ *
+ * where k = NBLOCKS,
+ *       p(x) = P[0] + P[STRIDE]*x + P[2*STRIDE]*x^2, and
+ *       m(x) = sum_{i=0}^k m_i*x^i.
+ *       m_i = DATA[i*SECTOR_SIZE]
+ */
+static inline void set_parity(unsigned long *data,
+                             int nblocks, 
+                             unsigned long *p, 
+                             int stride)
+{
+       unsigned long p0, p1, p2, t1, t2, *end;
+
+       end = data + nblocks * (FT_SECTOR_SIZE / sizeof(long));
+       p0 = p1 = p2 = 0;
+       while (data < end) {
+               /* The new parity bytes p0_i, p1_i, p2_i are computed
+                * from the old values p0_{i-1}, p1_{i-1}, p2_{i-1}
+                * recursively as:
+                *
+                *        p0_i = p1_{i-1} + r^105 * (m_{i-1} - p0_{i-1})
+                *        p1_i = p2_{i-1} + r^105 * (m_{i-1} - p0_{i-1})
+                *        p2_i =                    (m_{i-1} - p0_{i-1})
+                *
+                * With the initial condition: p0_0 = p1_0 = p2_0 = 0.
+                */
+               t1 = gfadd_long(*data, p0);
+               /*
+                * Multiply each byte in t1 by 0xc0:
+                */
+               if (sizeof(long) == 4) {
+                       t2= (((__u32) gfmul_c0[(__u32)t1 >> 24 & 0xff]) << 24 |
+                            ((__u32) gfmul_c0[(__u32)t1 >> 16 & 0xff]) << 16 |
+                            ((__u32) gfmul_c0[(__u32)t1 >>  8 & 0xff]) <<  8 |
+                            ((__u32) gfmul_c0[(__u32)t1 >>  0 & 0xff]) <<  0);
+               } else if (sizeof(long) == 8) {
+                       t2= (((__u64) gfmul_c0[(__u64)t1 >> 56 & 0xff]) << 56 |
+                            ((__u64) gfmul_c0[(__u64)t1 >> 48 & 0xff]) << 48 |
+                            ((__u64) gfmul_c0[(__u64)t1 >> 40 & 0xff]) << 40 |
+                            ((__u64) gfmul_c0[(__u64)t1 >> 32 & 0xff]) << 32 |
+                            ((__u64) gfmul_c0[(__u64)t1 >> 24 & 0xff]) << 24 |
+                            ((__u64) gfmul_c0[(__u64)t1 >> 16 & 0xff]) << 16 |
+                            ((__u64) gfmul_c0[(__u64)t1 >>  8 & 0xff]) <<  8 |
+                            ((__u64) gfmul_c0[(__u64)t1 >>  0 & 0xff]) <<  0);
+               } else {
+                       TRACE_FUN(ft_t_any);
+                       TRACE(ft_t_err, "Error: long is of size %d",
+                             (int) sizeof(long));
+                       TRACE_EXIT;
+               }
+               p0 = gfadd_long(t2, p1);
+               p1 = gfadd_long(t2, p2);
+               p2 = t1;
+               data += FT_SECTOR_SIZE / sizeof(long);
+       }
+       *p = p0;
+       p += stride;
+       *p = p1;
+       p += stride;
+       *p = p2;
+       return;
+}
+
+
+/* Compute the 3 syndrome values.  DATA should point to the first byte
+ * of the column for which the syndromes are desired.  The syndromes
+ * are computed over the first NBLOCKS of rows.  The three bytes will
+ * be placed in S[0], S[1], and S[2].
+ *
+ * S[i] is the value of the "message" polynomial m(x) evaluated at the
+ * i-th root of the generator polynomial g(x).
+ *
+ * As g(x)=(x-r^-1)(x-1)(x-r^1) we evaluate the message polynomial at
+ * x=r^-1 to get S[0], at x=r^0=1 to get S[1], and at x=r to get S[2].
+ * This could be done directly and efficiently via the Horner scheme.
+ * However, it would require multiplication tables for the factors
+ * r^-1 (0xc3) and r (0x02).  The following scheme does not require
+ * any multiplication tables beyond what's needed for set_parity()
+ * anyway and is slightly faster if there are no errors and slightly
+ * slower if there are errors.  The latter is hopefully the infrequent
+ * case.
+ *
+ * To understand the alternative algorithm, notice that set_parity(m,
+ * k, p) computes parity bytes such that:
+ *
+ *      x^k * p(x) = m(x) (modulo g(x)).
+ *
+ * That is, to evaluate m(r^m), where r^m is a root of g(x), we can
+ * simply evaluate (r^m)^k*p(r^m).  Also, notice that p is 0 if and
+ * only if s is zero.  That is, if all parity bytes are 0, we know
+ * there is no error in the data and consequently there is no need to
+ * compute s(x) at all!  In all other cases, we compute s(x) from p(x)
+ * by evaluating (r^m)^k*p(r^m) for m=-1, m=0, and m=1.  The p(x)
+ * polynomial is evaluated via the Horner scheme.
+ */
+static int compute_syndromes(unsigned long *data, int nblocks, unsigned long *s)
+{
+       unsigned long p[3];
+
+       set_parity(data, nblocks, p, 1);
+       if (p[0] | p[1] | p[2]) {
+               /* Some of the checked columns do not have a zero
+                * syndrome.  For simplicity, we compute the syndromes
+                * for all columns that we have computed the
+                * remainders for.
+                */
+               s[0] = gfmul_exp_long(
+                       gfadd_long(p[0], 
+                                  gfmul_exp_long(
+                                          gfadd_long(p[1], 
+                                                     gfmul_exp_long(p[2], -1)),
+                                          -1)), 
+                       -nblocks);
+               s[1] = gfadd_long(gfadd_long(p[2], p[1]), p[0]);
+               s[2] = gfmul_exp_long(
+                       gfadd_long(p[0], 
+                                  gfmul_exp_long(
+                                          gfadd_long(p[1],
+                                                     gfmul_exp_long(p[2], 1)),
+                                          1)),
+                       nblocks);
+               return 0;
+       } else {
+               return 1;
+       }
+}
+
+
+/* Correct the block in the column pointed to by DATA.  There are NBAD
+ * CRC errors and their indices are in BAD_LOC[0], up to
+ * BAD_LOC[NBAD-1].  If NBAD>1, Ainv holds the inverse of the matrix
+ * of the linear system that needs to be solved to determine the error
+ * magnitudes.  S[0], S[1], and S[2] are the syndrome values.  If row
+ * j gets corrected, then bit j will be set in CORRECTION_MAP.
+ */
+static inline int correct_block(__u8 *data, int nblocks,
+                               int nbad, int *bad_loc, Matrix Ainv,
+                               __u8 *s,
+                               SectorMap * correction_map)
+{
+       int ncorrected = 0;
+       int i;
+       __u8 t1, t2;
+       __u8 c0, c1, c2;        /* check bytes */
+       __u8 error_mag[3], log_error_mag;
+       __u8 *dp, l, e;
+       TRACE_FUN(ft_t_any);
+
+       switch (nbad) {
+       case 0:
+               /* might have a CRC failure: */
+               if (s[0] == 0) {
+                       /* more than one error */
+                       TRACE_ABORT(-1, ft_t_err,
+                                "ECC failed (0 CRC errors, >1 CRC failures)");
+               }
+               t1 = gfdiv(s[1], s[0]);
+               if ((bad_loc[nbad++] = gflog[t1]) >= nblocks) {
+                       TRACE(ft_t_err,
+                             "ECC failed (0 CRC errors, >1 CRC failures)");
+                       TRACE_ABORT(-1, ft_t_err,
+                                 "attempt to correct data at %d", bad_loc[0]);
+               }
+               error_mag[0] = s[1];
+               break;
+       case 1:
+               t1 = gfadd(gfmul_exp(s[1], bad_loc[0]), s[2]);
+               t2 = gfadd(gfmul_exp(s[0], bad_loc[0]), s[1]);
+               if (t1 == 0 && t2 == 0) {
+                       /* one erasure, no error: */
+                       Ainv[0][0] = gfpow[bad_loc[0]];
+               } else if (t1 == 0 || t2 == 0) {
+                       /* one erasure and more than one error: */
+                       TRACE_ABORT(-1, ft_t_err,
+                                   "ECC failed (1 erasure, >1 error)");
+               } else {
+                       /* one erasure, one error: */
+                       if ((bad_loc[nbad++] = gflog[gfdiv(t1, t2)]) 
+                           >= nblocks) {
+                               TRACE(ft_t_err, "ECC failed "
+                                     "(1 CRC errors, >1 CRC failures)");
+                               TRACE_ABORT(-1, ft_t_err,
+                                           "attempt to correct data at %d",
+                                           bad_loc[1]);
+                       }
+                       if (!gfinv2(bad_loc[0], bad_loc[1], Ainv)) {
+                               /* inversion failed---must have more
+                                 *  than one error 
+                                */
+                               TRACE_EXIT -1;
+                       }
+               }
+               /* FALL THROUGH TO ERROR MAGNITUDE COMPUTATION:
+                */
+       case 2:
+       case 3:
+               /* compute error magnitudes: */
+               gfmat_mul(nbad, Ainv, s, error_mag);
+               break;
+
+       default:
+               TRACE_ABORT(-1, ft_t_err,
+                           "Internal Error: number of CRC errors > 3");
+       }
+
+       /* Perform correction by adding ERROR_MAG[i] to the byte at
+        * offset BAD_LOC[i].  Also add the value of the computed
+        * error polynomial to the syndrome values.  If the correction
+        * was successful, the resulting check bytes should be zero
+        * (i.e., the corrected data is a valid code word).
+        */
+       c0 = s[0];
+       c1 = s[1];
+       c2 = s[2];
+       for (i = 0; i < nbad; ++i) {
+               e = error_mag[i];
+               if (e) {
+                       /* correct the byte at offset L by magnitude E: */
+                       l = bad_loc[i];
+                       dp = &data[l * FT_SECTOR_SIZE];
+                       *dp = gfadd(*dp, e);
+                       *correction_map |= 1 << l;
+                       ++ncorrected;
+
+                       log_error_mag = gflog[e];
+                       c0 = gfadd(c0, gfpow[mod255(log_error_mag - l)]);
+                       c1 = gfadd(c1, e);
+                       c2 = gfadd(c2, gfpow[mod255(log_error_mag + l)]);
+               }
+       }
+       if (c0 || c1 || c2) {
+               TRACE_ABORT(-1, ft_t_err,
+                           "ECC self-check failed, too many errors");
+       }
+       TRACE_EXIT ncorrected;
+}
+
+
+#if defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID)
+
+/* Perform a sanity check on the computed parity bytes:
+ */
+static int sanity_check(unsigned long *data, int nblocks)
+{
+       TRACE_FUN(ft_t_any);
+       unsigned long s[3];
+
+       if (!compute_syndromes(data, nblocks, s)) {
+               TRACE_ABORT(0, ft_bug,
+                           "Internal Error: syndrome self-check failed");
+       }
+       TRACE_EXIT 1;
+}
+
+#endif /* defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) */
+
+/* Compute the parity for an entire segment of data.
+ */
+int ftape_ecc_set_segment_parity(struct memory_segment *mseg)
+{
+       int i;
+       __u8 *parity_bytes;
+
+       parity_bytes = &mseg->data[(mseg->blocks - 3) * FT_SECTOR_SIZE];
+       for (i = 0; i < FT_SECTOR_SIZE; i += sizeof(long)) {
+               set_parity((unsigned long *) &mseg->data[i], mseg->blocks - 3,
+                          (unsigned long *) &parity_bytes[i],
+                          FT_SECTOR_SIZE / sizeof(long));
+#ifdef ECC_PARANOID
+               if (!sanity_check((unsigned long *) &mseg->data[i],
+                                  mseg->blocks)) {
+                       return -1;
+               }
+#endif                         /* ECC_PARANOID */
+       }
+       return 0;
+}
+
+
+/* Checks and corrects (if possible) the segment MSEG.  Returns one of
+ * ECC_OK, ECC_CORRECTED, and ECC_FAILED.
+ */
+int ftape_ecc_correct_data(struct memory_segment *mseg)
+{
+       int col, i, result;
+       int ncorrected = 0;
+       int nerasures = 0;      /* # of erasures (CRC errors) */
+       int erasure_loc[3];     /* erasure locations */
+       unsigned long ss[3];
+       __u8 s[3];
+       Matrix Ainv;
+       TRACE_FUN(ft_t_flow);
+
+       mseg->corrected = 0;
+
+       /* find first column that has non-zero syndromes: */
+       for (col = 0; col < FT_SECTOR_SIZE; col += sizeof(long)) {
+               if (!compute_syndromes((unsigned long *) &mseg->data[col],
+                                      mseg->blocks, ss)) {
+                       /* something is wrong---have to fix things */
+                       break;
+               }
+       }
+       if (col >= FT_SECTOR_SIZE) {
+               /* all syndromes are ok, therefore nothing to correct */
+               TRACE_EXIT ECC_OK;
+       }
+       /* count the number of CRC errors if there were any: */
+       if (mseg->read_bad) {
+               for (i = 0; i < mseg->blocks; i++) {
+                       if (BAD_CHECK(mseg->read_bad, i)) {
+                               if (nerasures >= 3) {
+                                       /* this is too much for ECC */
+                                       TRACE_ABORT(ECC_FAILED, ft_t_err,
+                                               "ECC failed (>3 CRC errors)");
+                               }       /* if */
+                               erasure_loc[nerasures++] = i;
+                       }
+               }
+       }
+       /*
+        * If there are at least 2 CRC errors, determine inverse of matrix
+        * of linear system to be solved:
+        */
+       switch (nerasures) {
+       case 2:
+               if (!gfinv2(erasure_loc[0], erasure_loc[1], Ainv)) {
+                       TRACE_EXIT ECC_FAILED;
+               }
+               break;
+       case 3:
+               if (!gfinv3(erasure_loc[0], erasure_loc[1],
+                           erasure_loc[2], Ainv)) {
+                       TRACE_EXIT ECC_FAILED;
+               }
+               break;
+       default:
+               /* this is not an error condition... */
+               break;
+       }
+
+       do {
+               for (i = 0; i < sizeof(long); ++i) {
+                       s[0] = ss[0];
+                       s[1] = ss[1];
+                       s[2] = ss[2];
+                       if (s[0] | s[1] | s[2]) {
+#ifdef BIG_ENDIAN
+                               result = correct_block(
+                                       &mseg->data[col + sizeof(long) - 1 - i],
+                                       mseg->blocks,
+                                       nerasures,
+                                       erasure_loc,
+                                       Ainv,
+                                       s,
+                                       &mseg->corrected);
+#else
+                               result = correct_block(&mseg->data[col + i],
+                                                      mseg->blocks,
+                                                      nerasures,
+                                                      erasure_loc,
+                                                      Ainv,
+                                                      s,
+                                                      &mseg->corrected);
+#endif
+                               if (result < 0) {
+                                       TRACE_EXIT ECC_FAILED;
+                               }
+                               ncorrected += result;
+                       }
+                       ss[0] >>= 8;
+                       ss[1] >>= 8;
+                       ss[2] >>= 8;
+               }
+
+#ifdef ECC_SANITY_CHECK
+               if (!sanity_check((unsigned long *) &mseg->data[col],
+                                 mseg->blocks)) {
+                       TRACE_EXIT ECC_FAILED;
+               }
+#endif                         /* ECC_SANITY_CHECK */
+
+               /* find next column with non-zero syndromes: */
+               while ((col += sizeof(long)) < FT_SECTOR_SIZE) {
+                       if (!compute_syndromes((unsigned long *)
+                                   &mseg->data[col], mseg->blocks, ss)) {
+                               /* something is wrong---have to fix things */
+                               break;
+                       }
+               }
+       } while (col < FT_SECTOR_SIZE);
+       if (ncorrected && nerasures == 0) {
+               TRACE(ft_t_warn, "block contained error not caught by CRC");
+       }
+       TRACE((ncorrected > 0) ? ft_t_noise : ft_t_any, "number of corrections: %d", ncorrected);
+       TRACE_EXIT ncorrected ? ECC_CORRECTED : ECC_OK;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-ecc.h b/drivers/char/ftape/lowlevel/ftape-ecc.h
new file mode 100644 (file)
index 0000000..4829146
--- /dev/null
@@ -0,0 +1,84 @@
+#ifndef _FTAPE_ECC_H_
+#define _FTAPE_ECC_H_
+
+/*
+ *      Copyright (C) 1993 Ning and David Mosberger.
+ *      Original:
+ *      Copyright (C) 1993 Bas Laarhoven.
+ *      Copyright (C) 1992 David L. Brown, Jr.
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ecc.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:11 $
+ *
+ *      This file contains the definitions for the
+ *      Reed-Solomon error correction code 
+ *      for the QIC-40/80 tape streamer device driver.
+ */
+
+#include "../lowlevel/ftape-bsm.h"
+
+#define BAD_CLEAR(entry) ((entry)=0)
+#define BAD_SET(entry,sector) ((entry)|=(1<<(sector)))
+#define BAD_CHECK(entry,sector) ((entry)&(1<<(sector)))
+
+/*
+ * Return values for ecc_correct_data:
+ */
+enum {
+       ECC_OK,                 /* Data was correct. */
+       ECC_CORRECTED,          /* Correctable error in data. */
+       ECC_FAILED,             /* Could not correct data. */
+};
+
+/*
+ * Representation of an in memory segment.  MARKED_BAD lists the
+ * sectors that were marked bad during formatting.  If the N-th sector
+ * in a segment is marked bad, bit 1<<N will be set in MARKED_BAD.
+ * The sectors should be read in from the disk and packed, as if the
+ * bad sectors were not there, and the segment just contained fewer
+ * sectors.  READ_SECTORS is a bitmap of errors encountered while
+ * reading the data.  These offsets are relative to the packed data.
+ * BLOCKS is a count of the sectors not marked bad.  This is just to
+ * prevent having to count the zero bits in MARKED_BAD each time this
+ * is needed.  DATA is the actual sector packed data from (or to) the
+ * tape.
+ */
+ struct memory_segment {
+       SectorMap marked_bad;
+       SectorMap read_bad;
+       int blocks;
+       __u8 *data;
+       SectorMap corrected;
+ };
+
+/*
+ * ecc.c defined global variables:
+ */
+#ifdef TEST
+extern int ftape_ecc_tracing;
+#endif
+
+/*
+ * ecc.c defined global functions:
+ */
+extern int ftape_ecc_correct_data(struct memory_segment *data);
+extern int ftape_ecc_set_segment_parity(struct memory_segment *data);
+
+#endif /* _FTAPE_ECC_H_ */
diff --git a/drivers/char/ftape/lowlevel/ftape-format.c b/drivers/char/ftape/lowlevel/ftape-format.c
new file mode 100644 (file)
index 0000000..0159119
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-format.c,v $
+ * $Revision: 1.2.4.1 $
+ * $Date: 1997/11/14 16:05:39 $
+ *
+ *      This file contains the code to support formatting of floppy
+ *      tape cartridges with the QIC-40/80/3010/3020 floppy-tape
+ *      driver "ftape" for Linux.
+ */
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-ecc.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-format.h"
+
+#if defined(TESTING)
+#define FT_FMT_SEGS_PER_BUF 50
+#else
+#define FT_FMT_SEGS_PER_BUF (FT_BUFF_SIZE/(4*FT_SECTORS_PER_SEGMENT))
+#endif
+
+/*
+ *  first segment of the new buffer
+ */
+static int switch_segment = 0;
+
+/*
+ *  at most 256 segments fit into one 32 kb buffer.  Even TR-1 cartridges have
+ *  more than this many segments per track, so better be careful.
+ *
+ *  buffer_struct *buff: buffer to store the formatting coordinates in
+ *  int  start: starting segment for this buffer.
+ *  int    spt: segments per track
+ *
+ *  Note: segment ids are relative to the start of the track here.
+ */
+static void setup_format_buffer(buffer_struct *buff, int start, int spt,
+                               __u8 gap3)
+{
+       int to_do = spt - start;
+       TRACE_FUN(ft_t_flow);
+
+       if (to_do > FT_FMT_SEGS_PER_BUF) {
+               to_do = FT_FMT_SEGS_PER_BUF;
+       }
+       buff->ptr          = buff->address;
+       buff->remaining    = to_do * FT_SECTORS_PER_SEGMENT; /* # sectors */
+       buff->bytes        = buff->remaining * 4; /* need 4 bytes per sector */
+       buff->gap3         = gap3;
+       buff->segment_id   = start;
+       buff->next_segment = start + to_do;
+       if (buff->next_segment >= spt) {
+               buff->next_segment = 0; /* 0 means: stop runner */
+       }
+       buff->status       = waiting; /* tells the isr that it can use
+                                      * this buffer
+                                      */
+       TRACE_EXIT;
+}
+
+
+/*
+ *  start formatting a new track.
+ */
+int ftape_format_track(const unsigned int track, const __u8 gap3)
+{
+       unsigned long flags;
+       buffer_struct *tail, *head;
+       int status;
+       TRACE_FUN(ft_t_flow);
+
+       TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),);
+       if (track & 1) {
+               if (!(status & QIC_STATUS_AT_EOT)) {
+                       TRACE_CATCH(ftape_seek_to_eot(),);
+               }
+       } else {
+               if (!(status & QIC_STATUS_AT_BOT)) {
+                       TRACE_CATCH(ftape_seek_to_bot(),);
+               }
+       }
+       ftape_abort_operation(); /* this sets ft_head = ft_tail = 0 */
+       ftape_set_state(formatting);
+
+       TRACE(ft_t_noise,
+             "Formatting track %d, logical: from segment %d to %d",
+             track, track * ft_segments_per_track, 
+             (track + 1) * ft_segments_per_track - 1);
+       
+       /*
+        *  initialize the buffer switching protocol for this track
+        */
+       head = ftape_get_buffer(ft_queue_head); /* tape isn't running yet */
+       tail = ftape_get_buffer(ft_queue_tail); /* tape isn't running yet */
+       switch_segment = 0;
+       do {
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+               setup_format_buffer(tail, switch_segment,
+                                   ft_segments_per_track, gap3);
+               switch_segment = tail->next_segment;
+       } while ((switch_segment != 0) &&
+                ((tail = ftape_next_buffer(ft_queue_tail)) != head));
+       /* go */
+       head->status = formatting;
+       TRACE_CATCH(ftape_seek_head_to_track(track),);
+       TRACE_CATCH(ftape_command(QIC_LOGICAL_FORWARD),);
+       save_flags(flags); cli();
+       TRACE_CATCH(fdc_setup_formatting(head), restore_flags(flags));
+       restore_flags(flags);   
+       TRACE_EXIT 0;
+}
+
+/*   return segment id of segment currently being formatted and do the
+ *   buffer switching stuff.
+ */
+int ftape_format_status(unsigned int *segment_id)
+{
+       buffer_struct *tail = ftape_get_buffer(ft_queue_tail);
+       int result;
+       TRACE_FUN(ft_t_flow);
+
+       while (switch_segment != 0 &&
+              ftape_get_buffer(ft_queue_head) != tail) {
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+               /*  need more buffers, first wait for empty buffer
+                */
+               TRACE_CATCH(ftape_wait_segment(formatting),);
+               /*  don't worry for gap3. If we ever hit this piece of code,
+                *  then all buffer already have the correct gap3 set!
+                */
+               setup_format_buffer(tail, switch_segment,
+                                   ft_segments_per_track, tail->gap3);
+               switch_segment = tail->next_segment;
+               if (switch_segment != 0) {
+                       tail = ftape_next_buffer(ft_queue_tail);
+               }
+       }
+       /*    should runner stop ?
+        */
+       if (ft_runner_status == aborting || ft_runner_status == do_abort) {
+               buffer_struct *head = ftape_get_buffer(ft_queue_head);
+               TRACE(ft_t_warn, "Error formatting segment %d",
+                     ftape_get_buffer(ft_queue_head)->segment_id);
+               (void)ftape_abort_operation();
+               TRACE_EXIT (head->status != error) ? -EAGAIN : -EIO;
+       }
+       /*
+        *  don't care if the timer expires, this is just kind of a
+        *  "select" operation that lets the calling process sleep
+        *  until something has happened
+        */
+       if (fdc_interrupt_wait(5 * FT_SECOND) < 0) {
+               TRACE(ft_t_noise, "End of track %d at segment %d",
+                     ft_location.track,
+                     ftape_get_buffer(ft_queue_head)->segment_id);
+               result = 1;  /* end of track, unlock module */
+       } else {
+               result = 0;
+       }
+       /*
+        *  the calling process should use the seg id to determine
+        *  which parts of the dma buffers can be safely overwritten
+        *  with new data.
+        */
+       *segment_id = ftape_get_buffer(ft_queue_head)->segment_id;
+       /*
+        *  Internally we start counting segment ids from the start of
+        *  each track when formatting, but externally we keep them
+        *  relative to the start of the tape:
+        */
+       *segment_id += ft_location.track * ft_segments_per_track;
+       TRACE_EXIT result;
+}
+
+/*
+ *  The segment id is relative to the start of the tape
+ */
+int ftape_verify_segment(const unsigned int segment_id, SectorMap *bsm)
+{
+       int result;
+       int verify_done = 0;
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_noise, "Verifying segment %d", segment_id);
+
+       if (ft_driver_state != verifying) {
+               TRACE(ft_t_noise, "calling ftape_abort_operation");
+               if (ftape_abort_operation() < 0) {
+                       TRACE(ft_t_err, "ftape_abort_operation failed");
+                       TRACE_EXIT -EIO;
+               }
+       }
+       *bsm = 0x00000000;
+       ftape_set_state(verifying);
+       for (;;) {
+               buffer_struct *tail;
+               /*
+                *  Allow escape from this loop on signal
+                */
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+               /*
+                *  Search all full buffers for the first matching the
+                *  wanted segment.  Clear other buffers on the fly.
+                */
+               tail = ftape_get_buffer(ft_queue_tail);
+               while (!verify_done && tail->status == done) {
+                       /*
+                        *  Allow escape from this loop on signal !
+                        */
+                       FT_SIGNAL_EXIT(_DONT_BLOCK);
+                       if (tail->segment_id == segment_id) {
+                               /*  If out buffer is already full,
+                                *  return its contents.  
+                                */
+                               TRACE(ft_t_flow, "found segment in cache: %d",
+                                     segment_id);
+                               if ((tail->soft_error_map |
+                                    tail->hard_error_map) != 0) {
+                                       TRACE(ft_t_info,"bsm[%d] = 0x%08lx",
+                                             segment_id,
+                                             (unsigned long)
+                                             (tail->soft_error_map |
+                                             tail->hard_error_map));
+                                       *bsm = (tail->soft_error_map |
+                                               tail->hard_error_map);
+                               }
+                               verify_done = 1;
+                       } else {
+                               TRACE(ft_t_flow,"zapping segment in cache: %d",
+                                     tail->segment_id);
+                       }
+                       tail->status = waiting;
+                       tail = ftape_next_buffer(ft_queue_tail);
+               }
+               if (!verify_done && tail->status == verifying) {
+                       if (tail->segment_id == segment_id) {
+                               switch(ftape_wait_segment(verifying)) {
+                               case 0:
+                                       break;
+                               case -EINTR:
+                                       TRACE_ABORT(-EINTR, ft_t_warn,
+                                                   "interrupted by "
+                                                   "non-blockable signal");
+                                       break;
+                               default:
+                                       ftape_abort_operation();
+                                       ftape_set_state(verifying);
+                                       /* be picky */
+                                       TRACE_ABORT(-EIO, ft_t_warn,
+                                                   "wait_segment failed");
+                               }
+                       } else {
+                               /*  We're reading the wrong segment,
+                                *  stop runner.
+                                */
+                               TRACE(ft_t_noise, "verifying wrong segment");
+                               ftape_abort_operation();
+                               ftape_set_state(verifying);
+                       }
+               }
+               /*    should runner stop ?
+                */
+               if (ft_runner_status == aborting) {
+                       buffer_struct *head = ftape_get_buffer(ft_queue_head);
+                       if (head->status == error ||
+                           head->status == verifying) {
+                               /* no data or overrun error */
+                               head->status = waiting;
+                       }
+                       TRACE_CATCH(ftape_dumb_stop(),);
+               } else {
+                       /*  If just passed last segment on tape: wait
+                        *  for BOT or EOT mark. Sets ft_runner_status to
+                        *  idle if at lEOT and successful 
+                        */
+                       TRACE_CATCH(ftape_handle_logical_eot(),);
+               }
+               if (verify_done) {
+                       TRACE_EXIT 0;
+               }
+               /*    Now at least one buffer is idle!
+                *    Restart runner & tape if needed.
+                */
+               /*  We could optimize the following a little bit. We know that 
+                *  the bad sector map is empty.
+                */
+               tail = ftape_get_buffer(ft_queue_tail);
+               if (tail->status == waiting) {
+                       buffer_struct *head = ftape_get_buffer(ft_queue_head);
+
+                       ftape_setup_new_segment(head, segment_id, -1);
+                       ftape_calc_next_cluster(head);
+                       if (ft_runner_status == idle) {
+                               result = ftape_start_tape(segment_id,
+                                                         head->sector_offset);
+                               switch(result) {
+                               case 0:
+                                       break;
+                               case -ETIME:
+                               case -EINTR:
+                                       TRACE_ABORT(result, ft_t_err, "Error: "
+                                                   "segment %d unreachable",
+                                                   segment_id);
+                                       break;
+                               default:
+                                       *bsm = EMPTY_SEGMENT;
+                                       TRACE_EXIT 0;
+                                       break;
+                               }
+                       }
+                       head->status = verifying;
+                       fdc_setup_read_write(head, FDC_VERIFY);
+               }
+       }
+       /* not reached */
+       TRACE_EXIT -EIO;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-format.h b/drivers/char/ftape/lowlevel/ftape-format.h
new file mode 100644 (file)
index 0000000..f151615
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _FTAPE_FORMAT_H
+#define _FTAPE_FORMAT_H
+
+/*
+ * Copyright (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-format.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:13 $
+ *
+ *      This file contains the low level definitions for the
+ *      formatting support for the QIC-40/80/3010/3020 floppy-tape
+ *      driver "ftape" for Linux.
+ */
+
+#ifdef __KERNEL__
+extern int ftape_format_track(const unsigned int track, const __u8 gap3);
+extern int ftape_format_status(unsigned int *segment_id);
+extern int ftape_verify_segment(const unsigned int segment_id, SectorMap *bsm);
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-init.c b/drivers/char/ftape/lowlevel/ftape-init.c
new file mode 100644 (file)
index 0000000..7a8d15f
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ *      Copyright (C) 1993-1996 Bas Laarhoven,
+ *                (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ *      This file contains the code that interfaces the kernel
+ *      for the QIC-40/80/3010/3020 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/major.h>
+
+#include <linux/ftape.h>
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16)
+#include <linux/init.h>
+#else
+#define __initdata
+#define __initfunc(__arg) __arg
+#endif
+#include <linux/qic117.h>
+#ifdef CONFIG_ZFTAPE
+#include <linux/zftape.h>
+#endif
+
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/ftape_syms.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-buffer.h"
+#include "../lowlevel/ftape-proc.h"
+#include "../lowlevel/ftape-tracing.h"
+
+/*      Global vars.
+ */
+char ft_src[] __initdata = "$Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-init.c,v $";
+char ft_rev[] __initdata = "$Revision: 1.8 $";
+char ft_dat[] __initdata = "$Date: 1997/11/06 00:38:08 $";
+
+
+/*  Called by modules package when installing the driver
+ *  or by kernel during the initialization phase
+ */
+__initfunc(int ftape_init(void))
+{
+       TRACE_FUN(ft_t_flow);
+
+#ifdef MODULE
+       printk(KERN_INFO FTAPE_VERSION "\n");
+        if (TRACE_LEVEL >= ft_t_info) {
+               printk(
+KERN_INFO "(c) 1993-1996 Bas Laarhoven (bas@vimec.nl)\n"
+KERN_INFO "(c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no)\n"
+KERN_INFO "(c) 1996-1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n"
+KERN_INFO "QIC-117 driver for QIC-40/80/3010/3020 floppy tape drives\n"
+KERN_INFO "Compiled for Linux version %s"
+#ifdef MODVERSIONS
+              " with versioned symbols"
+#endif
+              "\n", UTS_RELEASE);
+        }
+#else /* !MODULE */
+       /* print a short no-nonsense boot message */
+       printk(KERN_INFO FTAPE_VERSION " for Linux " UTS_RELEASE "\n");
+#endif /* MODULE */
+       TRACE(ft_t_info, "installing QIC-117 floppy tape hardware drive ... ");
+       TRACE(ft_t_info, "ftape_init @ 0x%p", ftape_init);
+       /*  Allocate the DMA buffers. They are deallocated at cleanup() time.
+        */
+#if TESTING
+#ifdef MODULE
+       while (ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS) < 0) {
+               ftape_sleep(FT_SECOND/20);
+               if (current->signal & ~current->blocked) {
+                       (void)ftape_set_nr_buffers(0);
+                       TRACE(ft_t_bug,
+                             "Killed by signal while allocating buffers.");
+                       TRACE_ABORT(-EINTR, 
+                                   ft_t_bug, "Free up memory and retry");
+               }
+       }
+#else
+       TRACE_CATCH(ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS),
+                   (void)ftape_set_nr_buffers(0));
+#endif
+#else
+       TRACE_CATCH(ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS),
+                   (void)ftape_set_nr_buffers(0));
+#endif
+       ft_drive_sel = -1;
+       ft_failure   = 1;         /* inhibit any operation but open */
+       ftape_udelay_calibrate(); /* must be before fdc_wait_calibrate ! */
+       fdc_wait_calibrate();
+#if (LINUX_VERSION_CODE >= KERNEL_VER(1,2,0) && \
+     LINUX_VERSION_CODE < KERNEL_VER(2,1,18))
+       register_symtab(&ftape_symbol_table); /* add global ftape symbols */
+#endif
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS)
+       (void)ftape_proc_init();
+#endif
+#ifdef CONFIG_ZFTAPE
+       (void)zft_init();
+#endif
+       TRACE_EXIT 0;
+}
+
+#ifdef MODULE
+
+#ifndef CONFIG_FT_NO_TRACE_AT_ALL
+static int ft_tracing = -1;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+#define FT_MOD_PARM(var,type,desc) \
+       MODULE_PARM(var,type); MODULE_PARM_DESC(var,desc)
+
+FT_MOD_PARM(ft_fdc_base,       "i", "Base address of FDC controller.");
+FT_MOD_PARM(ft_fdc_irq,        "i", "IRQ (interrupt channel) to use.");
+FT_MOD_PARM(ft_fdc_dma,        "i", "DMA channel to use.");
+FT_MOD_PARM(ft_fdc_threshold,  "i", "Threshold of the FDC Fifo.");
+FT_MOD_PARM(ft_fdc_rate_limit, "i", "Maximal data rate for FDC.");
+FT_MOD_PARM(ft_probe_fc10,     "i", 
+           "If non-zero, probe for a Colorado FC-10/FC-20 controller.");
+FT_MOD_PARM(ft_mach2,          "i",
+           "If non-zero, probe for a Mountain MACH-2 controller.");
+FT_MOD_PARM(ft_tracing,        "i", 
+           "Amount of debugging output, 0 <= tracing <= 8, default 3.");
+MODULE_AUTHOR(
+       "(c) 1993-1996 Bas Laarhoven (bas@vimec.nl), "
+       "(c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no), "
+       "(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)");
+MODULE_DESCRIPTION(
+       "QIC-117 driver for QIC-40/80/3010/3020 floppy tape drives.");
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
+char kernel_version[] = UTS_RELEASE;
+#endif
+
+/*  Called by modules package when installing the driver
+ */
+int init_module(void)
+{
+#ifndef CONFIG_FT_NO_TRACE_AT_ALL
+       if (ft_tracing != -1) {
+               ftape_tracing = ft_tracing;
+       }
+#endif
+       return ftape_init();
+}
+
+/*  Called by modules package when removing the driver
+ */
+void cleanup_module(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS)
+       ftape_proc_destroy();
+#endif
+       (void)ftape_set_nr_buffers(0);
+        printk(KERN_INFO "ftape: unloaded.\n");
+       TRACE_EXIT;
+}
+#endif /* MODULE */
diff --git a/drivers/char/ftape/lowlevel/ftape-init.h b/drivers/char/ftape/lowlevel/ftape-init.h
new file mode 100644 (file)
index 0000000..eae86b3
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef _FTAPE_INIT_H
+#define _FTAPE_INIT_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ *           (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-init.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:16 $
+ *
+ * This file contains the definitions for the interface to 
+ * the Linux kernel for floppy tape driver ftape.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <linux/signal.h>
+
+#define _S(nr) (1<<((nr)-1))
+#define _NEVER_BLOCK    (_S(SIGKILL)|_S(SIGSTOP))
+#define _DONT_BLOCK     (_NEVER_BLOCK|_S(SIGINT))
+#define _DO_BLOCK       (_S(SIGPIPE))
+#define _BLOCK_ALL      (0xffffffffL)
+
+
+#ifndef QIC117_TAPE_MAJOR
+#define QIC117_TAPE_MAJOR 27
+#endif
+
+/*      ftape-init.c defined global variables.
+ */
+#if defined(MODULE) && LINUX_VERSION_CODE <= KERNEL_VER(1,2,13)
+extern char kernel_version[];
+#endif
+
+/*      ftape-init.c defined global functions not defined in ftape.h
+ */
+#ifdef MODULE
+asmlinkage extern int  init_module   (void);
+asmlinkage extern void cleanup_module(void);
+#endif
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-io.c b/drivers/char/ftape/lowlevel/ftape-io.c
new file mode 100644 (file)
index 0000000..af81d18
--- /dev/null
@@ -0,0 +1,1018 @@
+/*
+ *      Copyright (C) 1993-1996 Bas Laarhoven,
+ *                (C) 1996      Kai Harrekilde-Petersen,
+ *                (C) 1997      Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-io.c,v $
+ * $Revision: 1.4 $
+ * $Date: 1997/11/11 14:02:36 $
+ *
+ *      This file contains the general control functions for the
+ *      QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/ioctl.h>
+#include <linux/mtio.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/ftape-calibr.h"
+
+/*      Global vars.
+ */
+/* NOTE: sectors start numbering at 1, all others at 0 ! */
+ft_timeout_table ftape_timeout;
+unsigned int ftape_tape_len = 0;
+volatile qic117_cmd_t ftape_current_command;
+const struct qic117_command_table qic117_cmds[] = QIC117_COMMANDS;
+int ftape_might_be_off_track;
+
+/*      Local vars.
+ */
+static int diagnostic_mode   = 0;
+static unsigned int ftape_udelay_count;
+static unsigned int ftape_udelay_time;
+
+void ftape_udelay(unsigned int usecs)
+{
+       volatile int count = (ftape_udelay_count * usecs +
+                              ftape_udelay_count - 1) / ftape_udelay_time;
+       volatile int i;
+
+       while (count-- > 0) {
+               for (i = 0; i < 20; ++i);
+       }
+}
+
+void ftape_udelay_calibrate(void)
+{
+       ftape_calibrate("ftape_udelay",
+                       ftape_udelay, &ftape_udelay_count, &ftape_udelay_time);
+}
+
+/*      Delay (msec) routine.
+ */
+void ftape_sleep(unsigned int time)
+{
+       TRACE_FUN(ft_t_any);
+
+       time *= 1000;   /* msecs -> usecs */
+       if (time < FT_USPT) {
+               /*  Time too small for scheduler, do a busy wait ! */
+               ftape_udelay(time);
+       } else {
+               unsigned long flags;
+               unsigned int ticks = (time + FT_USPT - 1) / FT_USPT;
+
+               TRACE(ft_t_any, "%d msec, %d ticks", time/1000, ticks);
+               current->timeout = jiffies + ticks;
+               current->state = TASK_INTERRUPTIBLE;
+               save_flags(flags);
+               sti();
+               do {
+                       while (current->state != TASK_RUNNING) {
+                               schedule();
+                       }
+                       /*  Mmm. Isn't current->blocked == 0xffffffff ?
+                        */
+                       if (current->signal & ~current->blocked) {
+                               TRACE(ft_t_err,
+                                     "awoken by non-blocked signal :-(");
+                               break;  /* exit on signal */
+                       }
+               } while (current->timeout > 0);
+               restore_flags(flags);
+       }
+       TRACE_EXIT;
+}
+
+/*  send a command or parameter to the drive
+ *  Generates # of step pulses.
+ */
+static inline int ft_send_to_drive(int arg)
+{
+       /*  Always wait for a command_timeout period to separate
+        *  individuals commands and/or parameters.
+        */
+       ftape_sleep(3 * FT_MILLISECOND);
+       /*  Keep cylinder nr within range, step towards home if possible.
+        */
+       if (ftape_current_cylinder >= arg) {
+               return fdc_seek(ftape_current_cylinder - arg);
+       } else {
+               return fdc_seek(ftape_current_cylinder + arg);
+       }
+}
+
+/* forward */ int ftape_report_raw_drive_status(int *status);
+
+static int ft_check_cmd_restrictions(qic117_cmd_t command)
+{
+       int status = -1;
+       TRACE_FUN(ft_t_any);
+       
+       TRACE(ft_t_flow, "%s", qic117_cmds[command].name);
+       /* A new motion command during an uninterruptible (motion)
+        *  command requires a ready status before the new command can
+        *  be issued. Otherwise a new motion command needs to be
+        *  checked against required status.
+        */
+       if (qic117_cmds[command].cmd_type == motion &&
+           qic117_cmds[ftape_current_command].non_intr) {
+               ftape_report_raw_drive_status(&status);
+               if ((status & QIC_STATUS_READY) == 0) {
+                       TRACE(ft_t_noise,
+                             "motion cmd (%d) during non-intr cmd (%d)",
+                             command, ftape_current_command);
+                       TRACE(ft_t_noise, "waiting until drive gets ready");
+                       ftape_ready_wait(ftape_timeout.seek,
+                                        &status);
+               }
+       }
+       if (qic117_cmds[command].mask != 0) {
+               __u8 difference;
+               /*  Some commands do require a certain status:
+                */
+               if (status == -1) {     /* not yet set */
+                       ftape_report_raw_drive_status(&status);
+               }
+               difference = ((status ^ qic117_cmds[command].state) &
+                             qic117_cmds[command].mask);
+               /*  Wait until the drive gets
+                *  ready. This may last forever if
+                *  the drive never gets ready... 
+                */
+               while ((difference & QIC_STATUS_READY) != 0) {
+                       TRACE(ft_t_noise, "command %d issued while not ready",
+                             command);
+                       TRACE(ft_t_noise, "waiting until drive gets ready");
+                       if (ftape_ready_wait(ftape_timeout.seek,
+                                            &status) == -EINTR) {
+                               /*  Bail out on signal !
+                                */
+                               TRACE_ABORT(-EINTR, ft_t_warn,
+                                     "interrupted by non-blockable signal");
+                       }
+                       difference = ((status ^ qic117_cmds[command].state) &
+                                     qic117_cmds[command].mask);
+               }
+               while ((difference & QIC_STATUS_ERROR) != 0) {
+                       int err;
+                       qic117_cmd_t cmd;
+
+                       TRACE(ft_t_noise,
+                             "command %d issued while error pending",
+                             command);
+                       TRACE(ft_t_noise, "clearing error status");
+                       ftape_report_error(&err, &cmd, 1);
+                       ftape_report_raw_drive_status(&status);
+                       difference = ((status ^ qic117_cmds[command].state) &
+                                     qic117_cmds[command].mask);
+                       if ((difference & QIC_STATUS_ERROR) != 0) {
+                               /*  Bail out on fatal signal !
+                                */
+                               FT_SIGNAL_EXIT(_NEVER_BLOCK);
+                       }
+               }
+               if (difference) {
+                       /*  Any remaining difference can't be solved
+                        *  here.  
+                        */
+                       if (difference & (QIC_STATUS_CARTRIDGE_PRESENT |
+                                         QIC_STATUS_NEW_CARTRIDGE |
+                                         QIC_STATUS_REFERENCED)) {
+                               TRACE(ft_t_warn,
+                                     "Fatal: tape removed or reinserted !");
+                               ft_failure = 1;
+                       } else {
+                               TRACE(ft_t_err, "wrong state: 0x%02x should be: 0x%02x",
+                                     status & qic117_cmds[command].mask,
+                                     qic117_cmds[command].state);
+                       }
+                       TRACE_EXIT -EIO;
+               }
+               if (~status & QIC_STATUS_READY & qic117_cmds[command].mask) {
+                       TRACE_ABORT(-EBUSY, ft_t_err, "Bad: still busy!");
+               }
+       }
+       TRACE_EXIT 0;
+}
+
+/*      Issue a tape command:
+ */
+int ftape_command(qic117_cmd_t command)
+{
+       int result = 0;
+       static int level = 0;
+       TRACE_FUN(ft_t_any);
+
+       if ((unsigned int)command > NR_ITEMS(qic117_cmds)) {
+               /*  This is a bug we'll want to know about too.
+                */
+               TRACE_ABORT(-EIO, ft_t_bug, "bug - bad command: %d", command);
+       }
+       if (++level > 5) { /*  This is a bug we'll want to know about. */
+               --level;
+               TRACE_ABORT(-EIO, ft_t_bug, "bug - recursion for command: %d",
+                           command);
+       }
+       /*  disable logging and restriction check for some commands,
+        *  check all other commands that have a prescribed starting
+        *  status.
+        */
+       if (diagnostic_mode) {
+               TRACE(ft_t_flow, "diagnostic command %d", command);
+       } else if (command == QIC_REPORT_DRIVE_STATUS ||
+                  command == QIC_REPORT_NEXT_BIT) {
+               TRACE(ft_t_any, "%s", qic117_cmds[command].name);
+       } else {
+               TRACE_CATCH(ft_check_cmd_restrictions(command), --level);
+       }
+       /*  Now all conditions are met or result was < 0.
+        */
+       result = ft_send_to_drive((unsigned int)command);
+       if (qic117_cmds[command].cmd_type == motion &&
+           command != QIC_LOGICAL_FORWARD && command != QIC_STOP_TAPE) {
+               ft_location.known = 0;
+       }
+       ftape_current_command = command;
+       --level;
+       TRACE_EXIT result;
+}
+
+/*      Send a tape command parameter:
+ *      Generates command # of step pulses.
+ *      Skips tape-status call !
+ */
+int ftape_parameter(unsigned int parameter)
+{
+       TRACE_FUN(ft_t_any);
+
+       TRACE(ft_t_flow, "called with parameter = %d", parameter);
+       TRACE_EXIT ft_send_to_drive(parameter + 2);
+}
+
+/*      Wait for the drive to get ready.
+ *      timeout time in milli-seconds
+ *      Returned status is valid if result != -EIO
+ *
+ *      Should we allow to be killed by SIGINT?  (^C)
+ *      Would be nice at least for large timeouts.
+ */
+int ftape_ready_wait(unsigned int timeout, int *status)
+{
+       unsigned long t0;
+       unsigned int poll_delay;
+       int signal_retries;
+       TRACE_FUN(ft_t_any);
+
+       /*  the following ** REALLY ** reduces the system load when
+        *  e.g. one simply rewinds or retensions. The tape is slow 
+        *  anyway. It is really not necessary to detect error 
+        *  conditions with 1/10 seconds granularity
+        *
+        *  On my AMD 133MHZ 486: 100 ms: 23% system load
+        *                        1  sec:  5%
+        *                        5  sec:  0.6%, yeah
+        */
+       if (timeout <= FT_SECOND) {
+               poll_delay = 100 * FT_MILLISECOND;
+               signal_retries = 20; /* two seconds */
+       } else if (timeout < 20 * FT_SECOND) {
+               TRACE(ft_t_flow, "setting poll delay to 1 second");
+               poll_delay = FT_SECOND;
+               signal_retries = 2; /* two seconds */
+       } else {
+               TRACE(ft_t_flow, "setting poll delay to 5 seconds");
+               poll_delay = 5 * FT_SECOND;
+               signal_retries = 1; /* five seconds */
+       }
+       for (;;) {
+               t0 = jiffies;
+               TRACE_CATCH(ftape_report_raw_drive_status(status),);
+               if (*status & QIC_STATUS_READY) {
+                       TRACE_EXIT 0;
+               }
+               if (!signal_retries--) {
+                       FT_SIGNAL_EXIT(_NEVER_BLOCK);
+               }
+               if ((int)timeout >= 0) {
+                       /* this will fail when jiffies wraps around about
+                        * once every year :-)
+                        */
+                       timeout -= ((jiffies - t0) * FT_SECOND) / HZ;
+                       if (timeout <= 0) {
+                               TRACE_ABORT(-ETIME, ft_t_err, "timeout");
+                       }
+                       ftape_sleep(poll_delay);
+                       timeout -= poll_delay;
+               } else {
+                       ftape_sleep(poll_delay);
+               }
+       }
+       TRACE_EXIT -ETIME;
+}
+
+/*      Issue command and wait up to timeout milli seconds for drive ready
+ */
+int ftape_command_wait(qic117_cmd_t command, unsigned int timeout, int *status)
+{
+       int result;
+
+       /* Drive should be ready, issue command
+        */
+       result = ftape_command(command);
+       if (result >= 0) {
+               result = ftape_ready_wait(timeout, status);
+       }
+       return result;
+}
+
+int ftape_parameter_wait(unsigned int parm, unsigned int timeout, int *status)
+{
+       int result;
+
+       /* Drive should be ready, issue command
+        */
+       result = ftape_parameter(parm);
+       if (result >= 0) {
+               result = ftape_ready_wait(timeout, status);
+       }
+       return result;
+}
+
+/*--------------------------------------------------------------------------
+ *      Report operations
+ */
+
+/* Query the drive about its status.  The command is sent and
+   result_length bits of status are returned (2 extra bits are read
+   for start and stop). */
+
+int ftape_report_operation(int *status,
+                          qic117_cmd_t command,
+                          int result_length)
+{
+       int i, st3;
+       unsigned int t0;
+       unsigned int dt;
+       TRACE_FUN(ft_t_any);
+
+       TRACE_CATCH(ftape_command(command),);
+       t0 = ftape_timestamp();
+       i = 0;
+       do {
+               ++i;
+               ftape_sleep(3 * FT_MILLISECOND);        /* see remark below */
+               TRACE_CATCH(fdc_sense_drive_status(&st3),);
+               dt = ftape_timediff(t0, ftape_timestamp());
+               /*  Ack should be asserted within Ttimout + Tack = 6 msec.
+                *  Looks like some drives fail to do this so extend this
+                *  period to 300 msec.
+                */
+       } while (!(st3 & ST3_TRACK_0) && dt < 300000);
+       if (!(st3 & ST3_TRACK_0)) {
+               TRACE(ft_t_err,
+                     "No acknowledge after %u msec. (%i iter)", dt / 1000, i);
+               TRACE_ABORT(-EIO, ft_t_err, "timeout on Acknowledge");
+       }
+       /*  dt may be larger than expected because of other tasks
+        *  scheduled while we were sleeping.
+        */
+       if (i > 1 && dt > 6000) {
+               TRACE(ft_t_err, "Acknowledge after %u msec. (%i iter)",
+                     dt / 1000, i);
+       }
+       *status = 0;
+       for (i = 0; i < result_length + 1; i++) {
+               TRACE_CATCH(ftape_command(QIC_REPORT_NEXT_BIT),);
+               TRACE_CATCH(fdc_sense_drive_status(&st3),);
+               if (i < result_length) {
+                       *status |= ((st3 & ST3_TRACK_0) ? 1 : 0) << i;
+               } else if ((st3 & ST3_TRACK_0) == 0) {
+                       TRACE_ABORT(-EIO, ft_t_err, "missing status stop bit");
+               }
+       }
+       /* this command will put track zero and index back into normal state */
+       (void)ftape_command(QIC_REPORT_NEXT_BIT);
+       TRACE_EXIT 0;
+}
+
+/* Report the current drive status. */
+
+int ftape_report_raw_drive_status(int *status)
+{
+       int result;
+       int count = 0;
+       TRACE_FUN(ft_t_any);
+
+       do {
+               result = ftape_report_operation(status,
+                                               QIC_REPORT_DRIVE_STATUS, 8);
+       } while (result < 0 && ++count <= 3);
+       if (result < 0) {
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "report_operation failed after %d trials", count);
+       }
+       if ((*status & 0xff) == 0xff) {
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "impossible drive status 0xff");
+       }
+       if (*status & QIC_STATUS_READY) {
+               ftape_current_command = QIC_NO_COMMAND; /* completed */
+       }
+       ft_last_status.status.drive_status = (__u8)(*status & 0xff);
+       TRACE_EXIT 0;
+}
+
+int ftape_report_drive_status(int *status)
+{
+       TRACE_FUN(ft_t_any);
+
+       TRACE_CATCH(ftape_report_raw_drive_status(status),);
+       if (*status & QIC_STATUS_NEW_CARTRIDGE ||
+           !(*status & QIC_STATUS_CARTRIDGE_PRESENT)) {
+               ft_failure = 1; /* will inhibit further operations */
+               TRACE_EXIT -EIO;
+       }
+       if (*status & QIC_STATUS_READY && *status & QIC_STATUS_ERROR) {
+               /*  Let caller handle all errors */
+               TRACE_ABORT(1, ft_t_warn, "warning: error status set!");
+       }
+       TRACE_EXIT 0;
+}
+
+int ftape_report_error(unsigned int *error,
+                      qic117_cmd_t *command, int report)
+{
+       static const ftape_error ftape_errors[] = QIC117_ERRORS;
+       int code;
+       TRACE_FUN(ft_t_any);
+
+       TRACE_CATCH(ftape_report_operation(&code, QIC_REPORT_ERROR_CODE, 16),);
+       *error   = (unsigned int)(code & 0xff);
+       *command = (qic117_cmd_t)((code>>8)&0xff);
+       /*  remember hardware status, maybe useful for status ioctls
+        */
+       ft_last_error.error.command = (__u8)*command;
+       ft_last_error.error.error   = (__u8)*error;
+       if (!report) {
+               TRACE_EXIT 0;
+       }
+       if (*error == 0) {
+               TRACE_ABORT(0, ft_t_info, "No error");
+       }
+       TRACE(ft_t_info, "errorcode: %d", *error);
+       if (*error < NR_ITEMS(ftape_errors)) {
+               TRACE(ft_t_noise, "%sFatal ERROR:",
+                     (ftape_errors[*error].fatal ? "" : "Non-"));
+               TRACE(ft_t_noise, "%s ...", ftape_errors[*error].message);
+       } else {
+               TRACE(ft_t_noise, "Unknown ERROR !");
+       }
+       if ((unsigned int)*command < NR_ITEMS(qic117_cmds) &&
+           qic117_cmds[*command].name != NULL) {
+               TRACE(ft_t_noise, "... caused by command \'%s\'",
+                     qic117_cmds[*command].name);
+       } else {
+               TRACE(ft_t_noise, "... caused by unknown command %d",
+                     *command);
+       }
+       TRACE_EXIT 0;
+}
+
+int ftape_in_error_state(int status)
+{
+       TRACE_FUN(ft_t_any);
+
+       if ((status & QIC_STATUS_READY) && (status & QIC_STATUS_ERROR)) {
+               TRACE_ABORT(1, ft_t_warn, "warning: error status set!");
+       }
+       TRACE_EXIT 0;
+}
+
+int ftape_report_configuration(qic_model *model,
+                              unsigned int *rate,
+                              int *qic_std,
+                              int *tape_len)
+{
+       int result;
+       int config;
+       int status;
+       static const unsigned int qic_rates[ 4] = { 250, 2000, 500, 1000 };
+       TRACE_FUN(ft_t_any);
+
+       result = ftape_report_operation(&config,
+                                       QIC_REPORT_DRIVE_CONFIGURATION, 8);
+       if (result < 0) {
+               ft_last_status.status.drive_config = (__u8)0x00;
+               *model = prehistoric;
+               *rate = 500;
+               *qic_std = QIC_TAPE_QIC40;
+               *tape_len = 205;
+               TRACE_EXIT 0;
+       } else {
+               ft_last_status.status.drive_config = (__u8)(config & 0xff);
+       }
+       *rate = qic_rates[(config & QIC_CONFIG_RATE_MASK) >> QIC_CONFIG_RATE_SHIFT];
+       result = ftape_report_operation(&status, QIC_REPORT_TAPE_STATUS, 8);
+       if (result < 0) {
+               ft_last_status.status.tape_status = (__u8)0x00;
+               /* pre- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is valid.
+                */
+               *qic_std = (config & QIC_CONFIG_80) ?
+                       QIC_TAPE_QIC80 : QIC_TAPE_QIC40;
+               /* ?? how's about 425ft tapes? */
+               *tape_len = (config & QIC_CONFIG_LONG) ? 307 : 0;
+               *model = pre_qic117c;
+               result = 0;
+       } else {
+               ft_last_status.status.tape_status = (__u8)(status & 0xff);
+               *model = post_qic117b;
+               TRACE(ft_t_any, "report tape status result = %02x", status);
+               /* post- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is
+                * invalid. 
+                */
+               switch (status & QIC_TAPE_STD_MASK) {
+               case QIC_TAPE_QIC40:
+               case QIC_TAPE_QIC80:
+               case QIC_TAPE_QIC3020:
+               case QIC_TAPE_QIC3010:
+                       *qic_std = status & QIC_TAPE_STD_MASK;
+                       break;
+               default:
+                       *qic_std = -1;
+                       break;
+               }
+               switch (status & QIC_TAPE_LEN_MASK) {
+               case QIC_TAPE_205FT:
+                       /* 205 or 425+ ft 550 Oe tape */
+                       *tape_len = 0;
+                       break;
+               case QIC_TAPE_307FT:
+                       /* 307.5 ft 550 Oe Extended Length (XL) tape */
+                       *tape_len = 307;
+                       break;
+               case QIC_TAPE_VARIABLE:
+                       /* Variable length 550 Oe tape */
+                       *tape_len = 0;
+                       break;
+               case QIC_TAPE_1100FT:
+                       /* 1100 ft 550 Oe tape */
+                       *tape_len = 1100;
+                       break;
+               case QIC_TAPE_FLEX:
+                       /* Variable length 900 Oe tape */
+                       *tape_len = 0;
+                       break;
+               default:
+                       *tape_len = -1;
+                       break;
+               }
+               if (*qic_std == -1 || *tape_len == -1) {
+                       TRACE(ft_t_any,
+                             "post qic-117b spec drive with unknown tape");
+               }
+               result = *tape_len == -1 ? -EIO : 0;
+               if (status & QIC_TAPE_WIDE) {
+                       switch (*qic_std) {
+                       case QIC_TAPE_QIC80:
+                               TRACE(ft_t_info, "TR-1 tape detected");
+                               break;
+                       case QIC_TAPE_QIC3010:
+                               TRACE(ft_t_info, "TR-2 tape detected");
+                               break;
+                       case QIC_TAPE_QIC3020:
+                               TRACE(ft_t_info, "TR-3 tape detected");
+                               break;
+                       default:
+                               TRACE(ft_t_warn,
+                                     "Unknown Travan tape type detected");
+                               break;
+                       }
+               }
+       }
+       TRACE_EXIT (result < 0) ? -EIO : 0;
+}
+
+int ftape_report_rom_version(int *version)
+{
+
+       if (ftape_report_operation(version, QIC_REPORT_ROM_VERSION, 8) < 0) {
+               return -EIO;
+       } else {
+               return 0;
+       }
+}
+
+int ftape_report_signature(int *signature)
+{
+       int result;
+
+       result = ftape_command(28);
+       result = ftape_report_operation(signature, 9, 8);
+       result = ftape_command(30);
+       return (result < 0) ? -EIO : 0;
+}
+
+void ftape_report_vendor_id(unsigned int *id)
+{
+       int result;
+       TRACE_FUN(ft_t_any);
+
+       /* We'll try to get a vendor id from the drive.  First
+        * according to the QIC-117 spec, a 16-bit id is requested.
+        * If that fails we'll try an 8-bit version, otherwise we'll
+        * try an undocumented query.
+        */
+       result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 16);
+       if (result < 0) {
+               result = ftape_report_operation((int *) id,
+                                               QIC_REPORT_VENDOR_ID, 8);
+               if (result < 0) {
+                       /* The following is an undocumented call found
+                        * in the CMS code.
+                        */
+                       result = ftape_report_operation((int *) id, 24, 8);
+                       if (result < 0) {
+                               *id = UNKNOWN_VENDOR;
+                       } else {
+                               TRACE(ft_t_noise, "got old 8 bit id: %04x",
+                                     *id);
+                               *id |= 0x20000;
+                       }
+               } else {
+                       TRACE(ft_t_noise, "got 8 bit id: %04x", *id);
+                       *id |= 0x10000;
+               }
+       } else {
+               TRACE(ft_t_noise, "got 16 bit id: %04x", *id);
+       }
+       if (*id == 0x0047) {
+               int version;
+               int sign;
+
+               if (ftape_report_rom_version(&version) < 0) {
+                       TRACE(ft_t_bug, "report rom version failed");
+                       TRACE_EXIT;
+               }
+               TRACE(ft_t_noise, "CMS rom version: %d", version);
+               ftape_command(QIC_ENTER_DIAGNOSTIC_1);
+               ftape_command(QIC_ENTER_DIAGNOSTIC_1);
+               diagnostic_mode = 1;
+               if (ftape_report_operation(&sign, 9, 8) < 0) {
+                       unsigned int error;
+                       qic117_cmd_t command;
+
+                       ftape_report_error(&error, &command, 1);
+                       ftape_command(QIC_ENTER_PRIMARY_MODE);
+                       diagnostic_mode = 0;
+                       TRACE_EXIT;     /* failure ! */
+               } else {
+                       TRACE(ft_t_noise, "CMS signature: %02x", sign);
+               }
+               if (sign == 0xa5) {
+                       result = ftape_report_operation(&sign, 37, 8);
+                       if (result < 0) {
+                               if (version >= 63) {
+                                       *id = 0x8880;
+                                       TRACE(ft_t_noise,
+                                             "This is an Iomega drive !");
+                               } else {
+                                       *id = 0x0047;
+                                       TRACE(ft_t_noise,
+                                             "This is a real CMS drive !");
+                               }
+                       } else {
+                               *id = 0x0047;
+                               TRACE(ft_t_noise, "CMS status: %d", sign);
+                       }
+               } else {
+                       *id = UNKNOWN_VENDOR;
+               }
+               ftape_command(QIC_ENTER_PRIMARY_MODE);
+               diagnostic_mode = 0;
+       }
+       TRACE_EXIT;
+}
+
+static int qic_rate_code(unsigned int rate)
+{
+       switch (rate) {
+       case 250:
+               return QIC_CONFIG_RATE_250;
+       case 500:
+               return QIC_CONFIG_RATE_500;
+       case 1000:
+               return QIC_CONFIG_RATE_1000;
+       case 2000:
+               return QIC_CONFIG_RATE_2000;
+       default:
+               return QIC_CONFIG_RATE_500;
+       }
+}
+
+static int ftape_set_rate_test(unsigned int *max_rate)
+{
+       unsigned int error;
+       qic117_cmd_t command;
+       int status;
+       int supported = 0;
+       TRACE_FUN(ft_t_any);
+
+       /*  Check if the drive does support the select rate command
+        *  by testing all different settings. If any one is accepted
+        *  we assume the command is supported, else not.
+        */
+       for (*max_rate = 2000; *max_rate >= 250; *max_rate /= 2) {
+               if (ftape_command(QIC_SELECT_RATE) < 0) {
+                       continue;
+               }               
+               if (ftape_parameter_wait(qic_rate_code(*max_rate),
+                                        1 * FT_SECOND, &status) < 0) {
+                       continue;
+               }
+               if (status & QIC_STATUS_ERROR) {
+                       ftape_report_error(&error, &command, 0);
+                       continue;
+               }
+               supported = 1; /* did accept a request */
+               break;
+       }
+       TRACE(ft_t_noise, "Select Rate command is%s supported", 
+             supported ? "" : " not");
+       TRACE_EXIT supported;
+}
+
+int ftape_set_data_rate(unsigned int new_rate /* Kbps */, unsigned int qic_std)
+{
+       int status;
+       int result = 0;
+       unsigned int data_rate = new_rate;
+       static int supported = 0;
+       int rate_changed = 0;
+       qic_model dummy_model;
+       unsigned int dummy_qic_std, dummy_tape_len;
+       TRACE_FUN(ft_t_any);
+
+       if (ft_drive_max_rate == 0) { /* first time */
+               supported = ftape_set_rate_test(&ft_drive_max_rate);
+       }
+       if (supported) {
+               ftape_command(QIC_SELECT_RATE);
+               result = ftape_parameter_wait(qic_rate_code(new_rate),
+                                             1 * FT_SECOND, &status);
+               if (result >= 0 && !(status & QIC_STATUS_ERROR)) {
+                       rate_changed = 1;
+               }
+       }
+       TRACE_CATCH(result = ftape_report_configuration(&dummy_model,
+                                                       &data_rate, 
+                                                       &dummy_qic_std,
+                                                       &dummy_tape_len),);
+       if (data_rate != new_rate) {
+               if (!supported) {
+                       TRACE(ft_t_warn, "Rate change not supported!");
+               } else if (rate_changed) {
+                       TRACE(ft_t_warn, "Requested: %d, got %d",
+                             new_rate, data_rate);
+               } else {
+                       TRACE(ft_t_warn, "Rate change failed!");
+               }
+               result = -EINVAL;
+       }
+       /*
+        *  Set data rate and write precompensation as specified:
+        *
+        *            |  QIC-40/80  | QIC-3010/3020
+        *   rate     |   precomp   |    precomp
+        *  ----------+-------------+--------------
+        *  250 Kbps. |   250 ns.   |     0 ns.
+        *  500 Kbps. |   125 ns.   |     0 ns.
+        *    1 Mbps. |    42 ns.   |     0 ns.
+        *    2 Mbps  |      N/A    |     0 ns.
+        */
+       if ((qic_std == QIC_TAPE_QIC40 && data_rate > 500) || 
+           (qic_std == QIC_TAPE_QIC80 && data_rate > 1000)) {
+               TRACE_ABORT(-EINVAL,
+                           ft_t_warn, "Datarate too high for QIC-mode");
+       }
+       TRACE_CATCH(fdc_set_data_rate(data_rate),_res = -EINVAL);
+       ft_data_rate = data_rate;
+       if (qic_std == QIC_TAPE_QIC40 || qic_std == QIC_TAPE_QIC80) {
+               switch (data_rate) {
+               case 250:
+                       fdc_set_write_precomp(250);
+                       break;
+               default:
+               case 500:
+                       fdc_set_write_precomp(125);
+                       break;
+               case 1000:
+                       fdc_set_write_precomp(42);
+                       break;
+               }
+       } else {
+               fdc_set_write_precomp(0);
+       }
+       TRACE_EXIT result;
+}
+
+/*  The next two functions are used to cope with excessive overrun errors
+ */
+int ftape_increase_threshold(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (fdc.type < i82077 || ft_fdc_threshold >= 12) {
+               TRACE_ABORT(-EIO, ft_t_err, "cannot increase fifo threshold");
+       }
+       if (fdc_fifo_threshold(++ft_fdc_threshold, NULL, NULL, NULL) < 0) {
+               TRACE(ft_t_err, "cannot increase fifo threshold");
+               ft_fdc_threshold --;
+               fdc_reset();
+       }
+       TRACE(ft_t_info, "New FIFO threshold: %d", ft_fdc_threshold);
+       TRACE_EXIT 0;
+}
+
+int ftape_half_data_rate(void)
+{
+       if (ft_data_rate < 500) {
+               return -1;
+       }
+       if (ftape_set_data_rate(ft_data_rate / 2, ft_qic_std) < 0) {
+               return -EIO;
+       }
+       ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len);
+       return 0;
+}
+
+/*      Seek the head to the specified track.
+ */
+int ftape_seek_head_to_track(unsigned int track)
+{
+       int status;
+       TRACE_FUN(ft_t_any);
+
+       ft_location.track = -1; /* remains set in case of error */
+       if (track >= ft_tracks_per_tape) {
+               TRACE_ABORT(-EINVAL, ft_t_bug, "track out of bounds");
+       }
+       TRACE(ft_t_flow, "seeking track %d", track);
+       TRACE_CATCH(ftape_command(QIC_SEEK_HEAD_TO_TRACK),);
+       TRACE_CATCH(ftape_parameter_wait(track, ftape_timeout.head_seek,
+                                        &status),);
+       ft_location.track = track;
+       ftape_might_be_off_track = 0;
+       TRACE_EXIT 0;
+}
+
+int ftape_wakeup_drive(wake_up_types method)
+{
+       int status;
+       int motor_on = 0;
+       TRACE_FUN(ft_t_any);
+
+       switch (method) {
+       case wake_up_colorado:
+               TRACE_CATCH(ftape_command(QIC_PHANTOM_SELECT),);
+               TRACE_CATCH(ftape_parameter(0 /* ft_drive_sel ?? */),);
+               break;
+       case wake_up_mountain:
+               TRACE_CATCH(ftape_command(QIC_SOFT_SELECT),);
+               ftape_sleep(FT_MILLISECOND);    /* NEEDED */
+               TRACE_CATCH(ftape_parameter(18),);
+               break;
+       case wake_up_insight:
+               ftape_sleep(100 * FT_MILLISECOND);
+               motor_on = 1;
+               fdc_motor(motor_on);    /* enable is done by motor-on */
+       case no_wake_up:
+               break;
+       default:
+               TRACE_EXIT -ENODEV;     /* unknown wakeup method */
+               break;
+       }
+       /*  If wakeup succeeded we shouldn't get an error here..
+        */
+       TRACE_CATCH(ftape_report_raw_drive_status(&status),
+                   if (motor_on) {
+                           fdc_motor(0);
+                   });
+       TRACE_EXIT 0;
+}
+
+int ftape_put_drive_to_sleep(wake_up_types method)
+{
+       TRACE_FUN(ft_t_any);
+
+       switch (method) {
+       case wake_up_colorado:
+               TRACE_CATCH(ftape_command(QIC_PHANTOM_DESELECT),);
+               break;
+       case wake_up_mountain:
+               TRACE_CATCH(ftape_command(QIC_SOFT_DESELECT),);
+               break;
+       case wake_up_insight:
+               fdc_motor(0);   /* enable is done by motor-on */
+       case no_wake_up:        /* no wakeup / no sleep ! */
+               break;
+       default:
+               TRACE_EXIT -ENODEV;     /* unknown wakeup method */
+       }
+       TRACE_EXIT 0;
+}
+
+int ftape_reset_drive(void)
+{
+       int result = 0;
+       int status;
+       unsigned int err_code;
+       qic117_cmd_t err_command;
+       int i;
+       TRACE_FUN(ft_t_any);
+
+       /*  We want to re-establish contact with our drive.  Fire a
+        *  number of reset commands (single step pulses) and pray for
+        *  success.
+        */
+       for (i = 0; i < 2; ++i) {
+               TRACE(ft_t_flow, "Resetting fdc");
+               fdc_reset();
+               ftape_sleep(10 * FT_MILLISECOND);
+               TRACE(ft_t_flow, "Reset command to drive");
+               result = ftape_command(QIC_RESET);
+               if (result == 0) {
+                       ftape_sleep(1 * FT_SECOND); /*  drive not
+                                                    *  accessible
+                                                    *  during 1 second
+                                                    */
+                       TRACE(ft_t_flow, "Re-selecting drive");
+
+                       /* Strange, the QIC-117 specs don't mention
+                        * this but the drive gets deselected after a
+                        * soft reset !  So we need to enable it
+                        * again.
+                        */
+                       if (ftape_wakeup_drive(ft_drive_type.wake_up) < 0) {
+                               TRACE(ft_t_err, "Wakeup failed !");
+                       }
+                       TRACE(ft_t_flow, "Waiting until drive gets ready");
+                       result= ftape_ready_wait(ftape_timeout.reset, &status);
+                       if (result == 0 && (status & QIC_STATUS_ERROR)) {
+                               result = ftape_report_error(&err_code,
+                                                           &err_command, 1);
+                               if (result == 0 && err_code == 27) {
+                                       /*  Okay, drive saw reset
+                                        *  command and responded as it
+                                        *  should
+                                        */
+                                       break;
+                               } else {
+                                       result = -EIO;
+                               }
+                       } else {
+                               result = -EIO;
+                       }
+               }
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+       }
+       if (result != 0) {
+               TRACE(ft_t_err, "General failure to reset tape drive");
+       } else {
+               /*  Restore correct settings: keep original rate 
+                */
+               ftape_set_data_rate(ft_data_rate, ft_qic_std);
+       }
+       ftape_init_drive_needed = 1;
+       TRACE_EXIT result;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-io.h b/drivers/char/ftape/lowlevel/ftape-io.h
new file mode 100644 (file)
index 0000000..841f52d
--- /dev/null
@@ -0,0 +1,94 @@
+#ifndef _FTAPE_IO_H
+#define _FTAPE_IO_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ *           (C) 1997      Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-io.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:18 $
+ *
+ *      This file contains definitions for the glue part of the
+ *      QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/qic117.h>
+#include <linux/ftape-vendors.h>
+
+typedef struct {
+       unsigned int seek;
+       unsigned int reset;
+       unsigned int rewind;
+       unsigned int head_seek;
+       unsigned int stop;
+       unsigned int pause;
+} ft_timeout_table;
+
+typedef enum {
+       prehistoric, pre_qic117c, post_qic117b, post_qic117d 
+} qic_model;
+
+/*
+ *      ftape-io.c defined global vars.
+ */
+extern ft_timeout_table ftape_timeout;
+extern unsigned int ftape_tape_len;
+extern volatile qic117_cmd_t ftape_current_command;
+extern const struct qic117_command_table qic117_cmds[];
+extern int ftape_might_be_off_track;
+
+/*
+ *      ftape-io.c defined global functions.
+ */
+extern void ftape_udelay(unsigned int usecs);
+extern void  ftape_udelay_calibrate(void);
+extern void ftape_sleep(unsigned int time);
+extern void ftape_report_vendor_id(unsigned int *id);
+extern int  ftape_command(qic117_cmd_t command);
+extern int  ftape_command_wait(qic117_cmd_t command,
+                              unsigned int timeout,
+                              int *status);
+extern int  ftape_parameter(unsigned int parameter);
+extern int  ftape_parameter_wait(unsigned int parameter,
+                                unsigned int timeout,
+                                int *status);
+extern int ftape_report_operation(int *status,
+                                 qic117_cmd_t  command,
+                                 int result_length);
+extern int ftape_report_configuration(qic_model *model,
+                                     unsigned int *rate,
+                                     int *qic_std,
+                                     int *tape_len);
+extern int ftape_report_drive_status(int *status);
+extern int ftape_report_raw_drive_status(int *status);
+extern int ftape_report_status(int *status);
+extern int ftape_ready_wait(unsigned int timeout, int *status);
+extern int ftape_seek_head_to_track(unsigned int track);
+extern int ftape_in_error_state(int status);
+extern int ftape_set_data_rate(unsigned int new_rate, unsigned int qic_std);
+extern int ftape_report_error(unsigned int *error,
+                             qic117_cmd_t *command,
+                             int report);
+extern int ftape_reset_drive(void);
+extern int ftape_put_drive_to_sleep(wake_up_types method);
+extern int ftape_wakeup_drive(wake_up_types method);
+extern int ftape_increase_threshold(void);
+extern int ftape_half_data_rate(void);
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-proc.c b/drivers/char/ftape/lowlevel/ftape-proc.c
new file mode 100644 (file)
index 0000000..ebb6a3f
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+ *      Copyright (C) 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-proc.c,v $
+ * $Revision: 1.11 $
+ * $Date: 1997/10/24 14:47:37 $
+ *
+ *      This file contains the procfs interface for the
+ *      QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/config.h>
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS)
+
+/*  adding proc entries from inside a module is REALLY complicated 
+ *  for pre-2.1.28 kernels. I don't want to care about it.
+ */
+
+#include <linux/proc_fs.h>
+
+#include <linux/ftape.h>
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) /* bail out */
+#error \
+Please disable CONFIG_FT_PROC_FS in "MCONFIG" or upgrade to a newer kernel!
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16)
+#include <linux/init.h>
+#else
+#define __initdata
+#define __initfunc(__arg) __arg
+#endif
+#include <linux/qic117.h>
+
+
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-proc.h"
+#include "../lowlevel/ftape-tracing.h"
+
+static int ftape_read_proc(char *page, char **start, off_t off,
+                          int count, int *eof, void *data);
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,28)
+
+#include <asm/segment.h> /* for memcpy_tofs() */
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long ftape_proc_read(struct inode* inode, struct file* file,
+                           char* buf, unsigned long count);
+#else
+static int ftape_proc_read(struct inode* inode, struct file* file,
+                          char* buf, int count);
+#endif
+
+#define FT_PROC_REGISTER(parent, child) proc_register_dynamic(parent, child)
+
+/*
+ *     Structures for interfacing with the /proc filesystem.
+ *     Router creates its own directory /proc/net/router with the folowing
+ *     entries:
+ *     config          device configuration
+ *     status          global device statistics
+ *     <device>        entry for each WAN device
+ */
+
+/*
+ *     Generic /proc/net/ftape/<file> file and inode operations
+ */
+
+
+static struct file_operations ftape_proc_fops =
+{
+       NULL,                   /* lseek   */
+       ftape_proc_read,        /* read    */
+       NULL,                   /* write   */
+       NULL,                   /* readdir */
+       NULL,                   /* select  */
+       NULL,                   /* ioctl   */
+       NULL,                   /* mmap    */
+       NULL,                   /* no special open code    */
+       NULL,                   /* no special release code */
+       NULL,                   /* can't fsync */
+};
+
+static struct inode_operations ftape_proc_inode_operations =
+{
+       &ftape_proc_fops,
+       NULL,                   /* create */
+       NULL,                   /* lookup */
+       NULL,                   /* link */
+       NULL,                   /* unlink */
+       NULL,                   /* symlink */
+       NULL,                   /* mkdir */
+       NULL,                   /* rmdir */
+       NULL,                   /* mknod */
+       NULL,                   /* rename */
+       NULL,                   /* readlink */
+       NULL,                   /* readpage */
+       NULL,                   /* writepage */
+       NULL,                   /* bmap */
+       NULL,                   /* truncate */
+       NULL,                   /* permission */
+};
+
+/*
+ * Proc filesystem directory entries.
+ */
+
+static int ftape_get_info(char *page, char **start, off_t off,
+                         int count, int dummy)
+{
+       int dummy_eof;
+
+       return ftape_read_proc(page, start, off, count, &dummy_eof, NULL);
+}
+
+static struct proc_dir_entry proc_ftape = {
+       0,                            /* low_ino    */
+       sizeof("ftape")-1,            /* namelen    */
+       "ftape",                      /* name       */
+       S_IFREG | S_IRUGO,            /* mode       */
+       1,                            /* nlink      */
+       0,                            /* uid        */
+       0,                            /* gid        */
+       0,                            /* size       */
+       &ftape_proc_inode_operations, /* ops        */
+       ftape_get_info,               /* get_info   */
+       NULL,                         /* fill_inode */
+       NULL,                         /* next       */
+       NULL,                         /* parent     */
+       NULL,                         /* subdir     */
+       NULL                          /* data       */
+};
+
+/*  Read ftape proc directory entry.
+ */
+
+#define PROC_BLOCK_SIZE        PAGE_SIZE
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long ftape_proc_read(struct inode * inode, struct file * file,
+                           char * buf, unsigned long nbytes)
+#else
+static int ftape_proc_read(struct inode * inode, struct file * file,
+                          char * buf, int nbytes)
+#endif
+{
+       char    *page;
+       int     retval=0;
+       int     eof=0;
+       int     n, count;
+       char    *start;
+       struct proc_dir_entry * dp;
+
+       if (nbytes < 0)
+               return -EINVAL;
+       dp = (struct proc_dir_entry *) inode->u.generic_ip;
+       if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+               return -ENOMEM;
+
+       while ((nbytes > 0) && !eof)
+       {
+               count = PROC_BLOCK_SIZE <= nbytes ? PROC_BLOCK_SIZE : nbytes;
+
+               start = NULL;
+               if (dp->get_info) {
+                       /*
+                        * Handle backwards compatibility with the old net
+                        * routines.
+                        * 
+                        * XXX What gives with the file->f_flags & O_ACCMODE
+                        * test?  Seems stupid to me....
+                        */
+                       n = dp->get_info(page, &start, file->f_pos, count,
+                                (file->f_flags & O_ACCMODE) == O_RDWR);
+                       if (n < count)
+                               eof = 1;
+               } else
+                       break;
+                       
+               if (!start) {
+                       /*
+                        * For proc files that are less than 4k
+                        */
+                       start = page + file->f_pos;
+                       n -= file->f_pos;
+                       if (n <= 0)
+                               break;
+                       if (n > count)
+                               n = count;
+               }
+               if (n == 0)
+                       break;  /* End of file */
+               if (n < 0) {
+                       if (retval == 0)
+                               retval = n;
+                       break;
+               }
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+               copy_to_user(buf, start, n);
+#else
+               memcpy_tofs(buf, start, n);
+#endif
+               file->f_pos += n;       /* Move down the file */
+               nbytes -= n;
+               buf += n;
+               retval += n;
+       }
+       free_page((unsigned long) page);
+       return retval;
+}
+
+#else /* LINUX_VERSION_CODE < KERNEL_VER(2,1,28) */
+
+#define FT_PROC_REGISTER(parent, child) proc_register(parent, child)
+
+/*
+ * Proc filesystem directory entries.
+ */
+
+static struct proc_dir_entry proc_ftape = {
+       0,                   /* low_ino    */
+       sizeof("ftape")-1,   /* namelen    */
+       "ftape",             /* name       */
+       S_IFREG | S_IRUGO,   /* mode       */
+       1,                   /* nlink      */
+       0,                   /* uid        */
+       0,                   /* gid        */
+       0,                   /* size       */
+       NULL,                /* ops        */
+       NULL,                /* get_info   */
+       NULL,                /* fill_inode */
+       NULL,                /* next       */
+       NULL,                /* parent     */
+       NULL,                /* subdir     */
+       NULL,                /* data       */
+       ftape_read_proc,     /* read_proc  */
+       NULL                 /* write_proc */
+};
+
+#endif
+
+static size_t get_driver_info(char *buf)
+{
+       const char *debug_level[] = { "bugs"  ,
+                                     "errors",
+                                     "warnings",
+                                     "informational",
+                                     "noisy",
+                                     "program flow",
+                                     "fdc and dma",
+                                     "data flow",
+                                     "anything" };
+
+       return sprintf(buf,
+                      "version       : %s\n"
+                      "used data rate: %d kbit/sec\n"
+                      "dma memory    : %d kb\n"
+                      "debug messages: %s\n",
+                      FTAPE_VERSION,
+                      ft_data_rate,
+                      FT_BUFF_SIZE * ft_nr_buffers >> 10,
+                      debug_level[TRACE_LEVEL]);
+}
+
+static size_t get_tapedrive_info(char *buf)
+{ 
+       return sprintf(buf,
+                      "vendor id : 0x%04x\n"
+                      "drive name: %s\n"
+                      "wind speed: %d ips\n"
+                      "wakeup    : %s\n"
+                      "max. rate : %d kbit/sec\n",
+                      ft_drive_type.vendor_id,
+                      ft_drive_type.name,
+                      ft_drive_type.speed,
+                      ((ft_drive_type.wake_up == no_wake_up)
+                       ? "No wakeup needed" :
+                       ((ft_drive_type.wake_up == wake_up_colorado)
+                        ? "Colorado" :
+                        ((ft_drive_type.wake_up == wake_up_mountain)
+                         ? "Mountain" :
+                         ((ft_drive_type.wake_up == wake_up_insight)
+                          ? "Motor on" :
+                          "Unknown")))),
+                      ft_drive_max_rate);
+}
+
+static size_t get_cartridge_info(char *buf)
+{
+       if (ftape_init_drive_needed) {
+               return sprintf(buf, "uninitialized\n");
+       }
+       if (ft_no_tape) {
+               return sprintf(buf, "no cartridge inserted\n");
+       }
+       return sprintf(buf,
+                      "segments  : %5d\n"
+                      "tracks    : %5d\n"
+                      "length    : %5dft\n"
+                      "formatted : %3s\n"
+                      "writable  : %3s\n"
+                      "QIC spec. : QIC-%s\n"
+                      "fmt-code  : %1d\n",
+                      ft_segments_per_track,
+                      ft_tracks_per_tape,
+                      ftape_tape_len,
+                      (ft_formatted == 1) ? "yes" : "no",
+                      (ft_write_protected == 1) ? "no" : "yes",
+                      ((ft_qic_std == QIC_TAPE_QIC40) ? "40" :
+                       ((ft_qic_std == QIC_TAPE_QIC80) ? "80" :
+                        ((ft_qic_std == QIC_TAPE_QIC3010) ? "3010" :
+                         ((ft_qic_std == QIC_TAPE_QIC3020) ? "3020" :
+                          "???")))),
+                      ft_format_code);
+}
+
+static size_t get_controller_info(char *buf)
+{
+       const char  *fdc_name[] = { "no fdc",
+                                   "i8272",
+                                   "i82077",
+                                   "i82077AA",
+                                   "Colorado FC-10 or FC-20",
+                                   "i82078",
+                                   "i82078_1" };
+
+       return sprintf(buf,
+                      "FDC type  : %s\n"
+                      "FDC base  : 0x%03x\n"
+                      "FDC irq   : %d\n"
+                      "FDC dma   : %d\n"
+                      "FDC thr.  : %d\n"
+                      "max. rate : %d kbit/sec\n",
+                      ft_mach2 ? "Mountain MACH-2" : fdc_name[fdc.type],
+                      fdc.sra, fdc.irq, fdc.dma,
+                      ft_fdc_threshold, ft_fdc_max_rate);
+}
+
+static size_t get_history_info(char *buf)
+{
+        size_t len;
+
+       len  = sprintf(buf,
+                      "\nFDC isr statistics\n"
+                      " id_am_errors     : %3d\n"
+                      " id_crc_errors    : %3d\n"
+                      " data_am_errors   : %3d\n"
+                      " data_crc_errors  : %3d\n"
+                      " overrun_errors   : %3d\n"
+                      " no_data_errors   : %3d\n"
+                      " retries          : %3d\n",
+                      ft_history.id_am_errors,   ft_history.id_crc_errors,
+                      ft_history.data_am_errors, ft_history.data_crc_errors,
+                      ft_history.overrun_errors, ft_history.no_data_errors,
+                      ft_history.retries);
+       len += sprintf(buf + len,
+                      "\nECC statistics\n"
+                      " crc_errors       : %3d\n"
+                      " crc_failures     : %3d\n"
+                      " ecc_failures     : %3d\n"
+                      " sectors corrected: %3d\n",
+                      ft_history.crc_errors,   ft_history.crc_failures,
+                      ft_history.ecc_failures, ft_history.corrected);
+       len += sprintf(buf + len,
+                      "\ntape quality statistics\n"
+                      " media defects    : %3d\n",
+                      ft_history.defects);
+       len += sprintf(buf + len,
+                      "\ntape motion statistics\n"
+                      " repositions      : %3d\n",
+                      ft_history.rewinds);
+       return len;
+}
+
+int ftape_read_proc(char *page, char **start, off_t off,
+                   int count, int *eof, void *data)
+{
+       char *ptr = page;
+       size_t len;
+       
+       ptr += sprintf(ptr, "Kernel Driver\n\n");
+       ptr += get_driver_info(ptr);
+       ptr += sprintf(ptr, "\nTape Drive\n\n");
+       ptr += get_tapedrive_info(ptr);
+       ptr += sprintf(ptr, "\nFDC Controller\n\n");
+       ptr += get_controller_info(ptr);
+       ptr += sprintf(ptr, "\nTape Cartridge\n\n");
+       ptr += get_cartridge_info(ptr);
+       ptr += sprintf(ptr, "\nHistory Record\n\n");
+       ptr += get_history_info(ptr);
+
+       len = strlen(page);
+       *start = 0;
+       if (off+count >= len) {
+               *eof = 1;
+       } else {
+               *eof = 0;
+       }
+       return len;
+}
+
+__initfunc(int ftape_proc_init(void))
+{
+       return FT_PROC_REGISTER(&proc_root, &proc_ftape);
+}
+
+#ifdef MODULE
+void ftape_proc_destroy(void)
+{
+       proc_unregister(&proc_root, proc_ftape.low_ino);
+}
+#endif
+
+#endif /* defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) */
diff --git a/drivers/char/ftape/lowlevel/ftape-proc.h b/drivers/char/ftape/lowlevel/ftape-proc.h
new file mode 100644 (file)
index 0000000..5673d5a
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _FTAPE_PROC_H
+#define _FTAPE_PROC_H
+
+/*
+ *      Copyright (C) 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-proc.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:20 $
+ *
+ *      This file contains definitions for the procfs interface of the
+ *      QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/proc_fs.h>
+
+extern struct proc_dir_entry proc_ftape;
+
+extern int  ftape_proc_init(void);
+extern void ftape_proc_destroy(void);
+
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape-read.c b/drivers/char/ftape/lowlevel/ftape-read.c
new file mode 100644 (file)
index 0000000..e67f099
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ *      Copyright (C) 1993-1996 Bas Laarhoven,
+ *                (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-read.c,v $
+ * $Revision: 1.6 $
+ * $Date: 1997/10/21 14:39:22 $
+ *
+ *      This file contains the reading code
+ *      for the QIC-117 floppy-tape driver for Linux.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-ecc.h"
+#include "../lowlevel/ftape-bsm.h"
+
+/*      Global vars.
+ */
+
+/*      Local vars.
+ */
+
+void ftape_zap_read_buffers(void)
+{
+       int i;
+
+       for (i = 0; i < ft_nr_buffers; ++i) {
+/*  changed to "fit" with dynamic allocation of tape_buffer. --khp  */
+               ft_buffer[i]->status = waiting;
+               ft_buffer[i]->bytes = 0;
+               ft_buffer[i]->skip = 0;
+               ft_buffer[i]->retry = 0;
+       }
+/*     ftape_reset_buffer(); */
+}
+
+static SectorMap convert_sector_map(buffer_struct * buff)
+{
+       int i = 0;
+       SectorMap bad_map = ftape_get_bad_sector_entry(buff->segment_id);
+       SectorMap src_map = buff->soft_error_map | buff->hard_error_map;
+       SectorMap dst_map = 0;
+       TRACE_FUN(ft_t_any);
+
+       if (bad_map || src_map) {
+               TRACE(ft_t_flow, "bad_map = 0x%08lx", (long) bad_map);
+               TRACE(ft_t_flow, "src_map = 0x%08lx", (long) src_map);
+       }
+       while (bad_map) {
+               while ((bad_map & 1) == 0) {
+                       if (src_map & 1) {
+                               dst_map |= (1 << i);
+                       }
+                       src_map >>= 1;
+                       bad_map >>= 1;
+                       ++i;
+               }
+               /* (bad_map & 1) == 1 */
+               src_map >>= 1;
+               bad_map >>= 1;
+       }
+       if (src_map) {
+               dst_map |= (src_map << i);
+       }
+       if (dst_map) {
+               TRACE(ft_t_flow, "dst_map = 0x%08lx", (long) dst_map);
+       }
+       TRACE_EXIT dst_map;
+}
+
+static int correct_and_copy_fraction(buffer_struct *buff, __u8 * destination,
+                                    int start, int size)
+{
+       struct memory_segment mseg;
+       int result;
+       SectorMap read_bad;
+       TRACE_FUN(ft_t_any);
+
+       mseg.read_bad = convert_sector_map(buff);
+       mseg.marked_bad = 0;    /* not used... */
+       mseg.blocks = buff->bytes / FT_SECTOR_SIZE;
+       mseg.data = buff->address;
+       /*    If there are no data sectors we can skip this segment.
+        */
+       if (mseg.blocks <= 3) {
+               TRACE_ABORT(0, ft_t_noise, "empty segment");
+       }
+       read_bad = mseg.read_bad;
+       ft_history.crc_errors += count_ones(read_bad);
+       result = ftape_ecc_correct_data(&mseg);
+       if (read_bad != 0 || mseg.corrected != 0) {
+               TRACE(ft_t_noise, "crc error map: 0x%08lx", (unsigned long)read_bad);
+               TRACE(ft_t_noise, "corrected map: 0x%08lx", (unsigned long)mseg.corrected);
+               ft_history.corrected += count_ones(mseg.corrected);
+       }
+       if (result == ECC_CORRECTED || result == ECC_OK) {
+               if (result == ECC_CORRECTED) {
+                       TRACE(ft_t_info, "ecc corrected segment: %d", buff->segment_id);
+               }
+               if(start < 0) {
+                       start= 0;
+               }
+               if((start+size) > ((mseg.blocks - 3) * FT_SECTOR_SIZE)) {
+                       size = (mseg.blocks - 3) * FT_SECTOR_SIZE  - start;
+               } 
+               if (size < 0) {
+                       size= 0;
+               }
+               if(size > 0) {
+                       memcpy(destination + start, mseg.data + start, size);
+               }
+               if ((read_bad ^ mseg.corrected) & mseg.corrected) {
+                       /* sectors corrected without crc errors set */
+                       ft_history.crc_failures++;
+               }
+               TRACE_EXIT size; /* (mseg.blocks - 3) * FT_SECTOR_SIZE; */
+       } else {
+               ft_history.ecc_failures++;
+               TRACE_ABORT(-EAGAIN,
+                           ft_t_err, "ecc failure on segment %d",
+                           buff->segment_id);
+       }
+       TRACE_EXIT 0;
+}
+
+/*      Read given segment into buffer at address.
+ */
+int ftape_read_segment_fraction(const int segment_id,
+                               void  *address, 
+                               const ft_read_mode_t read_mode,
+                               const int start,
+                               const int size)
+{
+       int result = 0;
+       int retry  = 0;
+       int bytes_read = 0;
+       int read_done  = 0;
+       TRACE_FUN(ft_t_flow);
+
+       ft_history.used |= 1;
+       TRACE(ft_t_data_flow, "segment_id = %d", segment_id);
+       if (ft_driver_state != reading) {
+               TRACE(ft_t_noise, "calling ftape_abort_operation");
+               TRACE_CATCH(ftape_abort_operation(),);
+               ftape_set_state(reading);
+       }
+       for(;;) {
+               buffer_struct *tail;
+               /*  Allow escape from this loop on signal !
+                */
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+               /*  Search all full buffers for the first matching the
+                *  wanted segment.  Clear other buffers on the fly.
+                */
+               tail = ftape_get_buffer(ft_queue_tail);
+               while (!read_done && tail->status == done) {
+                       /*  Allow escape from this loop on signal !
+                        */
+                       FT_SIGNAL_EXIT(_DONT_BLOCK);
+                       if (tail->segment_id == segment_id) {
+                               /*  If out buffer is already full,
+                                *  return its contents.  
+                                */
+                               TRACE(ft_t_flow, "found segment in cache: %d",
+                                     segment_id);
+                               if (tail->deleted) {
+                                       /*  Return a value that
+                                        *  read_header_segment
+                                        *  understands.  As this
+                                        *  should only occur when
+                                        *  searching for the header
+                                        *  segments it shouldn't be
+                                        *  misinterpreted elsewhere.
+                                        */
+                                       TRACE_EXIT 0;
+                               }
+                               result = correct_and_copy_fraction(
+                                       tail,
+                                       address,
+                                       start,
+                                       size);
+                               TRACE(ft_t_flow, "segment contains (bytes): %d",
+                                     result);
+                               if (result < 0) {
+                                       if (result != -EAGAIN) {
+                                               TRACE_EXIT result;
+                                       }
+                                       /* keep read_done == 0, will
+                                        * trigger
+                                        * ftape_abort_operation
+                                        * because reading wrong
+                                        * segment.
+                                        */
+                                       TRACE(ft_t_err, "ecc failed, retry");
+                                       ++retry;
+                               } else {
+                                       read_done = 1;
+                                       bytes_read = result;
+                               }
+                       } else {
+                               TRACE(ft_t_flow,"zapping segment in cache: %d",
+                                     tail->segment_id);
+                       }
+                       tail->status = waiting;
+                       tail = ftape_next_buffer(ft_queue_tail);
+               }
+               if (!read_done && tail->status == reading) {
+                       if (tail->segment_id == segment_id) {
+                               switch(ftape_wait_segment(reading)) {
+                               case 0:
+                                       break;
+                               case -EINTR:
+                                       TRACE_ABORT(-EINTR, ft_t_warn,
+                                                   "interrupted by "
+                                                   "non-blockable signal");
+                                       break;
+                               default:
+                                       TRACE(ft_t_noise,
+                                             "wait_segment failed");
+                                       ftape_abort_operation();
+                                       ftape_set_state(reading);
+                                       break;
+                               }
+                       } else {
+                               /*  We're reading the wrong segment,
+                                *  stop runner.
+                                */
+                               TRACE(ft_t_noise, "reading wrong segment");
+                               ftape_abort_operation();
+                               ftape_set_state(reading);
+                       }
+               }
+               /*    should runner stop ?
+                */
+               if (ft_runner_status == aborting) {
+                       buffer_struct *head = ftape_get_buffer(ft_queue_head);
+                       switch(head->status) {
+                       case error:
+                               ft_history.defects += 
+                                       count_ones(head->hard_error_map);
+                       case reading:
+                               head->status = waiting;
+                               break;
+                       default:
+                               break;
+                       }
+                       TRACE_CATCH(ftape_dumb_stop(),);
+               } else {
+                       /*  If just passed last segment on tape: wait
+                        *  for BOT or EOT mark. Sets ft_runner_status to
+                        *  idle if at lEOT and successful 
+                        */
+                       TRACE_CATCH(ftape_handle_logical_eot(),);
+               }
+               /*    If we got a segment: quit, or else retry up to limit.
+                *
+                *    If segment to read is empty, do not start runner for it,
+                *    but wait for next read call.
+                */
+               if (read_done ||
+                   ftape_get_bad_sector_entry(segment_id) == EMPTY_SEGMENT ) {
+                       /* bytes_read = 0;  should still be zero */
+                       TRACE_EXIT bytes_read;
+
+               }
+               if (retry > FT_RETRIES_ON_ECC_ERROR) {
+                       ft_history.defects++;
+                       TRACE_ABORT(-ENODATA, ft_t_err,
+                                   "too many retries on ecc failure");
+               }
+               /*    Now at least one buffer is empty !
+                *    Restart runner & tape if needed.
+                */
+               TRACE(ft_t_any, "head: %d, tail: %d, ft_runner_status: %d",
+                     ftape_buffer_id(ft_queue_head),
+                     ftape_buffer_id(ft_queue_tail),
+                     ft_runner_status);
+               TRACE(ft_t_any, "buffer[].status, [head]: %d, [tail]: %d",
+                     ftape_get_buffer(ft_queue_head)->status,
+                     ftape_get_buffer(ft_queue_tail)->status);
+               tail = ftape_get_buffer(ft_queue_tail);
+               if (tail->status == waiting) {
+                       buffer_struct *head = ftape_get_buffer(ft_queue_head);
+
+                       ftape_setup_new_segment(head, segment_id, -1);
+                       if (read_mode == FT_RD_SINGLE) {
+                               /* disable read-ahead */
+                               head->next_segment = 0;
+                       }
+                       ftape_calc_next_cluster(head);
+                       if (ft_runner_status == idle) {
+                               result = ftape_start_tape(segment_id,
+                                                         head->sector_offset);
+                               if (result < 0) {
+                                       TRACE_ABORT(result, ft_t_err, "Error: "
+                                                   "segment %d unreachable",
+                                                   segment_id);
+                               }
+                       }
+                       head->status = reading;
+                       fdc_setup_read_write(head, FDC_READ);
+               }
+       }
+       /* not reached */
+       TRACE_EXIT -EIO;
+}
+
+int ftape_read_header_segment(__u8 *address)
+{
+       int result;
+       int header_segment;
+       int first_failed = 0;
+       int status;
+       TRACE_FUN(ft_t_flow);
+
+       ft_used_header_segment = -1;
+       TRACE_CATCH(ftape_report_drive_status(&status),);
+       TRACE(ft_t_flow, "reading...");
+       /*  We're looking for the first header segment.
+        *  A header segment cannot contain bad sectors, therefor at the
+        *  tape start, segments with bad sectors are (according to QIC-40/80)
+        *  written with deleted data marks and must be skipped.
+        */
+       memset(address, '\0', (FT_SECTORS_PER_SEGMENT - 3) * FT_SECTOR_SIZE); 
+       result = 0;
+#define HEADER_SEGMENT_BOUNDARY 68  /* why not 42? */
+       for (header_segment = 0;
+            header_segment < HEADER_SEGMENT_BOUNDARY && result == 0;
+            ++header_segment) {
+               /*  Set no read-ahead, the isr will force read-ahead whenever
+                *  it encounters deleted data !
+                */
+               result = ftape_read_segment(header_segment,
+                                           address,
+                                           FT_RD_SINGLE);
+               if (result < 0 && !first_failed) {
+                       TRACE(ft_t_err, "header segment damaged, trying backup");
+                       first_failed = 1;
+                       result = 0;     /* force read of next (backup) segment */
+               }
+       }
+       if (result < 0 || header_segment >= HEADER_SEGMENT_BOUNDARY) {
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "no readable header segment found");
+       }
+       TRACE_CATCH(ftape_abort_operation(),);
+       ft_used_header_segment = header_segment;
+       result = ftape_decode_header_segment(address);
+       TRACE_EXIT result;
+}
+
+int ftape_decode_header_segment(__u8 *address)
+{
+       unsigned int max_floppy_side;
+       unsigned int max_floppy_track;
+       unsigned int max_floppy_sector;
+       unsigned int new_tape_len;
+       TRACE_FUN(ft_t_flow);
+
+       if (GET4(address, FT_SIGNATURE) == FT_D2G_MAGIC) {
+               /* Ditto 2GB header segment. They encrypt the bad sector map.
+                * We decrypt it and store them in normal format.
+                * I hope this is correct.
+                */
+               int i;
+               TRACE(ft_t_warn,
+                     "Found Ditto 2GB tape, "
+                     "trying to decrypt bad sector map");
+               for (i=256; i < 29 * FT_SECTOR_SIZE; i++) {
+                       address[i] = ~(address[i] - (i&0xff));
+               }
+               PUT4(address, 0,FT_HSEG_MAGIC);
+       } else if (GET4(address, FT_SIGNATURE) != FT_HSEG_MAGIC) {
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "wrong signature in header segment");
+       }
+       ft_format_code = (ft_format_type) address[FT_FMT_CODE];
+       if (ft_format_code != fmt_big) {
+               ft_header_segment_1   = GET2(address, FT_HSEG_1);
+               ft_header_segment_2   = GET2(address, FT_HSEG_2);
+               ft_first_data_segment = GET2(address, FT_FRST_SEG);
+               ft_last_data_segment  = GET2(address, FT_LAST_SEG);
+       } else {
+               ft_header_segment_1   = GET4(address, FT_6_HSEG_1);
+               ft_header_segment_2   = GET4(address, FT_6_HSEG_2);
+               ft_first_data_segment = GET4(address, FT_6_FRST_SEG);
+               ft_last_data_segment  = GET4(address, FT_6_LAST_SEG);
+       }
+       TRACE(ft_t_noise, "first data segment: %d", ft_first_data_segment);
+       TRACE(ft_t_noise, "last  data segment: %d", ft_last_data_segment);
+       TRACE(ft_t_noise, "header segments are %d and %d",
+             ft_header_segment_1, ft_header_segment_2);
+
+       /*    Verify tape parameters...
+        *    QIC-40/80 spec:                 tape_parameters:
+        *
+        *    segments-per-track              segments_per_track
+        *    tracks-per-cartridge            tracks_per_tape
+        *    max-floppy-side                 (segments_per_track *
+        *                                    tracks_per_tape - 1) /
+        *                                    ftape_segments_per_head
+        *    max-floppy-track                ftape_segments_per_head /
+        *                                    ftape_segments_per_cylinder - 1
+        *    max-floppy-sector               ftape_segments_per_cylinder *
+        *                                    FT_SECTORS_PER_SEGMENT
+        */
+       ft_segments_per_track = GET2(address, FT_SPT);
+       ft_tracks_per_tape    = address[FT_TPC];
+       max_floppy_side       = address[FT_FHM];
+       max_floppy_track      = address[FT_FTM];
+       max_floppy_sector     = address[FT_FSM];
+       TRACE(ft_t_noise, "(fmt/spt/tpc/fhm/ftm/fsm) = %d/%d/%d/%d/%d/%d",
+             ft_format_code, ft_segments_per_track, ft_tracks_per_tape,
+             max_floppy_side, max_floppy_track, max_floppy_sector);
+       new_tape_len = ftape_tape_len;
+       switch (ft_format_code) {
+       case fmt_425ft:
+               new_tape_len = 425;
+               break;
+       case fmt_normal:
+               if (ftape_tape_len == 0) {      /* otherwise 307 ft */
+                       new_tape_len = 205;
+               }
+               break;
+       case fmt_1100ft:
+               new_tape_len = 1100;
+               break;
+       case fmt_var:{
+                       int segments_per_1000_inch = 1;         /* non-zero default for switch */
+                       switch (ft_qic_std) {
+                       case QIC_TAPE_QIC40:
+                               segments_per_1000_inch = 332;
+                               break;
+                       case QIC_TAPE_QIC80:
+                               segments_per_1000_inch = 488;
+                               break;
+                       case QIC_TAPE_QIC3010:
+                               segments_per_1000_inch = 730;
+                               break;
+                       case QIC_TAPE_QIC3020:
+                               segments_per_1000_inch = 1430;
+                               break;
+                       }
+                       new_tape_len = (1000 * ft_segments_per_track +
+                                       (segments_per_1000_inch - 1)) / segments_per_1000_inch;
+                       break;
+               }
+       case fmt_big:{
+                       int segments_per_1000_inch = 1;         /* non-zero default for switch */
+                       switch (ft_qic_std) {
+                       case QIC_TAPE_QIC40:
+                               segments_per_1000_inch = 332;
+                               break;
+                       case QIC_TAPE_QIC80:
+                               segments_per_1000_inch = 488;
+                               break;
+                       case QIC_TAPE_QIC3010:
+                               segments_per_1000_inch = 730;
+                               break;
+                       case QIC_TAPE_QIC3020:
+                               segments_per_1000_inch = 1430;
+                               break;
+                       default:
+                               TRACE_ABORT(-EIO, ft_t_bug,
+                       "%x QIC-standard with fmt-code %d, please report",
+                                           ft_qic_std, ft_format_code);
+                       }
+                       new_tape_len = ((1000 * ft_segments_per_track +
+                                        (segments_per_1000_inch - 1)) / 
+                                       segments_per_1000_inch);
+                       break;
+               }
+       default:
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "unknown tape format, please report !");
+       }
+       if (new_tape_len != ftape_tape_len) {
+               ftape_tape_len = new_tape_len;
+               TRACE(ft_t_info, "calculated tape length is %d ft",
+                     ftape_tape_len);
+               ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len);
+       }
+       if (ft_segments_per_track == 0 && ft_tracks_per_tape == 0 &&
+           max_floppy_side == 0 && max_floppy_track == 0 &&
+           max_floppy_sector == 0) {
+               /*  QIC-40 Rev E and earlier has no values in the header.
+                */
+               ft_segments_per_track = 68;
+               ft_tracks_per_tape = 20;
+               max_floppy_side = 1;
+               max_floppy_track = 169;
+               max_floppy_sector = 128;
+       }
+       /*  This test will compensate for the wrong parameter on tapes
+        *  formatted by Conner software.
+        */
+       if (ft_segments_per_track == 150 &&
+           ft_tracks_per_tape == 28 &&
+           max_floppy_side == 7 &&
+           max_floppy_track == 149 &&
+           max_floppy_sector == 128) {
+TRACE(ft_t_info, "the famous CONNER bug: max_floppy_side off by one !");
+               max_floppy_side = 6;
+       }
+       /*  These tests will compensate for the wrong parameter on tapes
+        *  formatted by ComByte Windows software.
+        *
+        *  First, for 205 foot tapes
+        */
+       if (ft_segments_per_track == 100 &&
+           ft_tracks_per_tape == 28 &&
+           max_floppy_side == 9 &&
+           max_floppy_track == 149 &&
+           max_floppy_sector == 128) {
+TRACE(ft_t_info, "the ComByte bug: max_floppy_side incorrect!");
+               max_floppy_side = 4;
+       }
+       /* Next, for 307 foot tapes. */
+       if (ft_segments_per_track == 150 &&
+           ft_tracks_per_tape == 28 &&
+           max_floppy_side == 9 &&
+           max_floppy_track == 149 &&
+           max_floppy_sector == 128) {
+TRACE(ft_t_info, "the ComByte bug: max_floppy_side incorrect!");
+               max_floppy_side = 6;
+       }
+       /*  This test will compensate for the wrong parameter on tapes
+        *  formatted by Colorado Windows software.
+        */
+       if (ft_segments_per_track == 150 &&
+           ft_tracks_per_tape == 28 &&
+           max_floppy_side == 6 &&
+           max_floppy_track == 150 &&
+           max_floppy_sector == 128) {
+TRACE(ft_t_info, "the famous Colorado bug: max_floppy_track off by one !");
+               max_floppy_track = 149;
+       }
+       ftape_segments_per_head = ((max_floppy_sector/FT_SECTORS_PER_SEGMENT) *
+                                  (max_floppy_track + 1));
+       /*  This test will compensate for some bug reported by Dima
+        *  Brodsky.  Seems to be a Colorado bug, either. (freebee
+        *  Imation tape shipped together with Colorado T3000
+        */
+       if ((ft_format_code == fmt_var || ft_format_code == fmt_big) &&
+           ft_tracks_per_tape == 50 &&
+           max_floppy_side == 54 &&
+           max_floppy_track == 255 &&
+           max_floppy_sector == 128) {
+TRACE(ft_t_info, "the famous ??? bug: max_floppy_track off by one !");
+               max_floppy_track = 254;
+       }
+       /*
+        *    Verify drive_configuration with tape parameters
+        */
+       if (ftape_segments_per_head == 0 || ftape_segments_per_cylinder == 0 ||
+         ((ft_segments_per_track * ft_tracks_per_tape - 1) / ftape_segments_per_head
+          != max_floppy_side) ||
+           (ftape_segments_per_head / ftape_segments_per_cylinder - 1 != max_floppy_track) ||
+       (ftape_segments_per_cylinder * FT_SECTORS_PER_SEGMENT != max_floppy_sector)
+#ifdef TESTING
+           || ((ft_format_code == fmt_var || ft_format_code == fmt_big) && 
+               (max_floppy_track != 254 || max_floppy_sector != 128))
+#endif
+          ) {
+               TRACE(ft_t_err,"Tape parameters inconsistency, please report");
+               TRACE(ft_t_err, "reported = %d/%d/%d/%d/%d/%d",
+                     ft_format_code,
+                     ft_segments_per_track,
+                     ft_tracks_per_tape,
+                     max_floppy_side,
+                     max_floppy_track,
+                     max_floppy_sector);
+               TRACE(ft_t_err, "required = %d/%d/%d/%d/%d/%d",
+                     ft_format_code,
+                     ft_segments_per_track,
+                     ft_tracks_per_tape,
+                     ((ft_segments_per_track * ft_tracks_per_tape -1) / 
+                      ftape_segments_per_head ),
+                     (ftape_segments_per_head / 
+                      ftape_segments_per_cylinder - 1 ),
+                     (ftape_segments_per_cylinder * FT_SECTORS_PER_SEGMENT));
+               TRACE_EXIT -EIO;
+       }
+       ftape_extract_bad_sector_map(address);
+       TRACE_EXIT 0;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-read.h b/drivers/char/ftape/lowlevel/ftape-read.h
new file mode 100644 (file)
index 0000000..069f99f
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef _FTAPE_READ_H
+#define _FTAPE_READ_H
+
+/*
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ *           (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-read.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:22 $
+ *
+ *      This file contains the definitions for the read functions
+ *      for the QIC-117 floppy-tape driver for Linux.
+ *
+ */
+
+/*      ftape-read.c defined global functions.
+ */
+typedef enum {
+       FT_RD_SINGLE = 0,
+       FT_RD_AHEAD  = 1,
+} ft_read_mode_t;
+
+extern int ftape_read_header_segment(__u8 *address);
+extern int ftape_decode_header_segment(__u8 *address);
+extern int ftape_read_segment_fraction(const int segment,
+                                      void  *address, 
+                                      const ft_read_mode_t read_mode,
+                                      const int start,
+                                      const int size);
+#define ftape_read_segment(segment, address, read_mode)                        \
+       ftape_read_segment_fraction(segment, address, read_mode,        \
+                                   0, FT_SEGMENT_SIZE)
+extern void ftape_zap_read_buffers(void);
+
+#endif                         /* _FTAPE_READ_H */
diff --git a/drivers/char/ftape/lowlevel/ftape-rw.c b/drivers/char/ftape/lowlevel/ftape-rw.c
new file mode 100644 (file)
index 0000000..e3e0243
--- /dev/null
@@ -0,0 +1,1091 @@
+/*
+ *      Copyright (C) 1993-1996 Bas Laarhoven,
+ *                (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-rw.c,v $
+ * $Revision: 1.7 $
+ * $Date: 1997/10/28 14:26:49 $
+ *
+ *      This file contains some common code for the segment read and
+ *      segment write routines for the QIC-117 floppy-tape driver for
+ *      Linux.
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-ecc.h"
+#include "../lowlevel/ftape-bsm.h"
+
+/*      Global vars.
+ */
+int ft_nr_buffers = 0;
+buffer_struct *ft_buffer[FT_MAX_NR_BUFFERS] = {NULL, };
+static volatile int ft_head;
+static volatile int ft_tail;   /* not volatile but need same type as head */
+int fdc_setup_error;
+location_record ft_location = {-1, 0};
+volatile int ftape_tape_running = 0;
+
+/*      Local vars.
+ */
+static int overrun_count_offset = 0;
+static int inhibit_correction = 0;
+
+/*  maxmimal allowed overshoot when fast seeking
+ */
+#define OVERSHOOT_LIMIT 10
+
+/*      Increment cyclic buffer nr.
+ */
+buffer_struct *ftape_next_buffer(ft_buffer_queue_t pos)
+{
+       switch (pos) {
+       case ft_queue_head:
+               if (++ft_head >= ft_nr_buffers) {
+                       ft_head = 0;
+               }
+               return ft_buffer[ft_head];
+       case ft_queue_tail:
+               if (++ft_tail >= ft_nr_buffers) {
+                       ft_tail = 0;
+               }
+               return ft_buffer[ft_tail];
+       default:
+               return NULL;
+       }
+}
+int ftape_buffer_id(ft_buffer_queue_t pos)
+{
+       switch(pos) {
+       case ft_queue_head: return ft_head;
+       case ft_queue_tail: return ft_tail;
+       default: return -1;
+       }
+}
+buffer_struct *ftape_get_buffer(ft_buffer_queue_t pos)
+{
+       switch(pos) {
+       case ft_queue_head: return ft_buffer[ft_head];
+       case ft_queue_tail: return ft_buffer[ft_tail];
+       default: return NULL;
+       }
+}
+void ftape_reset_buffer(void)
+{
+       ft_head = ft_tail = 0;
+}
+
+buffer_state_enum ftape_set_state(buffer_state_enum new_state)
+{
+       buffer_state_enum old_state = ft_driver_state;
+
+       ft_driver_state = new_state;
+       return old_state;
+}
+/*      Calculate Floppy Disk Controller and DMA parameters for a segment.
+ *      head:   selects buffer struct in array.
+ *      offset: number of physical sectors to skip (including bad ones).
+ *      count:  number of physical sectors to handle (including bad ones).
+ */
+static int setup_segment(buffer_struct * buff, 
+                        int segment_id,
+                        unsigned int sector_offset, 
+                        unsigned int sector_count, 
+                        int retry)
+{
+       SectorMap offset_mask;
+       SectorMap mask;
+       TRACE_FUN(ft_t_any);
+
+       buff->segment_id = segment_id;
+       buff->sector_offset = sector_offset;
+       buff->remaining = sector_count;
+       buff->head = segment_id / ftape_segments_per_head;
+       buff->cyl = (segment_id % ftape_segments_per_head) / ftape_segments_per_cylinder;
+       buff->sect = (segment_id % ftape_segments_per_cylinder) * FT_SECTORS_PER_SEGMENT + 1;
+       buff->deleted = 0;
+       offset_mask = (1 << buff->sector_offset) - 1;
+       mask = ftape_get_bad_sector_entry(segment_id) & offset_mask;
+       while (mask) {
+               if (mask & 1) {
+                       offset_mask >>= 1;      /* don't count bad sector */
+               }
+               mask >>= 1;
+       }
+       buff->data_offset = count_ones(offset_mask);    /* good sectors to skip */
+       buff->ptr = buff->address + buff->data_offset * FT_SECTOR_SIZE;
+       TRACE(ft_t_flow, "data offset = %d sectors", buff->data_offset);
+       if (retry) {
+               buff->soft_error_map &= offset_mask;    /* keep skipped part */
+       } else {
+               buff->hard_error_map = buff->soft_error_map = 0;
+       }
+       buff->bad_sector_map = ftape_get_bad_sector_entry(buff->segment_id);
+       if (buff->bad_sector_map != 0) {
+               TRACE(ft_t_noise, "segment: %d, bad sector map: %08lx",
+                       buff->segment_id, (long)buff->bad_sector_map);
+       } else {
+               TRACE(ft_t_flow, "segment: %d", buff->segment_id);
+       }
+       if (buff->sector_offset > 0) {
+               buff->bad_sector_map >>= buff->sector_offset;
+       }
+       if (buff->sector_offset != 0 || buff->remaining != FT_SECTORS_PER_SEGMENT) {
+               TRACE(ft_t_flow, "sector offset = %d, count = %d",
+                       buff->sector_offset, buff->remaining);
+       }
+       /*    Segments with 3 or less sectors are not written with valid
+        *    data because there is no space left for the ecc.  The
+        *    data written is whatever happens to be in the buffer.
+        *    Reading such a segment will return a zero byte-count.
+        *    To allow us to read/write segments with all bad sectors
+        *    we fake one readable sector in the segment. This
+        *    prevents having to handle these segments in a very
+        *    special way.  It is not important if the reading of this
+        *    bad sector fails or not (the data is ignored). It is
+        *    only read to keep the driver running.
+        *
+        *    The QIC-40/80 spec. has no information on how to handle
+        *    this case, so this is my interpretation.  
+        */
+       if (buff->bad_sector_map == EMPTY_SEGMENT) {
+               TRACE(ft_t_flow, "empty segment %d, fake first sector good",
+                     buff->segment_id);
+               if (buff->ptr != buff->address) {
+                       TRACE(ft_t_bug, "This is a bug: %p/%p",
+                             buff->ptr, buff->address);
+               }
+               buff->bad_sector_map = FAKE_SEGMENT;
+       }
+       fdc_setup_error = 0;
+       buff->next_segment = segment_id + 1;
+       TRACE_EXIT 0;
+}
+
+/*      Calculate Floppy Disk Controller and DMA parameters for a new segment.
+ */
+int ftape_setup_new_segment(buffer_struct * buff, int segment_id, int skip)
+{
+       int result = 0;
+       static int old_segment_id = -1;
+       static buffer_state_enum old_ft_driver_state = idle;
+       int retry = 0;
+       unsigned offset = 0;
+       int count = FT_SECTORS_PER_SEGMENT;
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_flow, "%s segment %d (old = %d)",
+             (ft_driver_state == reading || ft_driver_state == verifying) 
+             ? "reading" : "writing",
+             segment_id, old_segment_id);
+       if (ft_driver_state != old_ft_driver_state) {   /* when verifying */
+               old_segment_id = -1;
+               old_ft_driver_state = ft_driver_state;
+       }
+       if (segment_id == old_segment_id) {
+               ++buff->retry;
+               ++ft_history.retries;
+               TRACE(ft_t_flow, "setting up for retry nr %d", buff->retry);
+               retry = 1;
+               if (skip && buff->skip > 0) {   /* allow skip on retry */
+                       offset = buff->skip;
+                       count -= offset;
+                       TRACE(ft_t_flow, "skipping %d sectors", offset);
+               }
+       } else {
+               buff->retry = 0;
+               buff->skip = 0;
+               old_segment_id = segment_id;
+       }
+       result = setup_segment(buff, segment_id, offset, count, retry);
+       TRACE_EXIT result;
+}
+
+/*      Determine size of next cluster of good sectors.
+ */
+int ftape_calc_next_cluster(buffer_struct * buff)
+{
+       /* Skip bad sectors.
+        */
+       while (buff->remaining > 0 && (buff->bad_sector_map & 1) != 0) {
+               buff->bad_sector_map >>= 1;
+               ++buff->sector_offset;
+               --buff->remaining;
+       }
+       /* Find next cluster of good sectors
+        */
+       if (buff->bad_sector_map == 0) {        /* speed up */
+               buff->sector_count = buff->remaining;
+       } else {
+               SectorMap map = buff->bad_sector_map;
+
+               buff->sector_count = 0;
+               while (buff->sector_count < buff->remaining && (map & 1) == 0) {
+                       ++buff->sector_count;
+                       map >>= 1;
+               }
+       }
+       return buff->sector_count;
+}
+
+/*  if just passed the last segment on a track, wait for BOT
+ *  or EOT mark.
+ */
+int ftape_handle_logical_eot(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (ft_runner_status == logical_eot) {
+               int status;
+
+               TRACE(ft_t_noise, "tape at logical EOT");
+               TRACE_CATCH(ftape_ready_wait(ftape_timeout.seek, &status),);
+               if ((status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) {
+                       TRACE_ABORT(-EIO, ft_t_err, "eot/bot not reached");
+               }
+               ft_runner_status = end_of_tape;
+       }
+       if (ft_runner_status == end_of_tape) {
+               TRACE(ft_t_noise, "runner stopped because of logical EOT");
+               ft_runner_status = idle;
+       }
+       TRACE_EXIT 0;
+}
+
+static int check_bot_eot(int status)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) {
+               ft_location.bot = ((ft_location.track & 1) == 0 ?
+                               (status & QIC_STATUS_AT_BOT) != 0:
+                               (status & QIC_STATUS_AT_EOT) != 0);
+               ft_location.eot = !ft_location.bot;
+               ft_location.segment = (ft_location.track +
+                       (ft_location.bot ? 0 : 1)) * ft_segments_per_track - 1;
+               ft_location.sector = -1;
+               ft_location.known  = 1;
+               TRACE(ft_t_flow, "tape at logical %s",
+                     ft_location.bot ? "bot" : "eot");
+               TRACE(ft_t_flow, "segment = %d", ft_location.segment);
+       } else {
+               ft_location.known = 0;
+       }
+       TRACE_EXIT ft_location.known;
+}
+
+/*      Read Id of first sector passing tape head.
+ */
+int ftape_read_id(void)
+{
+       int status;
+       __u8 out[2];
+       TRACE_FUN(ft_t_any);
+
+       /* Assume tape is running on entry, be able to handle
+        * situation where it stopped or is stopping.
+        */
+       ft_location.known = 0;  /* default is location not known */
+       out[0] = FDC_READID;
+       out[1] = ft_drive_sel;
+       TRACE_CATCH(fdc_command(out, 2),);
+       switch (fdc_interrupt_wait(20 * FT_SECOND)) {
+       case 0:
+               if (fdc_sect == 0) {
+                       if (ftape_report_drive_status(&status) >= 0 &&
+                           (status & QIC_STATUS_READY)) {
+                               ftape_tape_running = 0;
+                               TRACE(ft_t_flow, "tape has stopped");
+                               check_bot_eot(status);
+                       }
+               } else {
+                       ft_location.known = 1;
+                       ft_location.segment = (ftape_segments_per_head
+                                              * fdc_head
+                                              + ftape_segments_per_cylinder
+                                              * fdc_cyl
+                                              + (fdc_sect - 1)
+                                              / FT_SECTORS_PER_SEGMENT);
+                       ft_location.sector = ((fdc_sect - 1)
+                                             % FT_SECTORS_PER_SEGMENT);
+                       ft_location.eot = ft_location.bot = 0;
+               }
+               break;
+       case -ETIME:
+               /*  Didn't find id on tape, must be near end: Wait
+                *  until stopped.
+                */
+               if (ftape_ready_wait(FT_FOREVER, &status) >= 0) {
+                       ftape_tape_running = 0;
+                       TRACE(ft_t_flow, "tape has stopped");
+                       check_bot_eot(status);
+               }
+               break;
+       default:
+               /*  Interrupted or otherwise failing
+                *  fdc_interrupt_wait() 
+                */
+               TRACE(ft_t_err, "fdc_interrupt_wait failed");
+               break;
+       }
+       if (!ft_location.known) {
+               TRACE_ABORT(-EIO, ft_t_flow, "no id found");
+       }
+       if (ft_location.sector == 0) {
+               TRACE(ft_t_flow, "passing segment %d/%d",
+                     ft_location.segment, ft_location.sector);
+       } else {
+               TRACE(ft_t_fdc_dma, "passing segment %d/%d",
+                     ft_location.segment, ft_location.sector);
+       }
+       TRACE_EXIT 0;
+}
+
+static int logical_forward(void)
+{
+       ftape_tape_running = 1;
+       return ftape_command(QIC_LOGICAL_FORWARD);
+}
+
+int ftape_stop_tape(int *pstatus)
+{
+       int retry = 0;
+       int result;
+       TRACE_FUN(ft_t_flow);
+
+       do {
+               result = ftape_command_wait(QIC_STOP_TAPE,
+                                           ftape_timeout.stop, pstatus);
+               if (result == 0) {
+                       if ((*pstatus & QIC_STATUS_READY) == 0) {
+                               result = -EIO;
+                       } else {
+                               ftape_tape_running = 0;
+                       }
+               }
+       } while (result < 0 && ++retry <= 3);
+       if (result < 0) {
+               TRACE(ft_t_err, "failed ! (fatal)");
+       }
+       TRACE_EXIT result;
+}
+
+int ftape_dumb_stop(void)
+{
+       int result;
+       int status;
+       TRACE_FUN(ft_t_flow);
+
+       /*  Abort current fdc operation if it's busy (probably read
+        *  or write operation pending) with a reset.
+        */
+       if (fdc_ready_wait(100 /* usec */) < 0) {
+               TRACE(ft_t_noise, "aborting fdc operation");
+               fdc_reset();
+       }
+       /*  Reading id's after the last segment on a track may fail
+        *  but eventually the drive will become ready (logical eot).
+        */
+       result = ftape_report_drive_status(&status);
+       ft_location.known = 0;
+       do {
+               if (result == 0 && status & QIC_STATUS_READY) {
+                       /* Tape is not running any more.
+                        */
+                       TRACE(ft_t_noise, "tape already halted");
+                       check_bot_eot(status);
+                       ftape_tape_running = 0;
+               } else if (ftape_tape_running) {
+                       /*  Tape is (was) still moving.
+                        */
+#ifdef TESTING
+                       ftape_read_id();
+#endif
+                       result = ftape_stop_tape(&status);
+               } else {
+                       /*  Tape not yet ready but stopped.
+                        */
+                       result = ftape_ready_wait(ftape_timeout.pause,&status);
+               }
+       } while (ftape_tape_running && (current->signal & _NEVER_BLOCK) == 0);
+#ifndef TESTING
+       ft_location.known = 0;
+#endif
+       if (ft_runner_status == aborting || ft_runner_status == do_abort) {
+               ft_runner_status = idle;
+       }
+       TRACE_EXIT result;
+}
+
+/*      Wait until runner has finished tail buffer.
+ *
+ */
+int ftape_wait_segment(buffer_state_enum state)
+{
+       int status;
+       int result = 0;
+       TRACE_FUN(ft_t_flow);
+
+       while (ft_buffer[ft_tail]->status == state) {
+               TRACE(ft_t_flow, "state: %d", ft_buffer[ft_tail]->status);
+               /*  First buffer still being worked on, wait up to timeout.
+                *
+                *  Note: we check two times for being killed. 50
+                *  seconds are quite long. Note that
+                *  fdc_interrupt_wait() is not killable by any
+                *  means. ftape_read_segment() wants us to return
+                *  -EINTR in case of a signal.  
+                */
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+               result = fdc_interrupt_wait(50 * FT_SECOND);
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+               if (result < 0) {
+                       TRACE_ABORT(result,
+                                   ft_t_err, "fdc_interrupt_wait failed");
+               }
+               if (fdc_setup_error) {
+                       /* recover... FIXME */
+                       TRACE_ABORT(-EIO, ft_t_err, "setup error");
+               }
+       }
+       if (ft_buffer[ft_tail]->status != error) {
+               TRACE_EXIT 0;
+       }
+       TRACE_CATCH(ftape_report_drive_status(&status),);
+       TRACE(ft_t_noise, "ftape_report_drive_status: 0x%02x", status);
+       if ((status & QIC_STATUS_READY) && 
+           (status & QIC_STATUS_ERROR)) {
+               unsigned int error;
+               qic117_cmd_t command;
+               
+               /*  Report and clear error state.
+                *  In case the drive can't operate at the selected
+                *  rate, select the next lower data rate.
+                */
+               ftape_report_error(&error, &command, 1);
+               if (error == 31 && command == QIC_LOGICAL_FORWARD) {
+                       /* drive does not accept this data rate */
+                       if (ft_data_rate > 250) {
+                               TRACE(ft_t_info,
+                                     "Probable data rate conflict");
+                               TRACE(ft_t_info,
+                                     "Lowering data rate to %d Kbps",
+                                     ft_data_rate / 2);
+                               ftape_half_data_rate();
+                               if (ft_buffer[ft_tail]->retry > 0) {
+                                       /* give it a chance */
+                                       --ft_buffer[ft_tail]->retry;
+                               }
+                       } else {
+                               /* no rate is accepted... */
+                               TRACE(ft_t_err, "We're dead :(");
+                       }
+               } else {
+                       TRACE(ft_t_err, "Unknown error");
+               }
+               TRACE_EXIT -EIO;   /* g.p. error */
+       }
+       TRACE_EXIT 0;
+}
+
+/* forward */ static int seek_forward(int segment_id, int fast);
+
+static int fast_seek(int count, int reverse)
+{
+       int result = 0;
+       int status;
+       TRACE_FUN(ft_t_flow);
+
+       if (count > 0) {
+               /*  If positioned at begin or end of tape, fast seeking needs
+                *  special treatment.
+                *  Starting from logical bot needs a (slow) seek to the first
+                *  segment before the high speed seek. Most drives do this
+                *  automatically but some older don't, so we treat them
+                *  all the same.
+                *  Starting from logical eot is even more difficult because
+                *  we cannot (slow) reverse seek to the last segment.
+                *  TO BE IMPLEMENTED.
+                */
+               inhibit_correction = 0;
+               if (ft_location.known &&
+                   ((ft_location.bot && !reverse) ||
+                    (ft_location.eot && reverse))) {
+                       if (!reverse) {
+                               /*  (slow) skip to first segment on a track
+                                */
+                               seek_forward(ft_location.track * ft_segments_per_track, 0);
+                               --count;
+                       } else {
+                               /*  When seeking backwards from
+                                *  end-of-tape the number of erased
+                                *  gaps found seems to be higher than
+                                *  expected.  Therefor the drive must
+                                *  skip some more segments than
+                                *  calculated, but we don't know how
+                                *  many.  Thus we will prevent the
+                                *  re-calculation of offset and
+                                *  overshoot when seeking backwards.
+                                */
+                               inhibit_correction = 1;
+                               count += 3;     /* best guess */
+                       }
+               }
+       } else {
+               TRACE(ft_t_flow, "warning: zero or negative count: %d", count);
+       }
+       if (count > 0) {
+               int i;
+               int nibbles = count > 255 ? 3 : 2;
+
+               if (count > 4095) {
+                       TRACE(ft_t_noise, "skipping clipped at 4095 segment");
+                       count = 4095;
+               }
+               /* Issue this tape command first. */
+               if (!reverse) {
+                       TRACE(ft_t_noise, "skipping %d segment(s)", count);
+                       result = ftape_command(nibbles == 3 ?
+                          QIC_SKIP_EXTENDED_FORWARD : QIC_SKIP_FORWARD);
+               } else {
+                       TRACE(ft_t_noise, "backing up %d segment(s)", count);
+                       result = ftape_command(nibbles == 3 ?
+                          QIC_SKIP_EXTENDED_REVERSE : QIC_SKIP_REVERSE);
+               }
+               if (result < 0) {
+                       TRACE(ft_t_noise, "Skip command failed");
+               } else {
+                       --count;        /* 0 means one gap etc. */
+                       for (i = 0; i < nibbles; ++i) {
+                               if (result >= 0) {
+                                       result = ftape_parameter(count & 15);
+                                       count /= 16;
+                               }
+                       }
+                       result = ftape_ready_wait(ftape_timeout.rewind, &status);
+                       if (result >= 0) {
+                               ftape_tape_running = 0;
+                       }
+               }
+       }
+       TRACE_EXIT result;
+}
+
+static int validate(int id)
+{
+       /* Check to see if position found is off-track as reported
+        *  once.  Because all tracks in one direction lie next to
+        *  each other, if off-track the error will be approximately
+        *  2 * ft_segments_per_track.
+        */
+       if (ft_location.track == -1) {
+               return 1; /* unforseen situation, don't generate error */
+       } else {
+               /* Use margin of ft_segments_per_track on both sides
+                * because ftape needs some margin and the error we're
+                * looking for is much larger !
+                */
+               int lo = (ft_location.track - 1) * ft_segments_per_track;
+               int hi = (ft_location.track + 2) * ft_segments_per_track;
+
+               return (id >= lo && id < hi);
+       }
+}
+
+static int seek_forward(int segment_id, int fast)
+{
+       int failures = 0;
+       int count;
+       static int margin = 1;  /* fixed: stop this before target */
+       static int overshoot = 1;
+       static int min_count = 8;
+       int expected = -1;
+       int target = segment_id - margin;
+       int fast_seeking;
+       int prev_segment = ft_location.segment;
+       TRACE_FUN(ft_t_flow);
+
+       if (!ft_location.known) {
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "fatal: cannot seek from unknown location");
+       }
+       if (!validate(segment_id)) {
+               ftape_sleep(1 * FT_SECOND);
+               ft_failure = 1;
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "fatal: head off track (bad hardware?)");
+       }
+       TRACE(ft_t_noise, "from %d/%d to %d/0 - %d",
+             ft_location.segment, ft_location.sector,segment_id,margin);
+       count = target - ft_location.segment - overshoot;
+       fast_seeking = (fast &&
+                       count > (min_count + (ft_location.bot ? 1 : 0)));
+       if (fast_seeking) {
+               TRACE(ft_t_noise, "fast skipping %d segments", count);
+               expected = segment_id - margin;
+               fast_seek(count, 0);
+       }
+       if (!ftape_tape_running) {
+               logical_forward();
+       }
+       while (ft_location.segment < segment_id) {
+               /*  This requires at least one sector in a (bad) segment to
+                *  have a valid and readable sector id !
+                *  It looks like this is not guaranteed, so we must try
+                *  to find a way to skip an EMPTY_SEGMENT. !!! FIXME !!!
+                */
+               if (ftape_read_id() < 0 || !ft_location.known ||
+                   (current->signal & _DONT_BLOCK)) {
+                       ft_location.known = 0;
+                       if (!ftape_tape_running ||
+                           ++failures > FT_SECTORS_PER_SEGMENT) {
+                               TRACE_ABORT(-EIO, ft_t_err,
+                                           "read_id failed completely");
+                       }
+                       FT_SIGNAL_EXIT(_DONT_BLOCK);
+                       TRACE(ft_t_flow, "read_id failed, retry (%d)",
+                             failures);
+                       continue;
+               }
+               if (fast_seeking) {
+                       TRACE(ft_t_noise, "ended at %d/%d (%d,%d)",
+                             ft_location.segment, ft_location.sector,
+                             overshoot, inhibit_correction);
+                       if (!inhibit_correction &&
+                           (ft_location.segment < expected ||
+                            ft_location.segment > expected + margin)) {
+                               int error = ft_location.segment - expected;
+                               TRACE(ft_t_noise,
+                                     "adjusting overshoot from %d to %d",
+                                     overshoot, overshoot + error);
+                               overshoot += error;
+                               /*  All overshoots have the same
+                                *  direction, so it should never
+                                *  become negative, but who knows.
+                                */
+                               if (overshoot < -5 ||
+                                   overshoot > OVERSHOOT_LIMIT) {
+                                       if (overshoot < 0) {
+                                               /* keep sane value */
+                                               overshoot = -5;
+                                       } else {
+                                               /* keep sane value */
+                                               overshoot = OVERSHOOT_LIMIT;
+                                       }
+                                       TRACE(ft_t_noise,
+                                             "clipped overshoot to %d",
+                                             overshoot);
+                               }
+                       }
+                       fast_seeking = 0;
+               }
+               if (ft_location.known) {
+                       if (ft_location.segment > prev_segment + 1) {
+                               TRACE(ft_t_noise,
+                                     "missed segment %d while skipping",
+                                     prev_segment + 1);
+                       }
+                       prev_segment = ft_location.segment;
+               }
+       }
+       if (ft_location.segment > segment_id) {
+               TRACE_ABORT(-EIO,
+                           ft_t_noise, "failed: skip ended at segment %d/%d",
+                           ft_location.segment, ft_location.sector);
+       }
+       TRACE_EXIT 0;
+}
+
+static int skip_reverse(int segment_id, int *pstatus)
+{
+       int failures = 0;
+       static int overshoot = 1;
+       static int min_rewind = 2;      /* 1 + overshoot */
+       static const int margin = 1;    /* stop this before target */
+       int expected = 0;
+       int count = 1;
+       int short_seek;
+       int target = segment_id - margin;
+       TRACE_FUN(ft_t_flow);
+
+       if (ft_location.known && !validate(segment_id)) {
+               ftape_sleep(1 * FT_SECOND);
+               ft_failure = 1;
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "fatal: head off track (bad hardware?)");
+       }
+       do {
+               if (!ft_location.known) {
+                       TRACE(ft_t_warn, "warning: location not known");
+               }
+               TRACE(ft_t_noise, "from %d/%d to %d/0 - %d",
+                     ft_location.segment, ft_location.sector,
+                     segment_id, margin);
+               /*  min_rewind == 1 + overshoot_when_doing_minimum_rewind
+                *  overshoot  == overshoot_when_doing_larger_rewind
+                *  Initially min_rewind == 1 + overshoot, optimization
+                *  of both values will be done separately.
+                *  overshoot and min_rewind can be negative as both are
+                *  sums of three components:
+                *  any_overshoot == rewind_overshoot - 
+                *                   stop_overshoot   -
+                *                   start_overshoot
+                */
+               if (ft_location.segment - target - (min_rewind - 1) < 1) {
+                       short_seek = 1;
+               } else {
+                       count = ft_location.segment - target - overshoot;
+                       short_seek = (count < 1);
+               }
+               if (short_seek) {
+                       count = 1;      /* do shortest rewind */
+                       expected = ft_location.segment - min_rewind;
+                       if (expected/ft_segments_per_track != ft_location.track) {
+                               expected = (ft_location.track * 
+                                           ft_segments_per_track);
+                       }
+               } else {
+                       expected = target;
+               }
+               fast_seek(count, 1);
+               logical_forward();
+               if (ftape_read_id() < 0 || !ft_location.known ||
+                   (current->signal & _DONT_BLOCK)) {
+                       if ((!ftape_tape_running && !ft_location.known) ||
+                           ++failures > FT_SECTORS_PER_SEGMENT) {
+                               TRACE_ABORT(-EIO, ft_t_err,
+                                           "read_id failed completely");
+                       }
+                       FT_SIGNAL_EXIT(_DONT_BLOCK);
+                       TRACE_CATCH(ftape_report_drive_status(pstatus),);
+                       TRACE(ft_t_noise, "ftape_read_id failed, retry (%d)",
+                             failures);
+                       continue;
+               }
+               TRACE(ft_t_noise, "ended at %d/%d (%d,%d,%d)", 
+                     ft_location.segment, ft_location.sector,
+                     min_rewind, overshoot, inhibit_correction);
+               if (!inhibit_correction &&
+                   (ft_location.segment < expected ||
+                    ft_location.segment > expected + margin)) {
+                       int error = expected - ft_location.segment;
+                       if (short_seek) {
+                               TRACE(ft_t_noise,
+                                     "adjusting min_rewind from %d to %d",
+                                     min_rewind, min_rewind + error);
+                               min_rewind += error;
+                               if (min_rewind < -5) {
+                                       /* is this right ? FIXME ! */
+                                       /* keep sane value */
+                                       min_rewind = -5;
+                                       TRACE(ft_t_noise, 
+                                             "clipped min_rewind to %d",
+                                             min_rewind);
+                               }
+                       } else {
+                               TRACE(ft_t_noise,
+                                     "adjusting overshoot from %d to %d",
+                                     overshoot, overshoot + error);
+                               overshoot += error;
+                               if (overshoot < -5 ||
+                                   overshoot > OVERSHOOT_LIMIT) {
+                                       if (overshoot < 0) {
+                                               /* keep sane value */
+                                               overshoot = -5;
+                                       } else {
+                                               /* keep sane value */
+                                               overshoot = OVERSHOOT_LIMIT;
+                                       }
+                                       TRACE(ft_t_noise,
+                                             "clipped overshoot to %d",
+                                             overshoot);
+                               }
+                       }
+               }
+       } while (ft_location.segment > segment_id);
+       if (ft_location.known) {
+               TRACE(ft_t_noise, "current location: %d/%d",
+                     ft_location.segment, ft_location.sector);
+       }
+       TRACE_EXIT 0;
+}
+
+static int determine_position(void)
+{
+       int retry = 0;
+       int status;
+       int result;
+       TRACE_FUN(ft_t_flow);
+
+       if (!ftape_tape_running) {
+               /*  This should only happen if tape is stopped by isr.
+                */
+               TRACE(ft_t_flow, "waiting for tape stop");
+               if (ftape_ready_wait(ftape_timeout.pause, &status) < 0) {
+                       TRACE(ft_t_flow, "drive still running (fatal)");
+                       ftape_tape_running = 1; /* ? */
+               }
+       } else {
+               ftape_report_drive_status(&status);
+       }
+       if (status & QIC_STATUS_READY) {
+               /*  Drive must be ready to check error state !
+                */
+               TRACE(ft_t_flow, "drive is ready");
+               if (status & QIC_STATUS_ERROR) {
+                       unsigned int error;
+                       qic117_cmd_t command;
+
+                       /*  Report and clear error state, try to continue.
+                        */
+                       TRACE(ft_t_flow, "error status set");
+                       ftape_report_error(&error, &command, 1);
+                       ftape_ready_wait(ftape_timeout.reset, &status);
+                       ftape_tape_running = 0; /* ? */
+               }
+               if (check_bot_eot(status)) {
+                       if (ft_location.bot) {
+                               if ((status & QIC_STATUS_READY) == 0) {
+                                       /* tape moving away from
+                                        * bot/eot, let's see if we
+                                        * can catch up with the first
+                                        * segment on this track.
+                                        */
+                               } else {
+                                       TRACE(ft_t_flow,
+                                             "start tape from logical bot");
+                                       logical_forward();      /* start moving */
+                               }
+                       } else {
+                               if ((status & QIC_STATUS_READY) == 0) {
+                                       TRACE(ft_t_noise, "waiting for logical end of track");
+                                       result = ftape_ready_wait(ftape_timeout.reset, &status);
+                                       /* error handling needed ? */
+                               } else {
+                                       TRACE(ft_t_noise,
+                                             "tape at logical end of track");
+                               }
+                       }
+               } else {
+                       TRACE(ft_t_flow, "start tape");
+                       logical_forward();      /* start moving */
+                       ft_location.known = 0;  /* not cleared by logical forward ! */
+               }
+       }
+       /* tape should be moving now, start reading id's
+        */
+       while (!ft_location.known &&
+              retry++ < FT_SECTORS_PER_SEGMENT &&
+              (result = ftape_read_id()) < 0) {
+
+               TRACE(ft_t_flow, "location unknown");
+
+               /* exit on signal
+                */
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+
+               /*  read-id somehow failed, tape may
+                *  have reached end or some other
+                *  error happened.
+                */
+               TRACE(ft_t_flow, "read-id failed");
+               TRACE_CATCH(ftape_report_drive_status(&status),);
+               TRACE(ft_t_err, "ftape_report_drive_status: 0x%02x", status);
+               if (status & QIC_STATUS_READY) {
+                       ftape_tape_running = 0;
+                       TRACE(ft_t_noise, "tape stopped for unknown reason! "
+                             "status = 0x%02x", status);
+                       if (status & QIC_STATUS_ERROR ||
+                           !check_bot_eot(status)) {
+                               /* oops, tape stopped but not at end!
+                                */
+                               TRACE_EXIT -EIO;
+                       }
+               }
+       }
+       TRACE(ft_t_flow,
+             "tape is positioned at segment %d", ft_location.segment);
+       TRACE_EXIT ft_location.known ? 0 : -EIO;
+}
+
+/*      Get the tape running and position it just before the
+ *      requested segment.
+ *      Seek tape-track and reposition as needed.
+ */
+int ftape_start_tape(int segment_id, int sector_offset)
+{
+       int track = segment_id / ft_segments_per_track;
+       int result = -EIO;
+       int status;
+       static int last_segment = -1;
+       static int bad_bus_timing = 0;
+       /* number of segments passing the head between starting the tape
+        * and being able to access the first sector.
+        */
+       static int start_offset = 1;
+       int retry;
+       TRACE_FUN(ft_t_flow);
+
+       /* If sector_offset > 0, seek into wanted segment instead of
+        * into previous.
+        * This allows error recovery if a part of the segment is bad
+        * (erased) causing the tape drive to generate an index pulse
+        * thus causing a no-data error before the requested sector
+        * is reached.
+        */
+       ftape_tape_running = 0;
+       TRACE(ft_t_noise, "target segment: %d/%d%s", segment_id, sector_offset,
+               ft_buffer[ft_head]->retry > 0 ? " retry" : "");
+       if (ft_buffer[ft_head]->retry > 0) {    /* this is a retry */
+               int dist = segment_id - last_segment;
+
+               if ((int)ft_history.overrun_errors < overrun_count_offset) {
+                       overrun_count_offset = ft_history.overrun_errors;
+               } else if (dist < 0 || dist > 50) {
+                       overrun_count_offset = ft_history.overrun_errors;
+               } else if ((ft_history.overrun_errors -
+                           overrun_count_offset) >= 8) {
+                       if (ftape_increase_threshold() >= 0) {
+                               --ft_buffer[ft_head]->retry;
+                               overrun_count_offset =
+                                       ft_history.overrun_errors;
+                               TRACE(ft_t_warn, "increased threshold because "
+                                     "of excessive overrun errors");
+                       } else if (!bad_bus_timing && ft_data_rate >= 1000) {
+                               ftape_half_data_rate();
+                               --ft_buffer[ft_head]->retry;
+                               bad_bus_timing = 1;
+                               overrun_count_offset =
+                                       ft_history.overrun_errors;
+                               TRACE(ft_t_warn, "reduced datarate because "
+                                     "of excessive overrun errors");
+                       }
+               }
+       }
+       last_segment = segment_id;
+       if (ft_location.track != track ||
+           (ftape_might_be_off_track && ft_buffer[ft_head]->retry== 0)) {
+               /* current track unknown or not equal to destination
+                */
+               ftape_ready_wait(ftape_timeout.seek, &status);
+               ftape_seek_head_to_track(track);
+               /* overrun_count_offset = ft_history.overrun_errors; */
+       }
+       result = -EIO;
+       retry = 0;
+       while (result < 0     &&
+              retry++ <= 5   &&
+              !ft_failure &&
+              (current->signal & _DONT_BLOCK) == 0) {
+               
+               if (retry && start_offset < 5) {
+                       start_offset ++;
+               }
+               /*  Check if we are able to catch the requested
+                *  segment in time.
+                */
+               if ((ft_location.known || (determine_position() == 0)) &&
+                   ft_location.segment >=
+                   (segment_id -
+                    ((ftape_tape_running || ft_location.bot)
+                     ? 0 : start_offset))) {
+                       /*  Too far ahead (in or past target segment).
+                        */
+                       if (ftape_tape_running) {
+                               if ((result = ftape_stop_tape(&status)) < 0) {
+                                       TRACE(ft_t_err,
+                                             "stop tape failed with code %d",
+                                             result);
+                                       break;
+                               }
+                               TRACE(ft_t_noise, "tape stopped");
+                               ftape_tape_running = 0;
+                       }
+                       TRACE(ft_t_noise, "repositioning");
+                       ++ft_history.rewinds;
+                       if (segment_id % ft_segments_per_track < start_offset){
+                               TRACE(ft_t_noise, "end of track condition\n"
+                                     KERN_INFO "segment_id        : %d\n"
+                                     KERN_INFO "ft_segments_per_track: %d\n"
+                                     KERN_INFO "start_offset      : %d",
+                                     segment_id, ft_segments_per_track, 
+                                     start_offset);
+                                     
+                               /*  If seeking to first segments on
+                                *  track better do a complete rewind
+                                *  to logical begin of track to get a
+                                *  more steady tape motion.  
+                                */
+                               result = ftape_command_wait(
+                                       (ft_location.track & 1)
+                                       ? QIC_PHYSICAL_FORWARD
+                                       : QIC_PHYSICAL_REVERSE,
+                                       ftape_timeout.rewind, &status);
+                               check_bot_eot(status);  /* update location */
+                       } else {
+                               result= skip_reverse(segment_id - start_offset,
+                                                    &status);
+                       }
+               }
+               if (!ft_location.known) {
+                       TRACE(ft_t_bug, "panic: location not known");
+                       result = -EIO;
+                       continue; /* while() will check for failure */
+               }
+               TRACE(ft_t_noise, "current segment: %d/%d",
+                     ft_location.segment, ft_location.sector);
+               /*  We're on the right track somewhere before the
+                *  wanted segment.  Start tape movement if needed and
+                *  skip to just before or inside the requested
+                *  segment. Keep tape running.  
+                */
+               result = 0;
+               if (ft_location.segment < 
+                   (segment_id - ((ftape_tape_running || ft_location.bot)
+                                  ? 0 : start_offset))) {
+                       if (sector_offset > 0) {
+                               result = seek_forward(segment_id,
+                                                     retry <= 3);
+                       } else {
+                               result = seek_forward(segment_id - 1,
+                                                     retry <= 3);
+                       }
+               }
+               if (result == 0 &&
+                   ft_location.segment !=
+                   (segment_id - (sector_offset > 0 ? 0 : 1))) {
+                       result = -EIO;
+               }
+       }
+       if (result < 0) {
+               TRACE(ft_t_err, "failed to reposition");
+       } else {
+               ft_runner_status = running;
+       }
+       TRACE_EXIT result;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-rw.h b/drivers/char/ftape/lowlevel/ftape-rw.h
new file mode 100644 (file)
index 0000000..8624613
--- /dev/null
@@ -0,0 +1,121 @@
+#ifndef _FTAPE_RW_H
+#define _FTAPE_RW_H
+
+/*
+ * Copyright (C) 1993-1996 Bas Laarhoven,
+ *           (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-rw.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:25 $
+ *
+ *      This file contains the definitions for the read and write
+ *      functions for the QIC-117 floppy-tape driver for Linux.
+ *
+ * Claus-Justus Heine (1996/09/20): Add definition of format code 6
+ * Claus-Justus Heine (1996/10/04): Changed GET/PUT macros to cast to (__u8 *)
+ *
+ */
+
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/ftape-bsm.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0)
+#include <asm/unaligned.h>
+
+#define GET2(address, offset) get_unaligned((__u16*)((__u8 *)address + offset))
+#define GET4(address, offset) get_unaligned((__u32*)((__u8 *)address + offset))
+#define GET8(address, offset) get_unaligned((__u64*)((__u8 *)address + offset))
+#define PUT2(address, offset , value) put_unaligned((value), (__u16*)((__u8 *)address + offset))
+#define PUT4(address, offset , value) put_unaligned((value), (__u32*)((__u8 *)address + offset))
+#define PUT8(address, offset , value) put_unaligned((value), (__u64*)((__u8 *)address + offset))
+#else
+#define GET2(address, offset) *(__u16*)((__u8 *)address + offset)
+#define GET4(address, offset) *(__u32*)((__u8 *)address + offset)
+#define GET8(address, offset) *(__u64*)((__u8 *)address + offset)
+#define PUT2(address, offset , value) *(__u16*)((__u8 *)address + offset) = (__u16)(value)
+#define PUT4(address, offset , value) *(__u32*)((__u8 *)address + offset) = (__u32)(value)
+#define PUT8(address, offset , value) *(__u64*)((__u8 *)address + offset) = (__u32)(value)
+#endif
+
+enum runner_status_enum {
+       idle = 0,
+       running,
+       do_abort,
+       aborting,
+       logical_eot,
+       end_of_tape,
+};
+
+typedef enum ft_buffer_queue {
+       ft_queue_head = 0,
+       ft_queue_tail = 1
+} ft_buffer_queue_t;
+
+
+typedef struct {
+       int track;              /* tape head position */
+       volatile int segment;   /* current segment */
+       volatile int sector;    /* sector offset within current segment */
+       volatile unsigned int bot;      /* logical begin of track */
+       volatile unsigned int eot;      /* logical end of track */
+       volatile unsigned int known;    /* validates bot, segment, sector */
+} location_record;
+
+/*      Count nr of 1's in pattern.
+ */
+extern inline int count_ones(unsigned long mask)
+{
+       int bits;
+
+       for (bits = 0; mask != 0; mask >>= 1) {
+               if (mask & 1) {
+                       ++bits;
+               }
+       }
+       return bits;
+}
+
+#define FT_MAX_NR_BUFFERS 16 /* arbitrary value */
+/*      ftape-rw.c defined global vars.
+ */
+extern buffer_struct *ft_buffer[FT_MAX_NR_BUFFERS];
+extern int ft_nr_buffers;
+extern location_record ft_location;
+extern volatile int ftape_tape_running;
+
+/*      ftape-rw.c defined global functions.
+ */
+extern int  ftape_setup_new_segment(buffer_struct * buff,
+                                   int segment_id,
+                                   int offset);
+extern int  ftape_calc_next_cluster(buffer_struct * buff);
+extern buffer_struct *ftape_next_buffer (ft_buffer_queue_t pos);
+extern buffer_struct *ftape_get_buffer  (ft_buffer_queue_t pos);
+extern int            ftape_buffer_id   (ft_buffer_queue_t pos);
+extern void           ftape_reset_buffer(void);
+extern int  ftape_read_id(void);
+extern void ftape_tape_parameters(__u8 drive_configuration);
+extern int  ftape_wait_segment(buffer_state_enum state);
+extern int  ftape_dumb_stop(void);
+extern int  ftape_start_tape(int segment_id, int offset);
+extern int  ftape_stop_tape(int *pstatus);
+extern int  ftape_handle_logical_eot(void);
+extern buffer_state_enum ftape_set_state(buffer_state_enum new_state);
+#endif                         /* _FTAPE_RW_H */
diff --git a/drivers/char/ftape/lowlevel/ftape-setup.c b/drivers/char/ftape/lowlevel/ftape-setup.c
new file mode 100644 (file)
index 0000000..5cb89c6
--- /dev/null
@@ -0,0 +1,105 @@
+/* 
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-setup.c,v $
+ * $Revision: 1.7 $
+ * $Date: 1997/10/10 09:57:06 $
+ *
+ *      This file contains the code for processing the kernel command
+ *      line options for the QIC-40/80/3010/3020 floppy-tape driver
+ *      "ftape" for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/ftape.h>
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16)
+#include <linux/init.h>
+#else
+#define __initdata
+#define __initfunc(__arg) __arg
+#endif
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/fdc-io.h"
+
+static struct param_table {
+       const char *name;
+       int *var;
+       int def_param;
+       int min;
+       int max;
+} config_params[] __initdata = {
+#ifndef CONFIG_FT_NO_TRACE_AT_ALL
+       { "tracing",   &ftape_tracing,     3,              ft_t_bug, ft_t_any},
+#endif
+       { "ioport",    &ft_fdc_base,       CONFIG_FT_FDC_BASE,     0x0, 0xfff},
+       { "irq",       &ft_fdc_irq,        CONFIG_FT_FDC_IRQ,        2,    15},
+       { "dma",       &ft_fdc_dma,        CONFIG_FT_FDC_DMA,        0,     3},
+       { "threshold", &ft_fdc_threshold,  CONFIG_FT_FDC_THR,         1,    16},
+       { "datarate",  &ft_fdc_rate_limit, CONFIG_FT_FDC_MAX_RATE, 500,  2000},
+       { "fc10",      &ft_probe_fc10,     CONFIG_FT_PROBE_FC10,     0,     1},
+       { "mach2",     &ft_mach2,          CONFIG_FT_MACH2,          0,     1}
+};
+
+__initfunc(void ftape_setup(char *str, int *ints))
+{
+       int i;
+       int param;
+       TRACE_FUN(ft_t_flow);
+
+       if (str) {
+               for (i=0; i < NR_ITEMS(config_params); i++) {
+                       if (strcmp(str,config_params[i].name) == 0){
+                               if (ints[0]) {
+                                       param = ints[1];
+                               } else {
+                                       param = config_params[i].def_param;
+                               }
+                               if (param < config_params[i].min ||
+                                   param > config_params[i].max) {
+                                       TRACE(ft_t_err,
+                                       "parameter %s out of range %d ... %d",
+                                             config_params[i].name,
+                                             config_params[i].min,
+                                             config_params[i].max);
+                                       TRACE_EXIT;
+                               }
+                               if(config_params[i].var) {
+                                       TRACE(ft_t_info, "%s=%d", str, param);
+                                       *config_params[i].var = param;
+                               }
+                               TRACE_EXIT;
+                       }
+               }
+       }
+       if (str) {
+               TRACE(ft_t_err, "unknown ftape option [%s]", str);
+               
+               TRACE(ft_t_err, "allowed options are:");
+               for (i=0; i < NR_ITEMS(config_params); i++) {
+                       TRACE(ft_t_err, " %s",config_params[i].name);
+               }
+       } else {
+               TRACE(ft_t_err, "botched ftape option");
+       }
+       TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-tracing.c b/drivers/char/ftape/lowlevel/ftape-tracing.c
new file mode 100644 (file)
index 0000000..95e3df8
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ *      Copyright (C) 1993-1996 Bas Laarhoven,
+ *                (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-tracing.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:27 $
+ *
+ *      This file contains the reading code
+ *      for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+
+/*      Global vars.
+ */
+/*      tracing
+ *      set it to:     to log :
+ *       0              bugs
+ *       1              + errors
+ *       2              + warnings
+ *       3              + information
+ *       4              + more information
+ *       5              + program flow
+ *       6              + fdc/dma info
+ *       7              + data flow
+ *       8              + everything else
+ */
+ft_trace_t ftape_tracing = ft_t_info; /* Default level: information and up */
+int  ftape_function_nest_level = 0;
+
+/*      Local vars.
+ */
+static __u8 trace_id = 0;
+static char spacing[] = "*                              ";
+
+void ftape_trace_call(const char *file, const char *name)
+{
+       char *indent;
+
+       /*    Since printk seems not to work with "%*s" format
+        *    we'll use this work-around.
+        */
+       if (ftape_function_nest_level < 0) {
+               printk(KERN_INFO "function nest level (%d) < 0\n",
+                      ftape_function_nest_level);
+               ftape_function_nest_level = 0;
+       }
+       if (ftape_function_nest_level < sizeof(spacing)) {
+               indent = (spacing +
+                         sizeof(spacing) - 1 -
+                         ftape_function_nest_level);
+       } else {
+               indent = spacing;
+       }
+       printk(KERN_INFO "[%03d]%s+%s (%s)\n",
+              (int) trace_id++, indent, file, name);
+}
+
+void ftape_trace_exit(const char *file, const char *name)
+{
+       char *indent;
+
+       /*    Since printk seems not to work with "%*s" format
+        *    we'll use this work-around.
+        */
+       if (ftape_function_nest_level < 0) {
+               printk(KERN_INFO "function nest level (%d) < 0\n", ftape_function_nest_level);
+               ftape_function_nest_level = 0;
+       }
+       if (ftape_function_nest_level < sizeof(spacing)) {
+               indent = (spacing +
+                         sizeof(spacing) - 1 -
+                         ftape_function_nest_level);
+       } else {
+               indent = spacing;
+       }
+       printk(KERN_INFO "[%03d]%s-%s (%s)\n",
+              (int) trace_id++, indent, file, name);
+}
+
+void ftape_trace_log(const char *file, const char *function)
+{
+       char *indent;
+
+       /*    Since printk seems not to work with "%*s" format
+        *    we'll use this work-around.
+        */
+       if (ftape_function_nest_level < 0) {
+               printk(KERN_INFO "function nest level (%d) < 0\n", ftape_function_nest_level);
+               ftape_function_nest_level = 0;
+       }
+       if (ftape_function_nest_level < sizeof(spacing)) {
+               indent = (spacing + 
+                         sizeof(spacing) - 1 - 
+                         ftape_function_nest_level);
+       } else {
+               indent = spacing;
+       }
+       printk(KERN_INFO "[%03d]%s%s (%s) - ", 
+              (int) trace_id++, indent, file, function);
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-tracing.h b/drivers/char/ftape/lowlevel/ftape-tracing.h
new file mode 100644 (file)
index 0000000..ac67556
--- /dev/null
@@ -0,0 +1,180 @@
+#ifndef _FTAPE_TRACING_H
+#define _FTAPE_TRACING_H
+
+/*
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ *           (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-tracing.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:28 $
+ *
+ *      This file contains definitions that eases the debugging of the
+ *      QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+
+/*
+ *  Be very careful with TRACE_EXIT and TRACE_ABORT.
+ *
+ *  if (something) TRACE_EXIT error;
+ *
+ *  will NOT work. Use
+ *
+ *  if (something) {
+ *    TRACE_EXIT error;
+ *  }
+ *
+ *  instead. Maybe a bit dangerous, but save lots of lines of code.
+ */
+
+#define LL_X "%d/%d KB"
+#define LL(x) (unsigned int)((__u64)(x)>>10), (unsigned int)((x)&1023)
+
+typedef enum {
+       ft_t_nil = -1,
+       ft_t_bug,
+       ft_t_err,
+       ft_t_warn,
+       ft_t_info,
+       ft_t_noise,
+       ft_t_flow,
+       ft_t_fdc_dma,
+       ft_t_data_flow,
+       ft_t_any
+} ft_trace_t;
+
+#ifdef  CONFIG_FT_NO_TRACE_AT_ALL
+/*  the compiler will optimize away most TRACE() macros
+ */
+#define FT_TRACE_TOP_LEVEL     ft_t_bug
+#define TRACE_FUN(level)       do {} while(0)
+#define TRACE_EXIT             return
+#define TRACE(l, m, i...)                                              \
+{                                                                      \
+       if ((ft_trace_t)(l) == FT_TRACE_TOP_LEVEL) {                    \
+               printk(KERN_INFO"ftape"__FILE__"("__FUNCTION__"):\n"    \
+                      KERN_INFO m".\n" ,##i);                          \
+       }                                                               \
+}
+#define SET_TRACE_LEVEL(l)      if ((l) == (l)) do {} while(0)
+#define TRACE_LEVEL            FT_TRACE_TOP_LEVEL
+
+#else
+
+#ifdef CONFIG_FT_NO_TRACE
+/*  the compiler will optimize away many TRACE() macros
+ *  the ftape_simple_trace_call() function simply increments 
+ *  the function nest level.
+ */ 
+#define FT_TRACE_TOP_LEVEL     ft_t_warn
+#define TRACE_FUN(level)       ftape_function_nest_level++
+#define TRACE_EXIT             ftape_function_nest_level--; return
+
+#else
+#ifdef CONFIG_FT_FULL_DEBUG
+#define FT_TRACE_TOP_LEVEL ft_t_any
+#else
+#define FT_TRACE_TOP_LEVEL ft_t_flow
+#endif
+#define TRACE_FUN(level)                                       \
+       const ft_trace_t _tracing = level;                      \
+       if (ftape_tracing >= (ft_trace_t)(level) &&             \
+           (ft_trace_t)(level) <= FT_TRACE_TOP_LEVEL)          \
+               ftape_trace_call(__FILE__, __FUNCTION__);       \
+       ftape_function_nest_level ++;
+
+#define TRACE_EXIT                                             \
+       --ftape_function_nest_level;                            \
+       if (ftape_tracing >= (ft_trace_t)(_tracing) &&          \
+           (ft_trace_t)(_tracing) <= FT_TRACE_TOP_LEVEL)       \
+               ftape_trace_exit(__FILE__, __FUNCTION__);       \
+       return
+
+#endif
+
+#define TRACE(l, m, i...)                                      \
+{                                                              \
+       if (ftape_tracing >= (ft_trace_t)(l) &&                 \
+           (ft_trace_t)(l) <= FT_TRACE_TOP_LEVEL) {            \
+               ftape_trace_log(__FILE__, __FUNCTION__);        \
+               printk(m".\n" ,##i);                            \
+       }                                                       \
+}
+
+#define SET_TRACE_LEVEL(l)                             \
+{                                                      \
+       if ((ft_trace_t)(l) <= FT_TRACE_TOP_LEVEL) {    \
+               ftape_tracing = (ft_trace_t)(l);        \
+       } else {                                        \
+               ftape_tracing = FT_TRACE_TOP_LEVEL;     \
+       }                                               \
+}
+#define TRACE_LEVEL                                                                 \
+((ftape_tracing <= FT_TRACE_TOP_LEVEL) ? ftape_tracing : FT_TRACE_TOP_LEVEL)
+
+
+/*      Global variables declared in tracing.c
+ */
+extern ft_trace_t ftape_tracing;  /* sets default level */
+extern int ftape_function_nest_level;
+
+/*      Global functions declared in tracing.c
+ */
+extern void ftape_trace_call(const char *file, const char *name);
+extern void ftape_trace_exit(const char *file, const char *name);
+extern void ftape_trace_log (const char *file, const char *name);
+
+#endif /* !defined(CONFIG_FT_NO_TRACE_AT_ALL) */
+
+/*
+ *   Abort with a message.
+ */
+#define TRACE_ABORT(res, i...)                 \
+{                                              \
+       TRACE(##i);                             \
+       TRACE_EXIT res;                         \
+}
+
+/*   The following transforms the common "if(result < 0) ... " into a
+ *   one-liner.
+ */
+#define _TRACE_CATCH(level, fun, action)                               \
+{                                                                      \
+       int _res = (fun);                                               \
+       if (_res < 0) {                                                 \
+               do { action /* */ ; } while(0);                         \
+               TRACE_ABORT(_res, level, "%s failed: %d", #fun, _res);  \
+       }                                                               \
+}
+
+#define TRACE_CATCH(fun, fail) _TRACE_CATCH(ft_t_err, fun, fail)
+
+/*  Abort the current function when signalled. This doesn't belong here,
+ *  but rather into ftape-rw.h (maybe)
+ */
+#define FT_SIGNAL_EXIT(sig_mask)                                       \
+       if (current->signal & (sig_mask)) {                             \
+               TRACE_ABORT(-EINTR,                                     \
+                           ft_t_warn,                                  \
+                           "interrupted by non-blockable signal");     \
+       }
+
+#endif /* _FTAPE_TRACING_H */
diff --git a/drivers/char/ftape/lowlevel/ftape-write.c b/drivers/char/ftape/lowlevel/ftape-write.c
new file mode 100644 (file)
index 0000000..a0f095b
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ *      Copyright (C) 1993-1995 Bas Laarhoven,
+ *                (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-write.c,v $
+ * $Revision: 1.3.4.1 $
+ * $Date: 1997/11/14 18:07:04 $
+ *
+ *      This file contains the writing code
+ *      for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/ftape.h>
+#include <linux/qic117.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-ecc.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/fdc-isr.h"
+
+/*      Global vars.
+ */
+
+/*      Local vars.
+ */
+static int last_write_failed = 0;
+
+void ftape_zap_write_buffers(void)
+{
+       int i;
+
+       for (i = 0; i < ft_nr_buffers; ++i) {
+               ft_buffer[i]->status = done;
+       }
+       ftape_reset_buffer();
+}
+
+static int copy_and_gen_ecc(void *destination, 
+                           const void *source,
+                           const SectorMap bad_sector_map)
+{
+       int result;
+       struct memory_segment mseg;
+       int bads = count_ones(bad_sector_map);
+       TRACE_FUN(ft_t_any);
+
+       if (bads > 0) {
+               TRACE(ft_t_noise, "bad sectors in map: %d", bads);
+       }
+       if (bads + 3 >= FT_SECTORS_PER_SEGMENT) {
+               TRACE(ft_t_noise, "empty segment");
+               mseg.blocks = 0; /* skip entire segment */
+               result = 0;      /* nothing written */
+       } else {
+               mseg.blocks = FT_SECTORS_PER_SEGMENT - bads;
+               mseg.data = destination;
+               memcpy(mseg.data, source, (mseg.blocks - 3) * FT_SECTOR_SIZE);
+               result = ftape_ecc_set_segment_parity(&mseg);
+               if (result < 0) {
+                       TRACE(ft_t_err, "ecc_set_segment_parity failed");
+               } else {
+                       result = (mseg.blocks - 3) * FT_SECTOR_SIZE;
+               }
+       }
+       TRACE_EXIT result;
+}
+
+
+int ftape_start_writing(const ft_write_mode_t mode)
+{
+       buffer_struct *head = ftape_get_buffer(ft_queue_head);
+       int segment_id = head->segment_id;
+       int result;
+       buffer_state_enum wanted_state = (mode == FT_WR_DELETE
+                                         ? deleting
+                                         : writing);
+       TRACE_FUN(ft_t_flow);
+
+       if ((ft_driver_state != wanted_state) || head->status != waiting) {
+               TRACE_EXIT 0;
+       }
+       ftape_setup_new_segment(head, segment_id, 1);
+       if (mode == FT_WR_SINGLE) {
+               /* stop tape instead of pause */
+               head->next_segment = 0;
+       }
+       ftape_calc_next_cluster(head); /* prepare */
+       head->status = ft_driver_state; /* either writing or deleting */
+       if (ft_runner_status == idle) {
+               TRACE(ft_t_noise,
+                     "starting runner for segment %d", segment_id);
+               TRACE_CATCH(ftape_start_tape(segment_id,head->sector_offset),);
+       } else {
+               TRACE(ft_t_noise, "runner not idle, not starting tape");
+       }
+       /* go */
+       result = fdc_setup_read_write(head, (mode == FT_WR_DELETE
+                                            ? FDC_WRITE_DELETED : FDC_WRITE));
+       ftape_set_state(wanted_state); /* should not be necessary */
+       TRACE_EXIT result;
+}
+
+/*  Wait until all data is actually written to tape.
+ *  
+ *  There is a problem: when the tape runs into logical EOT, then this
+ *  failes. We need to restart the runner in this case.
+ */
+int ftape_loop_until_writes_done(void)
+{
+       buffer_struct *head;
+       TRACE_FUN(ft_t_flow);
+
+       while ((ft_driver_state == writing || ft_driver_state == deleting) && 
+              ftape_get_buffer(ft_queue_head)->status != done) {
+               /* set the runner status to idle if at lEOT */
+               TRACE_CATCH(ftape_handle_logical_eot(), last_write_failed = 1);
+               /* restart the tape if necessary */
+               if (ft_runner_status == idle) {
+                       TRACE(ft_t_noise, "runner is idle, restarting");
+                       if (ft_driver_state == deleting) {
+                               TRACE_CATCH(ftape_start_writing(FT_WR_DELETE),
+                                           last_write_failed = 1);
+                       } else {
+                               TRACE_CATCH(ftape_start_writing(FT_WR_MULTI),
+                                           last_write_failed = 1);
+                       }
+               }
+               TRACE(ft_t_noise, "tail: %d, head: %d", 
+                     ftape_buffer_id(ft_queue_tail),
+                     ftape_buffer_id(ft_queue_head));
+               TRACE_CATCH(fdc_interrupt_wait(5 * FT_SECOND),
+                           last_write_failed = 1);
+               head = ftape_get_buffer(ft_queue_head);
+               if (head->status == error) {
+                       /* Allow escape from loop when signaled !
+                        */
+                       FT_SIGNAL_EXIT(_DONT_BLOCK);
+                       if (head->hard_error_map != 0) {
+                               /*  Implement hard write error recovery here
+                                */
+                       }
+                       /* retry this one */
+                       head->status = waiting;
+                       if (ft_runner_status == aborting) {
+                               ftape_dumb_stop();
+                       }
+                       if (ft_runner_status != idle) {
+                               TRACE_ABORT(-EIO, ft_t_err,
+                                           "unexpected state: "
+                                           "ft_runner_status != idle");
+                       }
+                       ftape_start_writing(ft_driver_state == deleting
+                                           ? FT_WR_MULTI : FT_WR_DELETE);
+               }
+               TRACE(ft_t_noise, "looping until writes done");
+       }
+       ftape_set_state(idle);
+       TRACE_EXIT 0;
+}
+
+/*      Write given segment from buffer at address to tape.
+ */
+static int write_segment(const int segment_id,
+                        const void *address, 
+                        const ft_write_mode_t write_mode)
+{
+       int bytes_written = 0;
+       buffer_struct *tail;
+       buffer_state_enum wanted_state = (write_mode == FT_WR_DELETE
+                                         ? deleting : writing);
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_noise, "segment_id = %d", segment_id);
+       if (ft_driver_state != wanted_state) {
+               if (ft_driver_state == deleting ||
+                   wanted_state == deleting) {
+                       TRACE_CATCH(ftape_loop_until_writes_done(),);
+               }
+               TRACE(ft_t_noise, "calling ftape_abort_operation");
+               TRACE_CATCH(ftape_abort_operation(),);
+               ftape_zap_write_buffers();
+               ftape_set_state(wanted_state);
+       }
+       /*    if all buffers full we'll have to wait...
+        */
+       ftape_wait_segment(wanted_state);
+       tail = ftape_get_buffer(ft_queue_tail);
+       switch(tail->status) {
+       case done:
+               ft_history.defects += count_ones(tail->hard_error_map);
+               break;
+       case waiting:
+               /* this could happen with multiple EMPTY_SEGMENTs, but
+                * shouldn't happen any more as we re-start the runner even
+                * with an empty segment.
+                */
+               bytes_written = -EAGAIN;
+               break;
+       case error:
+               /*  setup for a retry
+                */
+               tail->status = waiting;
+               bytes_written = -EAGAIN; /* force retry */
+               if (tail->hard_error_map != 0) {
+                       TRACE(ft_t_warn, 
+                             "warning: %d hard error(s) in written segment",
+                             count_ones(tail->hard_error_map));
+                       TRACE(ft_t_noise, "hard_error_map = 0x%08lx", 
+                             (long)tail->hard_error_map);
+                       /*  Implement hard write error recovery here
+                        */
+               }
+               break;
+       default:
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "wait for empty segment failed, tail status: %d",
+                           tail->status);
+       }
+       /*    should runner stop ?
+        */
+       if (ft_runner_status == aborting) {
+               buffer_struct *head = ftape_get_buffer(ft_queue_head);
+               if (head->status == wanted_state) {
+                       head->status = done; /* ???? */
+               }
+               /*  don't call abort_operation(), we don't want to zap
+                *  the dma buffers
+                */
+               TRACE_CATCH(ftape_dumb_stop(),);
+       } else {
+               /*  If just passed last segment on tape: wait for BOT
+                *  or EOT mark. Sets ft_runner_status to idle if at lEOT
+                *  and successful 
+                */
+               TRACE_CATCH(ftape_handle_logical_eot(),);
+       }
+       if (tail->status == done) {
+               /* now at least one buffer is empty, fill it with our
+                * data.  skip bad sectors and generate ecc.
+                * copy_and_gen_ecc return nr of bytes written, range
+                * 0..29 Kb inclusive!  
+                *
+                * Empty segments are handled inside coyp_and_gen_ecc()
+                */
+               if (write_mode != FT_WR_DELETE) {
+                       TRACE_CATCH(bytes_written = copy_and_gen_ecc(
+                               tail->address, address,
+                               ftape_get_bad_sector_entry(segment_id)),);
+               }
+               tail->segment_id = segment_id;
+               tail->status = waiting;
+               tail = ftape_next_buffer(ft_queue_tail);
+       }
+       /*  Start tape only if all buffers full or flush mode.
+        *  This will give higher probability of streaming.
+        */
+       if (ft_runner_status != running && 
+           ((tail->status == waiting &&
+             ftape_get_buffer(ft_queue_head) == tail) ||
+            write_mode != FT_WR_ASYNC)) {
+               TRACE_CATCH(ftape_start_writing(write_mode),);
+       }
+       TRACE_EXIT bytes_written;
+}
+
+/*  Write as much as fits from buffer to the given segment on tape
+ *  and handle retries.
+ *  Return the number of bytes written (>= 0), or:
+ *      -EIO          write failed
+ *      -EINTR        interrupted by signal
+ *      -ENOSPC       device full
+ */
+int ftape_write_segment(const int segment_id,
+                       const void *buffer, 
+                       const ft_write_mode_t flush)
+{
+       int retry = 0;
+       int result;
+       TRACE_FUN(ft_t_flow);
+
+       ft_history.used |= 2;
+       if (segment_id >= ft_tracks_per_tape*ft_segments_per_track) {
+               /* tape full */
+               TRACE_ABORT(-ENOSPC, ft_t_err,
+                           "invalid segment id: %d (max %d)", 
+                           segment_id, 
+                           ft_tracks_per_tape * ft_segments_per_track -1);
+       }
+       for (;;) {
+               if ((result = write_segment(segment_id, buffer, flush)) >= 0) {
+                       if (result == 0) { /* empty segment */
+                               TRACE(ft_t_noise,
+                                     "empty segment, nothing written");
+                       }
+                       TRACE_EXIT result;
+               }
+               if (result == -EAGAIN) {
+                       if (++retry > 100) { /* give up */
+                               TRACE_ABORT(-EIO, ft_t_err,
+                                     "write failed, >100 retries in segment");
+                       }
+                       TRACE(ft_t_warn, "write error, retry %d (%d)",
+                             retry,
+                             ftape_get_buffer(ft_queue_tail)->segment_id);
+               } else {
+                       TRACE_ABORT(result, ft_t_err,
+                                   "write_segment failed, error: %d", result);
+               }
+               /* Allow escape from loop when signaled !
+                */
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+       }
+}
diff --git a/drivers/char/ftape/lowlevel/ftape-write.h b/drivers/char/ftape/lowlevel/ftape-write.h
new file mode 100644 (file)
index 0000000..0e7f898
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef _FTAPE_WRITE_H
+#define _FTAPE_WRITE_H
+
+/*
+ * Copyright (C) 1994-1995 Bas Laarhoven,
+ *           (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-write.h,v $
+ $Author: claus $
+ *
+ $Revision: 1.2 $
+ $Date: 1997/10/05 19:18:30 $
+ $State: Exp $
+ *
+ *      This file contains the definitions for the write functions
+ *      for the QIC-117 floppy-tape driver for Linux.
+ *
+ */
+
+
+/*      ftape-write.c defined global functions.
+ */
+typedef enum {
+       FT_WR_ASYNC  = 0, /* start tape only when all buffers are full */
+       FT_WR_MULTI  = 1, /* start tape, but don't necessarily stop */
+       FT_WR_SINGLE = 2, /* write a single segment and stop afterwards */
+       FT_WR_DELETE = 3  /* write deleted data marks */
+} ft_write_mode_t;
+
+extern int  ftape_start_writing(const ft_write_mode_t mode);
+extern int  ftape_write_segment(const int segment,
+                               const void *address, 
+                               const ft_write_mode_t flushing);
+extern void ftape_zap_write_buffers(void);
+extern int  ftape_loop_until_writes_done(void);
+
+#endif                         /* _FTAPE_WRITE_H */
+
diff --git a/drivers/char/ftape/lowlevel/ftape_syms.c b/drivers/char/ftape/lowlevel/ftape_syms.c
new file mode 100644 (file)
index 0000000..dce3721
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *      Copyright (C) 1996-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape_syms.c,v $
+ * $Revision: 1.4 $
+ * $Date: 1997/10/17 00:03:51 $
+ *
+ *      This file contains the the symbols that the ftape low level
+ *      part of the QIC-40/80/3010/3020 floppy-tape driver "ftape"
+ *      exports to it's high level clients
+ */
+
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/ftape.h>
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-init.h"
+#include "../lowlevel/fdc-io.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-rw.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-buffer.h"
+#include "../lowlevel/ftape-format.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+# define FT_KSYM(sym) EXPORT_SYMBOL(##sym);
+#else
+# define FT_KSYM(sym) X(##sym),
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+struct symbol_table ftape_symbol_table = {
+#include <linux/symtab_begin.h>
+#endif
+/* bad sector handling from ftape-bsm.c */
+FT_KSYM(ftape_get_bad_sector_entry)
+FT_KSYM(ftape_find_end_of_bsm_list)
+/* from ftape-rw.c */
+FT_KSYM(ftape_set_state)
+/* from ftape-ctl.c */
+FT_KSYM(ftape_seek_to_bot)
+FT_KSYM(ftape_seek_to_eot)
+FT_KSYM(ftape_abort_operation)
+FT_KSYM(ftape_get_status)
+FT_KSYM(ftape_enable)
+FT_KSYM(ftape_disable)
+FT_KSYM(ftape_mmap)
+FT_KSYM(ftape_calibrate_data_rate)
+/* from ftape-io.c */
+FT_KSYM(ftape_reset_drive)
+FT_KSYM(ftape_command)
+FT_KSYM(ftape_parameter)
+FT_KSYM(ftape_ready_wait)
+FT_KSYM(ftape_report_operation)
+FT_KSYM(ftape_report_error)
+/* from ftape-read.c */
+FT_KSYM(ftape_read_segment_fraction)
+FT_KSYM(ftape_zap_read_buffers)
+FT_KSYM(ftape_read_header_segment)
+FT_KSYM(ftape_decode_header_segment)
+/* from ftape-write.c */
+FT_KSYM(ftape_write_segment)
+FT_KSYM(ftape_start_writing)
+FT_KSYM(ftape_loop_until_writes_done)
+/* from ftape-buffer.h */
+FT_KSYM(ftape_set_nr_buffers)
+/* from ftape-format.h */
+FT_KSYM(ftape_format_track)
+FT_KSYM(ftape_format_status)
+FT_KSYM(ftape_verify_segment)
+/* from tracing.c */
+#ifndef CONFIG_FT_NO_TRACE_AT_ALL
+FT_KSYM(ftape_tracing)
+FT_KSYM(ftape_function_nest_level)
+FT_KSYM(ftape_trace_call)
+FT_KSYM(ftape_trace_exit)
+FT_KSYM(ftape_trace_log)
+#endif
+/* end of ksym table */
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+#include <linux/symtab_end.h>
+};
+#endif
diff --git a/drivers/char/ftape/lowlevel/ftape_syms.h b/drivers/char/ftape/lowlevel/ftape_syms.h
new file mode 100644 (file)
index 0000000..d31f835
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _FTAPE_SYMS_H
+#define _FTAPE_SYMS_H
+
+/*
+ * Copyright (C) 1996-1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape_syms.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:32 $
+ *
+ *      This file contains the definitions needed to export symbols
+ *      from the QIC-40/80/3010/3020 floppy-tape driver "ftape" for
+ *      Linux.
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+#include <linux/module.h>
+/*      ftape-vfs.c defined global vars.
+ */
+
+extern struct symbol_table ftape_symbol_table;
+#endif
+
+#endif
diff --git a/drivers/char/ftape/qic117.h b/drivers/char/ftape/qic117.h
deleted file mode 100644 (file)
index 9eb9bb6..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-#ifndef _QIC117_H
-#define _QIC117_H
-
-/*
- *      Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/qic117.h,v $
- $Author: bas $
- *
- $Revision: 1.27 $
- $Date: 1995/05/01 19:02:20 $
- $State: Beta $
- *
- *      This file contains QIC-117 spec. related definitions
- *      for the QIC-40/80 floppy-tape driver for Linux.
- *
- *      These data were taken from the Quarter-Inch Cartridge
- *      Drive Standards, Inc. document titled:
- *      `Common Command Set Interface Specification for Flexible
- *       Disk Controller Based Minicartridge Tape Drives'
- *       document QIC-117 Revision C, 3 Dec 92.
- *      For more information, contact:
- *       Quarter-Inch Cartridge Drive Standards, Inc.
- *       311 East Carrillo Street
- *       Santa Barbara, California 93101
- *       Telephone (805) 963-3853
- *       Fax       (805) 962-1541
- *
- *      Current QIC standard revisions (of interest) are:
- *       QIC-80-MC,   Rev. M, 15 Jun 95.
- *       QIC-117,     Rev. I, 15 Mar 95.
- *       QIC-122,     Rev. B,  6 Mar 91.
- *       QIC-130,     Rev. C,  2 Sep 92.
- *       QIC-3010-MC, Rev. F, 14 Jun 95.
- *       QIC-3020-MC, Rev. G, 31 Aug 95.
- *       QIC-CRF3,    Rev. B, 15 Jun 95.
- *
- */
-
-/*
- *      QIC-117 common command set rev. I.
- *      These commands are sent to the tape unit
- *      as number of pulses over the step line.
- */
-
-#define QIC_RESET                      1
-#define QIC_REPORT_NEXT_BIT            2
-#define QIC_PAUSE                      3
-#define QIC_MICRO_STEP_PAUSE           4
-#define QIC_ALTERNATE_TIMEOUT          5
-#define QIC_REPORT_DRIVE_STATUS                6
-#define QIC_REPORT_ERROR_CODE          7
-#define QIC_REPORT_DRIVE_CONFIGURATION 8
-#define QIC_REPORT_ROM_VERSION         9
-#define QIC_LOGICAL_FORWARD            10
-#define QIC_PHYSICAL_REVERSE           11
-#define QIC_PHYSICAL_FORWARD           12
-#define QIC_SEEK_HEAD_TO_TRACK         13
-#define QIC_SEEK_LOAD_POINT            14
-#define QIC_ENTER_FORMAT_MODE          15
-#define QIC_WRITE_REFERENCE_BURST      16
-#define QIC_ENTER_VERIFY_MODE          17
-#define QIC_STOP_TAPE                  18
-/* commands 19-20: reserved */
-#define QIC_MICRO_STEP_HEAD_UP         21
-#define QIC_MICRO_STEP_HEAD_DOWN       22
-#define QIC_SOFT_SELECT                        23
-#define QIC_SOFT_DESELECT              24
-#define QIC_SKIP_REVERSE               25
-#define QIC_SKIP_FORWARD               26
-#define QIC_SELECT_RATE                        27
-/* command 27, in ccs2: Select Rate or Format */
-#define QIC_ENTER_DIAGNOSTIC_1         28
-#define QIC_ENTER_DIAGNOSTIC_2         29
-#define QIC_ENTER_PRIMARY_MODE         30
-/* command 31: vendor unique */
-#define QIC_REPORT_VENDOR_ID           32
-#define QIC_REPORT_TAPE_STATUS         33
-#define QIC_SKIP_EXTENDED_REVERSE      34
-#define QIC_SKIP_EXTENDED_FORWARD      35
-#define QIC_CALIBRATE_TAPE_LENGTH      36
-#define QIC_REPORT_FORMAT_SEGMENTS     37
-#define QIC_SET_FORMAT_SEGMENTS                38
-/* commands 39-45: reserved */
-#define QIC_PHANTOM_SELECT             46
-#define QIC_PHANTOM_DESELECT           47
-
-typedef enum {
-       discretional = 0, required, ccs1, ccs2
-} qic_compatibility;
-
-typedef enum {
-       unused, mode, motion, report
-} command_types;
-
-struct qic117_command_table {
-       char *name;
-       byte mask;
-       byte state;
-       byte cmd_type;
-       byte non_intr;
-       byte level;
-};
-
-#define QIC117_COMMANDS {\
-/* command                           mask  state cmd_type           */\
-/* |    name                         |     |     |       non_intr   */\
-/* |    |                            |     |     |       |  level   */\
-/* 0*/ {NULL,                        0x00, 0x00, mode,   0, discretional},\
-/* 1*/ {"soft reset",                0x00, 0x00, motion, 1, required},\
-/* 2*/ {"report next bit",           0x00, 0x00, report, 0, required},\
-/* 3*/ {"pause",                     0x36, 0x24, motion, 1, required},\
-/* 4*/ {"micro step pause",          0x36, 0x24, motion, 1, required},\
-/* 5*/ {"alternate command timeout", 0x00, 0x00, mode,   0, required},\
-/* 6*/ {"report drive status",       0x00, 0x00, report, 0, required},\
-/* 7*/ {"report error code",         0x01, 0x01, report, 0, required},\
-/* 8*/ {"report drive configuration",0x00, 0x00, report, 0, required},\
-/* 9*/ {"report rom version",        0x00, 0x00, report, 0, required},\
-/*10*/ {"logical forward",           0x37, 0x25, motion, 0, required},\
-/*11*/ {"physical reverse",          0x17, 0x05, motion, 0, required},\
-/*12*/ {"physical forward",          0x17, 0x05, motion, 0, required},\
-/*13*/ {"seek head to track",        0x37, 0x25, motion, 0, required},\
-/*14*/ {"seek load point",           0x17, 0x05, motion, 1, required},\
-/*15*/ {"enter format mode",         0x1f, 0x05, mode,   0, required},\
-/*16*/ {"write reference burst",     0x1f, 0x05, motion, 1, required},\
-/*17*/ {"enter verify mode",         0x37, 0x25, mode,   0, required},\
-/*18*/ {"stop tape",                 0x00, 0x00, motion, 1, required},\
-/*19*/ {"reserved (19)",             0x00, 0x00, unused, 0, discretional},\
-/*20*/ {"reserved (20)",             0x00, 0x00, unused, 0, discretional},\
-/*21*/ {"micro step head up",        0x02, 0x00, motion, 0, required},\
-/*22*/ {"micro step head down",      0x02, 0x00, motion, 0, required},\
-/*23*/ {"soft select",               0x00, 0x00, mode,   0, discretional},\
-/*24*/ {"soft deselect",             0x00, 0x00, mode,   0, discretional},\
-/*25*/ {"skip segments reverse",     0x36, 0x24, motion, 1, required},\
-/*26*/ {"skip segments forward",     0x36, 0x24, motion, 1, required},\
-/*27*/ {"select rate [or format]",   0x03, 0x01, mode,   0, required /* [ccs2] */},\
-/*28*/ {"enter diag mode 1",         0x00, 0x00, mode,   0, discretional},\
-/*29*/ {"enter diag mode 2",         0x00, 0x00, mode,   0, discretional},\
-/*30*/ {"enter primary mode",        0x00, 0x00, mode,   0, required},\
-/*31*/ {"vendor unique (31)",        0x00, 0x00, unused, 0, discretional},\
-/*32*/ {"report vendor id",          0x00, 0x00, report, 0, required},\
-/*33*/ {"report tape status",        0x04, 0x04, report, 0, ccs1},\
-/*34*/ {"skip extended reverse",     0x36, 0x24, motion, 1, ccs1},\
-/*35*/ {"skip extended forward",     0x36, 0x24, motion, 1, ccs1},\
-/*36*/ {"calibrate tape length",     0x17, 0x05, motion, 1, ccs2},\
-/*37*/ {"report format segments",    0x17, 0x05, report, 0, ccs2},\
-/*38*/ {"set format segments",       0x17, 0x05, mode,   0, ccs2},\
-/*39*/ {"reserved (39)",             0x00, 0x00, unused, 0, discretional},\
-/*40*/ {"vendor unique (40)",        0x00, 0x00, unused, 0, discretional},\
-/*41*/ {"vendor unique (41)",        0x00, 0x00, unused, 0, discretional},\
-/*42*/ {"vendor unique (42)",        0x00, 0x00, unused, 0, discretional},\
-/*43*/ {"vendor unique (43)",        0x00, 0x00, unused, 0, discretional},\
-/*44*/ {"vendor unique (44)",        0x00, 0x00, unused, 0, discretional},\
-/*45*/ {"vendor unique (45)",        0x00, 0x00, unused, 0, discretional},\
-/*46*/ {"phantom select",            0x00, 0x00, mode,   0, discretional},\
-/*47*/ {"phantom deselect",          0x00, 0x00, mode,   0, discretional},\
-}
-
-/*
- *      Status bits returned by QIC_REPORT_DRIVE_STATUS
- */
-
-#define QIC_STATUS_READY       0x01    /* Drive is ready or idle. */
-#define QIC_STATUS_ERROR       0x02    /* Error detected, must read
-                                          error code to clear this */
-#define QIC_STATUS_CARTRIDGE_PRESENT 0x04      /* Tape is present */
-#define QIC_STATUS_WRITE_PROTECT 0x08  /* Tape is write protected */
-#define QIC_STATUS_NEW_CARTRIDGE 0x10  /* New cartridge inserted, must
-                                          read error status to clear. */
-#define QIC_STATUS_REFERENCED  0x20    /* Cartridge appears to have been
-                                          formatted. */
-#define QIC_STATUS_AT_BOT      0x40    /* Cartridge is at physical
-                                          beginning of tape. */
-#define QIC_STATUS_AT_EOT      0x80    /* Cartridge is at physical end
-                                          of tape. */
-/*
- *      Status bits returned by QIC_REPORT_DRIVE_CONFIGURATION
- */
-
-#define QIC_CONFIG_RATE_MASK   0x18
-#define QIC_CONFIG_RATE_SHIFT  3
-#define QIC_CONFIG_RATE_250    0
-#define QIC_CONFIG_RATE_500    2
-#define QIC_CONFIG_RATE_1000   3
-#define QIC_CONFIG_RATE_2000   1
-
-#define QIC_CONFIG_LONG                0x40    /* Extra Length Tape Detected */
-#define QIC_CONFIG_80          0x80    /* QIC-80 detected. */
-
-/*
- *      Status bits returned by QIC_REPORT_TAPE_STATUS
- */
-
-#define QIC_TAPE_STD_MASK       0x0f
-#define QIC_TAPE_QIC40         0x01
-#define QIC_TAPE_QIC80         0x02
-#define QIC_TAPE_QIC3020       0x03
-#define QIC_TAPE_QIC3010       0x04
-
-#define QIC_TAPE_LEN_MASK       0x70
-#define QIC_TAPE_205FT          0x10
-#define QIC_TAPE_307FT          0x20
-#define QIC_TAPE_400FT          0x30
-#define QIC_TAPE_1100FT         0x40
-#define QIC_TAPE_FLEX          0x60
-
-#define QIC_TAPE_WIDE          0x80
-
-/*
- *      Errors: List of error codes, and their severity.
- */
-
-typedef struct {
-       char *message;          /* Text describing the error. */
-       int fatal;              /* Non-zero if the error is fatal. */
-} ftape_error;
-
-#define QIC117_ERRORS {\
-  /* 0*/ { "No error", 0, },\
-  /* 1*/ { "Command Received while Drive Not Ready", 0, },\
-  /* 2*/ { "Cartridge Not Present or Removed", 1, },\
-  /* 3*/ { "Motor Speed Error (not within 1%)", 1, },\
-  /* 4*/ { "Motor Speed Fault (jammed, or gross speed error", 1, },\
-  /* 5*/ { "Cartridge Write Protected", 1, },\
-  /* 6*/ { "Undefined or Reserved Command Code", 1, },\
-  /* 7*/ { "Illegal Track Address Specified for Seek", 1, },\
-  /* 8*/ { "Illegal Command in Report Subcontext", 0, },\
-  /* 9*/ { "Illegal Entry into a Diagnostic Mode", 1, },\
-  /*10*/ { "Broken Tape Detected (based on hole sensor)", 1, },\
-  /*11*/ { "Warning--Read Gain Setting Error", 1, },\
-  /*12*/ { "Command Received While Error Status Pending (obs)", 1, },\
-  /*13*/ { "Command Received While New Cartridge Pending", 1, },\
-  /*14*/ { "Command Illegal or Undefined in Primary Mode", 1, },\
-  /*15*/ { "Command Illegal or Undefined in Format Mode", 1, },\
-  /*16*/ { "Command Illegal or Undefined in Verify Mode", 1, },\
-  /*17*/ { "Logical Forward Not a Logical BOT or no Format Segments in Format Mode", 1, },\
-  /*18*/ { "Logical EOT Before All Segments generated", 1, },\
-  /*19*/ { "Command Illegal When Cartridge Not Referenced", 1, },\
-  /*20*/ { "Self-Diagnostic Failed (cannot be cleared)", 1, },\
-  /*21*/ { "Warning EEPROM Not Initialized, Defaults Set", 1, },\
-  /*22*/ { "EEPROM Corrupted or Hardware Failure", 1, },\
-  /*23*/ { "Motion Time-out Error", 1, },\
-  /*24*/ { "Data Segment Too Long -- Logical Forward or Pause", 1, },\
-  /*25*/ { "Transmit Overrun (obs)", 1, },\
-  /*26*/ { "Power On Reset Occurred", 0, },\
-  /*27*/ { "Software Reset Occurred", 0, },\
-  /*28*/ { "Diagnostic Mode 1 Error", 1, },\
-  /*29*/ { "Diagnostic Mode 2 Error", 1, },\
-  /*30*/ { "Command Received During Non-Interruptible Process", 1, },\
-  /*31*/ { "Rate or Format Selection Error", 1, },\
-  /*32*/ { "Illegal Command While in High Speed Mode", 1, },\
-  /*33*/ { "Illegal Seek Segment Value", 1, },\
-  /*34*/ { "Invalid Media", 1, },\
-  /*35*/ { "Head Positioning Failure", 1, },\
-  /*36*/ { "Write Reference Burst Failure", 1, },\
-  /*37*/ { "Prom Code Missing", 1, },\
-  /*38*/ { "Invalid Format", 1, },\
-  /*39*/ { "EOT/BOT System Failure", 1, },\
-  /*40*/ { "Prom A Checksum Error", 1, },\
-  /*41*/ { "Drive Wakeup Reset Occurred", 1, },\
-  /*42*/ { "Prom B Checksum Error", 1, },\
-  /*43*/ { "Illegal Entry into Format Mode", 1, },\
-}
-
-#endif                         /* _QIC117_H */
diff --git a/drivers/char/ftape/tracing.c b/drivers/char/ftape/tracing.c
deleted file mode 100644 (file)
index a2d1a42..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-
-/*
- *      Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- *      This file contains the reading code
- *      for the QIC-117 floppy-tape driver for Linux.
- */
-
-#include <linux/ftape.h>
-
-#include "tracing.h"
-
-/*      Global vars.
- */
-/*      tracing
- *      set it to:     to get:
- *       0              bugs
- *       1              + errors
- *       2              + warnings
- *       3              + information
- *       4              + more information
- *       5              + program flow
- *       6              + fdc/dma info
- *       7              + data flow
- *       8              + everything else
- */
-int tracing = 3;               /* Default level: report only errors */
-
-#ifndef NO_TRACE_AT_ALL
-
-byte trace_id = 0;
-int function_nest_level = 0;
-
-/*      Local vars.
- */
-static char spacing[] = "*                              ";
-
-int trace_call(int level, char *file, char *name)
-{
-       char *indent;
-
-       if (tracing >= level && level <= TOP_LEVEL) {
-               /*    Since printk seems not to work with "%*s" format
-                *    we'll use this work-around.
-                */
-               if (function_nest_level < sizeof(spacing)) {
-                       indent = spacing + sizeof(spacing) - 1 - function_nest_level;
-               } else {
-                       indent = spacing;
-               }
-               printk(KERN_INFO "[%03d]%s+%s (%s)\n", (int) trace_id++, indent, file, name);
-       }
-       return function_nest_level++;
-}
-
-void trace_exit(int level, char *file, char *name)
-{
-       char *indent;
-
-       if (tracing >= level && level <= TOP_LEVEL) {
-               /*    Since printk seems not to work with "%*s" format
-                *    we'll use this work-around.
-                */
-               if (function_nest_level < sizeof(spacing)) {
-                       indent = spacing + sizeof(spacing) - 1 - function_nest_level;
-               } else {
-                       indent = spacing;
-               }
-               printk(KERN_INFO "[%03d]%s-%s (%s)\n", (int) trace_id++, indent, file, name);
-       }
-}
-
-void trace_log(char *file, char *name)
-{
-       char *indent;
-
-       /*    Since printk seems not to work with "%*s" format
-        *    we'll use this work-around.
-        */
-       if (function_nest_level < sizeof(spacing)) {
-               indent = spacing + sizeof(spacing) - 1 - function_nest_level;
-       } else {
-               indent = spacing;
-       }
-       printk(KERN_INFO "[%03d]%s%s (%s) - ", (int) trace_id++, indent, file, name);
-}
-
-#endif                         /* NO_TRACE_AT_ALL */
diff --git a/drivers/char/ftape/tracing.h b/drivers/char/ftape/tracing.h
deleted file mode 100644 (file)
index 9341bc5..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#ifndef _TRACING_H
-#define _TRACING_H
-
-/*
- * Copyright (C) 1994-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/tracing.h,v $
- $Author: bas $
- *
- $Revision: 1.10 $
- $Date: 1995/04/22 07:30:15 $
- $State: Beta $
- *
- *      This file contains definitions that eases the debugging
- *      of the QIC-40/80 floppy-tape driver for Linux.
- */
-
-#include <linux/kernel.h>
-
-#ifdef NO_TRACE_AT_ALL
-static inline void trace_dummy(void)
-{
-}
-
-#define TRACE_FUN( level, name) int _trace_dummy
-#define TRACE_EXIT              _trace_dummy= 0
-#define TRACE_(l,m)             trace_dummy()
-#define TRACE(l,m)              trace_dummy()
-#define TRACEi(l,m,i)           trace_dummy()
-#define TRACElx(l,m,i)          trace_dummy()
-#define TRACEx1(l,m,a)          trace_dummy()
-#define TRACEx2(l,m,a,b)        trace_dummy()
-#define TRACEx3(l,m,a,b,c)      trace_dummy()
-#define TRACEx4(l,m,a,b,c,d)    trace_dummy()
-#define TRACEx5(l,m,a,b,c,d,e)  trace_dummy()
-#define TRACEx6(l,m,a,b,c,d,e,f)  trace_dummy()
-#else
-#ifdef NO_TRACE
-#define TOP_LEVEL 2
-#else
-#define TOP_LEVEL 10
-#endif
-
-#define TRACE_FUN( level, name) \
-  char _trace_fun[] = name; \
-  int _function_nest_level = trace_call( level, __FILE__, _trace_fun); \
-  int _tracing = level
-
-#define TRACE_EXIT \
-  function_nest_level = _function_nest_level; \
-  trace_exit( _tracing, __FILE__, _trace_fun)
-
-#define TRACE_(l,m) \
-{ \
-  if (tracing >= (l) && (l) <= TOP_LEVEL) { \
-    trace_log( __FILE__, _trace_fun); \
-    m; \
-  } \
-}
-#define TRACE(l,m) TRACE_(l,printk(m".\n"))
-#define TRACEi(l,m,i) TRACE_(l,printk(m" %d.\n",i))
-#define TRACElx(l,m,i) TRACE_(l,printk(m" 0x%08lx.\n",i))
-#define TRACEx1(l,m,a) TRACE_(l,printk(m".\n",a))
-#define TRACEx2(l,m,a,b) TRACE_(l,printk(m".\n",a,b))
-#define TRACEx3(l,m,a,b,c) TRACE_(l,printk(m".\n",a,b,c))
-#define TRACEx4(l,m,a,b,c,d) TRACE_(l,printk(m".\n",a,b,c,d))
-#define TRACEx5(l,m,a,b,c,d,e) TRACE_(l,printk(m".\n",a,b,c,d,e))
-#define TRACEx6(l,m,a,b,c,d,e,f) TRACE_(l,printk(m".\n",a,b,c,d,e,f))
-
-/*      Global variables declared in tracing.c
- */
-extern unsigned char trace_id;
-extern int tracing;            /* sets default level */
-extern int function_nest_level;
-
-/*      Global functions declared in tracing.c
- */
-extern int trace_call(int level, char *file, char *name);
-extern void trace_exit(int level, char *file, char *name);
-extern void trace_log(char *file, char *name);
-
-#endif                         /* NO_TRACE_AT_ALL */
-
-#endif
diff --git a/drivers/char/ftape/vendors.h b/drivers/char/ftape/vendors.h
deleted file mode 100644 (file)
index 9f8e398..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-#ifndef _VENDORS_H
-#define _VENDORS_H
-
-/*
- *      Copyright (C) 1993-1995 Bas Laarhoven.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING.  If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- *
- $Source: /home/bas/distr/ftape-2.03b/RCS/vendors.h,v $
- $Author: bas $
- *
- $Revision: 1.34 $
- $Date: 1995/05/27 08:54:21 $
- $State: Beta $
- *
- *      This file contains the supported drive types
- *      with their QIC-117 spec. vendor code and
- *      drive dependent configuration information.
- */
-
-typedef enum {
-       unknown_wake_up = 0,
-       no_wake_up,
-       wake_up_colorado,
-       wake_up_mountain,
-       wake_up_insight,
-} wake_up_types;
-
-typedef struct {
-       wake_up_types wake_up;  /* see wake_up_types */
-       char *name;             /* Text describing the drive */
-} wakeup_method;
-
-/*  Note: order of entries in WAKEUP_METHODS must be so that a variable
- *        of type wake_up_types can be used as an index in the array.
- */
-#define WAKEUP_METHODS { \
-  { unknown_wake_up,    "Unknown" }, \
-  { no_wake_up,         "None" }, \
-  { wake_up_colorado,   "Colorado" }, \
-  { wake_up_mountain,   "Mountain" }, \
-  { wake_up_insight,    "Motor-on" }, \
-}
-
-typedef struct {
-       unsigned int vendor_id; /* vendor id from drive */
-       int speed;              /* maximum tape transport speed (ips) */
-       wake_up_types wake_up;  /* see wake_up_types */
-       char *name;             /* Text describing the drive */
-} vendor_struct;
-
-#define UNKNOWN_VENDOR (-1)
-
-#define QIC117_VENDORS { \
-/* see _vendor_struct */ \
-  { 0x00000,  82, wake_up_colorado,  "Colorado DJ-10 (old)" }, \
-  { 0x00047,  90, wake_up_colorado,  "Colorado DJ-10/DJ-20" }, \
-  { 0x011c2,  84, wake_up_colorado,  "Colorado 700" }, \
-  { 0x011c3,  90, wake_up_colorado,  "Colorado 1400" }, \
-  { 0x011c4,  84, wake_up_colorado,  "Colorado DJ-10/DJ-20 (new)" }, \
-  { 0x011c5,  84, wake_up_colorado,  "HP Colorado T1000" }, \
-  { 0x00005,  45, wake_up_mountain,  "Archive 5580i" }, \
-  { 0x10005,  50, wake_up_insight,   "Insight 80Mb, Irwin 80SX" }, \
-  { 0x00140,  74, wake_up_mountain,  "Archive S.Hornet [Identity/Escom]" }, \
-  { 0x00146,  72, wake_up_mountain,  "Archive 31250Q [Escom]" }, \
-  { 0x0014a, 100, wake_up_mountain,  "Archive XL9250i [Conner/Escom]" }, \
-  { 0x0014c,  98, wake_up_mountain,  "Conner C250MQT" }, \
-  { 0x0014e,  80, wake_up_mountain,  "Conner C250MQ" }, \
-  { 0x00150,  80, wake_up_mountain,  "Conner TSM420R/TST800R" }, \
-  { 0x00152,  80, wake_up_mountain,  "Conner TSM850R" }, \
-  { 0x00156,  80, wake_up_mountain,  "Conner TSM850R/1700R/TST3200R" }, \
-  { 0x00180,   0, wake_up_mountain,  "Summit SE 150" }, \
-  { 0x00181,  85, wake_up_mountain,  "Summit SE 250, Mountain FS8000" }, \
-  { 0x001c1,  82, no_wake_up,        "Wangtek 3040F" }, \
-  { 0x001c8,  64, no_wake_up,        "Wangtek 3080F" }, \
-  { 0x001c8,  64, wake_up_colorado,  "Wangtek 3080F" }, \
-  { 0x001ca,  67, no_wake_up,        "Wangtek 3080F (new)" }, \
-  { 0x001cc,  77, wake_up_colorado,  "Wangtek 3200 / Teac 700" }, \
-  { 0x001cd,  75, wake_up_colorado,  "Reveal TB1400" }, \
-  { 0x00380,   0, wake_up_colorado,  "Exabyte EXB-1500" }, \
-  { 0x08880,  64, no_wake_up,        "Iomega 250" }, \
-  { 0x08880,  64, wake_up_colorado,  "Iomega 250, Ditto 800" }, \
-  { 0x08880,  64, wake_up_insight,   "Iomega 250" }, \
-  { 0x08881,   0, wake_up_colorado,  "Iomega 700" }, \
-  { 0x08882,  80, wake_up_colorado,  "Iomega 3200" }, \
-  { 0x00021,  70, no_wake_up,        "AIWA CT-803" }, \
-  { 0x00021,   0, wake_up_mountain,  "COREtape QIC80" }, \
-}
-
-#define QIC117_MAKE_CODES { \
-  { 0, "Unassigned" }, \
-  { 1, "Alloy Computer Products" }, \
-  { 2, "3M" }, \
-  { 3, "Tandberg Data" }, \
-  { 4, "Colorado" }, \
-  { 5, "Archive/Conner" }, \
-  { 6, "Mountain/Summit Memory Systems" }, \
-  { 7, "Wangtek/Rexon/Tecmar" }, \
-  { 8, "Sony" }, \
-  { 9, "Cipher Data Products" }, \
-  { 10, "Irwin Magnetic Systems" }, \
-  { 11, "Braemar" }, \
-  { 12, "Verbatim" }, \
-  { 13, "Core International" }, \
-  { 14, "Exabyte" }, \
-  { 15, "Teac" }, \
-  { 16, "Gigatek" }, \
-  { 17, "ComByte" }, \
-  { 18, "PERTEC Memories" }, \
-  { 71, "Colorado" }, \
-  { 546, "Iomega Inc" }, \
-}
-
-#endif
diff --git a/drivers/char/ftape/zftape/Makefile b/drivers/char/ftape/zftape/Makefile
new file mode 100644 (file)
index 0000000..f0cb73a
--- /dev/null
@@ -0,0 +1,54 @@
+#
+#       Copyright (C) 1996, 1997 Claus-Justus Heine.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING.  If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Source: /homes/cvs/ftape-stacked/ftape/zftape/Makefile,v $
+# $Revision: 1.4 $
+# $Date: 1997/10/05 19:18:58 $
+#
+#      Makefile for the QIC-40/80/3010/3020 zftape interface VFS to
+#      ftape
+#
+
+#
+#  This isn't used inside the kernel, only for my private development
+#  version
+#
+ifndef TOPDIR
+TOPDIR=../..
+include $(TOPDIR)/MCONFIG
+endif
+
+SUB_DIRS     := 
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+# ZFT_OBSOLETE - enable the MTIOC_ZFTAPE_GETBLKSZ ioctl. You should
+#                leave this enabled for compatibility with taper.
+
+EXTRA_CFLAGS := -DZFT_OBSOLETE
+
+O_TARGET := zftape.o
+O_OBJS   := zftape-rw.o zftape-ctl.o zftape-read.o \
+           zftape-write.o zftape-vtbl.o zftape-eof.o \
+           zftape-init.o zftape-buffers.o
+
+OX_OBJS  += zftape_syms.o
+
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/char/ftape/zftape/zftape-buffers.c b/drivers/char/ftape/zftape/zftape-buffers.c
new file mode 100644 (file)
index 0000000..13594d0
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *      Copyright (C) 1995-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-buffers.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:59 $
+ *
+ *      This file contains the dynamic buffer allocation routines 
+ *      of zftape
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/segment.h>
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+#include <linux/vmalloc.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/*  global variables
+ */
+
+/*  local varibales
+ */
+static unsigned int used_memory = 0;
+static unsigned int peak_memory = 0;
+
+void zft_memory_stats(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_noise, "Memory usage (vmalloc allocations):\n"
+             KERN_INFO "total allocated: %d\n"
+             KERN_INFO "peak allocation: %d",
+             used_memory, peak_memory);
+       peak_memory = used_memory;
+       TRACE_EXIT;
+}
+
+int zft_vcalloc_once(void *new, size_t size)
+{
+       TRACE_FUN(ft_t_flow);
+       if (zft_vmalloc_once(new, size) < 0) {
+               TRACE_EXIT -ENOMEM;
+       }
+       memset(*(void **)new, '\0', size);
+       TRACE_EXIT 0;
+}
+int zft_vmalloc_once(void *new, size_t size)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (*(void **)new != NULL || size == 0) {
+               TRACE_EXIT 0;
+       }
+       if ((*(void **)new = vmalloc(size)) == NULL) {
+               TRACE_EXIT -ENOMEM;
+       }
+       used_memory += size;
+       if (peak_memory < used_memory) {
+               peak_memory = used_memory;
+       }
+       TRACE_ABORT(0, ft_t_noise,
+                   "allocated buffer @ %p, %d bytes", *(void **)new, size);
+}
+int zft_vcalloc_always(void *new, size_t size)
+{
+       TRACE_FUN(ft_t_flow);
+
+       zft_vfree(new, size);
+       TRACE_EXIT zft_vcalloc_once(new, size);
+}
+int zft_vmalloc_always(void *new, size_t size)
+{
+       TRACE_FUN(ft_t_flow);
+
+       zft_vfree(new, size);
+       TRACE_EXIT zft_vmalloc_once(new, size);
+}
+void zft_vfree(void *old, size_t size)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (*(void **)old) {
+               vfree(*(void **)old);
+               used_memory -= size;
+               TRACE(ft_t_noise, "released buffer @ %p, %d bytes",
+                     *(void **)old, size);
+               *(void **)old = NULL;
+       }
+       TRACE_EXIT;
+}
+
+void *zft_kmalloc(size_t size)
+{
+       void *new;
+
+       while ((new = kmalloc(size, GFP_KERNEL)) == NULL) {
+               current->timeout = HZ/10;
+               current->state   = TASK_INTERRUPTIBLE;
+               schedule();
+       }
+       memset(new, 0, size);
+       used_memory += size;
+       if (peak_memory < used_memory) {
+               peak_memory = used_memory;
+       }
+       return new;
+}
+
+void zft_kfree(void *old, size_t size)
+{
+       kfree(old);
+       used_memory -= size;
+}
+
+/* there are some more buffers that are allocated on demand.
+ * cleanup_module() calles this function to be sure to have released
+ * them 
+ */
+void zft_uninit_mem(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       zft_vfree(&zft_hseg_buf, FT_SEGMENT_SIZE);
+       zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE); zft_deblock_segment = -1;
+       zft_free_vtbl();
+       if (zft_cmpr_lock(0 /* don't load */) == 0) {
+               (*zft_cmpr_ops->cleanup)();
+               (*zft_cmpr_ops->reset)(); /* unlock it again */
+       }
+       zft_memory_stats();
+       TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/zftape/zftape-buffers.h b/drivers/char/ftape/zftape/zftape-buffers.h
new file mode 100644 (file)
index 0000000..c614984
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef _FTAPE_DYNMEM_H
+#define _FTAPE_DYNMEM_H
+
+/*
+ *      Copyright (C) 1995-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-buffers.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:18:59 $
+ *
+ *   memory allocation routines.
+ *
+ */
+
+/* we do not allocate all of the really large buffer memory before
+ * someone tries to open the drive. ftape_open() may fail with
+ * -ENOMEM, but that's better having 200k of vmalloced memory which
+ * cannot be swapped out.
+ */
+
+extern void  zft_memory_stats(void);
+extern int   zft_vmalloc_once(void *new, size_t size);
+extern int   zft_vcalloc_once(void *new, size_t size);
+extern int   zft_vmalloc_always(void *new, size_t size);
+extern int   zft_vcalloc_always(void *new, size_t size);
+extern void  zft_vfree(void *old, size_t size);
+extern void *zft_kmalloc(size_t size);
+extern void  zft_kfree(void *old, size_t size);
+
+/* called by cleanup_module() 
+ */
+extern void zft_uninit_mem(void);
+
+#endif
+
+
+
+
+
+
+
diff --git a/drivers/char/ftape/zftape/zftape-ctl.c b/drivers/char/ftape/zftape/zftape-ctl.c
new file mode 100644 (file)
index 0000000..19d10c9
--- /dev/null
@@ -0,0 +1,1502 @@
+/* 
+ *      Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-ctl.c,v $
+ * $Revision: 1.2.6.2 $
+ * $Date: 1997/11/14 18:07:33 $
+ *
+ *      This file contains the non-read/write zftape functions
+ *      for the QIC-40/80/3010/3020 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+#include <linux/fcntl.h>
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/*      Global vars.
+ */
+int zft_write_protected = 0; /* this is when cartridge rdonly or O_RDONLY */
+int zft_header_read     = 0;
+int zft_offline         = 0;
+unsigned int zft_unit   = 0;
+int zft_resid           = 0;
+int zft_mt_compression  = 0;
+
+/*      Local vars.
+ */
+static int going_offline   = 0;
+
+typedef int (mt_fun)(int *argptr);
+typedef int (*mt_funp)(int *argptr);
+typedef struct
+{
+       mt_funp function;
+       unsigned offline         : 1; /* op permitted if offline or no_tape */
+       unsigned write_protected : 1; /* op permitted if write-protected    */
+       unsigned not_formatted   : 1; /* op permitted if tape not formatted */
+       unsigned raw_mode        : 1; /* op permitted if zft_mode == 0    */
+       unsigned need_idle_state : 1; /* need to call def_idle_state        */
+       char     *name;
+} fun_entry;
+
+static mt_fun mt_dummy, mt_reset, mt_fsr, mt_bsr, mt_rew, mt_offl, mt_nop,
+       mt_weof, mt_erase, mt_ras2, mt_setblk, mt_setdensity,
+       mt_seek, mt_tell, mt_reten, mt_eom, mt_fsf, mt_bsf,
+       mt_fsfm, mt_bsfm, mt_setdrvbuffer, mt_compression;
+
+static fun_entry mt_funs[]=
+{ 
+       {mt_reset       , 1, 1, 1, 1, 0, "MT_RESET" }, /*  0 */
+       {mt_fsf         , 0, 1, 0, 0, 1, "MT_FSF"   },
+       {mt_bsf         , 0, 1, 0, 0, 1, "MT_BSF"   },
+       {mt_fsr         , 0, 1, 0, 1, 1, "MT_FSR"   },
+       {mt_bsr         , 0, 1, 0, 1, 1, "MT_BSR"   },
+       {mt_weof        , 0, 0, 0, 0, 0, "MT_WEOF"  }, /*  5 */
+       {mt_rew         , 0, 1, 1, 1, 0, "MT_REW"   },
+       {mt_offl        , 0, 1, 1, 1, 0, "MT_OFFL"  },
+       {mt_nop         , 1, 1, 1, 1, 0, "MT_NOP"   },
+       {mt_reten       , 0, 1, 1, 1, 0, "MT_RETEN" },
+       {mt_bsfm        , 0, 1, 0, 0, 1, "MT_BSFM"  }, /* 10 */
+       {mt_fsfm        , 0, 1, 0, 0, 1, "MT_FSFM"  },
+       {mt_eom         , 0, 1, 0, 0, 1, "MT_EOM"   },
+       {mt_erase       , 0, 0, 0, 1, 0, "MT_ERASE" },
+       {mt_dummy       , 1, 1, 1, 1, 0, "MT_RAS1"  },
+       {mt_ras2        , 0, 0, 0, 1, 0, "MT_RAS2"  },
+       {mt_dummy       , 1, 1, 1, 1, 0, "MT_RAS3"  },
+       {mt_dummy       , 1, 1, 1, 1, 0, "UNKNOWN"  },
+       {mt_dummy       , 1, 1, 1, 1, 0, "UNKNOWN"  },
+       {mt_dummy       , 1, 1, 1, 1, 0, "UNKNOWN"  },
+       {mt_setblk      , 1, 1, 1, 1, 1, "MT_SETBLK"}, /* 20 */
+       {mt_setdensity  , 1, 1, 1, 1, 0, "MT_SETDENSITY"},
+       {mt_seek        , 0, 1, 0, 1, 1, "MT_SEEK"  },
+       {mt_dummy       , 0, 1, 0, 1, 1, "MT_TELL"  }, /* wr-only ?! */
+       {mt_setdrvbuffer, 1, 1, 1, 1, 0, "MT_SETDRVBUFFER" },
+       {mt_dummy       , 1, 1, 1, 1, 0, "MT_FSS"   }, /* 25 */
+       {mt_dummy       , 1, 1, 1, 1, 0, "MT_BSS"   },
+       {mt_dummy       , 1, 1, 1, 1, 0, "MT_WSM"   },
+       {mt_dummy       , 1, 1, 1, 1, 0, "MT_LOCK"  },
+       {mt_dummy       , 1, 1, 1, 1, 0, "MT_UNLOCK"},
+       {mt_dummy       , 1, 1, 1, 1, 0, "MT_LOAD"  }, /* 30 */
+       {mt_dummy       , 1, 1, 1, 1, 0, "MT_UNLOAD"},
+       {mt_compression , 1, 1, 1, 0, 1, "MT_COMPRESSION"},
+       {mt_dummy       , 1, 1, 1, 1, 0, "MT_SETPART"},
+       {mt_dummy       , 1, 1, 1, 1, 0, "MT_MKPART"}
+};  
+
+#define NR_MT_CMDS NR_ITEMS(mt_funs)
+
+void zft_reset_position(zft_position *pos)
+{
+       TRACE_FUN(ft_t_flow);
+
+       pos->seg_byte_pos =
+               pos->volume_pos = 0;
+       if (zft_header_read) {
+               /* need to keep track of the volume table and
+                * compression map. We therefor simply
+                * position at the beginning of the first
+                * volume. This covers old ftape archives as
+                * well has various flavours of the
+                * compression map segments. The worst case is
+                * that the compression map shows up as a
+                * additional volume in front of all others.
+                */
+               pos->seg_pos  = zft_find_volume(0)->start_seg;
+               pos->tape_pos = zft_calc_tape_pos(pos->seg_pos);
+       } else {
+               pos->tape_pos =  0;
+               pos->seg_pos  = -1;
+       }
+       zft_just_before_eof =  0;
+       zft_deblock_segment = -1;
+       zft_io_state        = zft_idle;
+       zft_zap_read_buffers();
+       zft_prevent_flush();
+       /*  unlock the compresison module if it is loaded.
+        *  The zero arg means not to try to load the module.
+        */
+       if (zft_cmpr_lock(0) == 0) {
+               (*zft_cmpr_ops->reset)(); /* unlock */
+       }
+       TRACE_EXIT;
+}
+
+static void zft_init_driver(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       zft_resid =
+               zft_header_read          =
+               zft_old_ftape            =
+               zft_offline              =
+               zft_write_protected      =
+               going_offline            =
+               zft_mt_compression       =
+               zft_header_changed       =
+               zft_volume_table_changed =
+               zft_written_segments     = 0;
+       zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ;
+       zft_reset_position(&zft_pos); /* does most of the stuff */
+       ftape_zap_read_buffers();
+       ftape_set_state(idle);
+       TRACE_EXIT;
+}
+
+int zft_def_idle_state(void)
+{ 
+       int result = 0;
+       TRACE_FUN(ft_t_flow);
+       
+       if (!zft_header_read) {
+               result = zft_read_header_segments();
+       } else if ((result = zft_flush_buffers()) >= 0 && zft_qic_mode) {
+               /*  don't move past eof
+                */
+               (void)zft_close_volume(&zft_pos);
+       }
+       if (ftape_abort_operation() < 0) {
+               TRACE(ft_t_warn, "ftape_abort_operation() failed");
+               result = -EIO;
+       }
+       /* clear remaining read buffers */
+       zft_zap_read_buffers();
+       zft_io_state = zft_idle;
+       TRACE_EXIT result;
+}
+
+/*****************************************************************************
+ *                                                                           *
+ *  functions for the MTIOCTOP commands                                      *
+ *                                                                           *
+ *****************************************************************************/
+
+static int mt_dummy(int *dummy)
+{
+       TRACE_FUN(ft_t_flow);
+       
+       TRACE_EXIT -ENOSYS;
+}
+
+static int mt_reset(int *dummy)
+{        
+       TRACE_FUN(ft_t_flow);
+       
+       (void)ftape_seek_to_bot();
+       TRACE_CATCH(ftape_reset_drive(),
+                   zft_init_driver(); zft_uninit_mem(); zft_offline = 1);
+       /*  fake a re-open of the device. This will set all flage and 
+        *  allocate buffers as appropriate. The new tape condition will
+        *  force the open routine to do anything we need.
+        */
+       TRACE_CATCH(_zft_open(-1 /* fake reopen */, 0 /* dummy */),);
+       TRACE_EXIT 0;
+}
+
+static int mt_fsf(int *arg)
+{
+       int result;
+       TRACE_FUN(ft_t_flow);
+
+       result = zft_skip_volumes(*arg, &zft_pos);
+       zft_just_before_eof = 0;
+       TRACE_EXIT result;
+}
+
+static int mt_bsf(int *arg)
+{
+       int result = 0;
+       TRACE_FUN(ft_t_flow);
+       
+       if (*arg != 0) {
+               result = zft_skip_volumes(-*arg + 1, &zft_pos);
+       }
+       TRACE_EXIT result;
+}
+
+static int seek_block(__s64 data_offset,
+                     __s64 block_increment,
+                     zft_position *pos)
+{ 
+       int result      = 0;
+       __s64 new_block_pos;
+       __s64 vol_block_count;
+       const zft_volinfo *volume;
+       int exceed;
+       TRACE_FUN(ft_t_flow);
+       
+       volume = zft_find_volume(pos->seg_pos);
+       if (volume->start_seg == 0 || volume->end_seg == 0) {
+               TRACE_EXIT -EIO;
+       }
+       new_block_pos   = (zft_div_blksz(data_offset, volume->blk_sz)
+                          + block_increment);
+       vol_block_count = zft_div_blksz(volume->size, volume->blk_sz);
+       if (new_block_pos < 0) {
+               TRACE(ft_t_noise,
+                     "new_block_pos " LL_X " < 0", LL(new_block_pos));
+               zft_resid     = (int)new_block_pos;
+               new_block_pos = 0;
+               exceed = 1;
+       } else if (new_block_pos > vol_block_count) {
+               TRACE(ft_t_noise,
+                     "new_block_pos " LL_X " exceeds size of volume " LL_X,
+                     LL(new_block_pos), LL(vol_block_count));
+               zft_resid     = (int)(vol_block_count - new_block_pos);
+               new_block_pos = vol_block_count;
+               exceed = 1;
+       } else {
+               exceed = 0;
+       }
+       if (zft_use_compression && volume->use_compression) {
+               TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),);
+               result = (*zft_cmpr_ops->seek)(new_block_pos, pos, volume,
+                                              zft_deblock_buf);
+               pos->tape_pos  = zft_calc_tape_pos(pos->seg_pos);
+               pos->tape_pos += pos->seg_byte_pos;
+       } else {
+               pos->volume_pos = zft_mul_blksz(new_block_pos, volume->blk_sz);
+               pos->tape_pos   = zft_calc_tape_pos(volume->start_seg);
+               pos->tape_pos  += pos->volume_pos;
+               pos->seg_pos    = zft_calc_seg_byte_coord(&pos->seg_byte_pos,
+                                                         pos->tape_pos);
+       }
+       zft_just_before_eof = volume->size == pos->volume_pos;
+       if (zft_just_before_eof) {
+               /* why this? because zft_file_no checks agains start
+                * and end segment of a volume. We do not want to
+                * advance to the next volume with this function.
+                */
+               TRACE(ft_t_noise, "set zft_just_before_eof");
+               zft_position_before_eof(pos, volume);
+       }
+       TRACE(ft_t_noise, "\n"
+             KERN_INFO "new_seg_pos : %d\n"
+             KERN_INFO "new_tape_pos: " LL_X "\n"
+             KERN_INFO "vol_size    : " LL_X "\n"
+             KERN_INFO "seg_byte_pos: %d\n"
+             KERN_INFO "blk_sz  : %d", 
+             pos->seg_pos, LL(pos->tape_pos),
+             LL(volume->size), pos->seg_byte_pos,
+             volume->blk_sz);
+       if (!exceed) {
+               zft_resid = new_block_pos - zft_div_blksz(pos->volume_pos,
+                                                         volume->blk_sz);
+       }
+       if (zft_resid < 0) {
+               zft_resid = -zft_resid;
+       }
+       TRACE_EXIT ((exceed || zft_resid != 0) && result >= 0) ? -EINVAL : result;
+}     
+
+static int mt_fsr(int *arg)
+{ 
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       result = seek_block(zft_pos.volume_pos,  *arg, &zft_pos);
+       TRACE_EXIT result;
+}
+
+static int mt_bsr(int *arg)
+{   
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       result = seek_block(zft_pos.volume_pos, -*arg, &zft_pos);
+       TRACE_EXIT result;
+}
+
+static int mt_weof(int *arg)
+{
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       TRACE_CATCH(zft_flush_buffers(),);
+       result = zft_weof(*arg, &zft_pos);
+       TRACE_EXIT result;
+}
+
+static int mt_rew(int *dummy)
+{          
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       if(zft_header_read) {
+               (void)zft_def_idle_state();
+       }
+       result = ftape_seek_to_bot();
+       zft_reset_position(&zft_pos);
+       TRACE_EXIT result;
+}
+
+static int mt_offl(int *dummy)
+{
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       going_offline= 1;
+       result = mt_rew(NULL);
+       TRACE_EXIT result;
+}
+
+static int mt_nop(int *dummy)
+{
+       TRACE_FUN(ft_t_flow);
+       /*  should we set tape status?
+        */
+       if (!zft_offline) { /* offline includes no_tape */
+               (void)zft_def_idle_state();
+       }
+       TRACE_EXIT 0; 
+}
+
+static int mt_reten(int *dummy)
+{  
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       if(zft_header_read) {
+               (void)zft_def_idle_state();
+       }
+       result = ftape_seek_to_eot();
+       if (result >= 0) {
+               result = ftape_seek_to_bot();
+       }
+       TRACE_EXIT(result);
+}
+
+static int fsfbsfm(int arg, zft_position *pos)
+{ 
+       const zft_volinfo *vtbl;
+       __s64 block_pos;
+       TRACE_FUN(ft_t_flow);
+       
+       /* What to do? This should seek to the next file-mark and
+        * position BEFORE. That is, a next write would just extend
+        * the current file.  Well. Let's just seek to the end of the
+        * current file, if count == 1.  If count > 1, then do a
+        * "mt_fsf(count - 1)", and then seek to the end of that file.
+        * If count == 0, do nothing
+        */
+       if (arg == 0) {
+               TRACE_EXIT 0;
+       }
+       zft_just_before_eof = 0;
+       TRACE_CATCH(zft_skip_volumes(arg < 0 ? arg : arg-1, pos),
+                   if (arg > 0) {
+                           zft_resid ++; 
+                   });
+       vtbl      = zft_find_volume(pos->seg_pos);
+       block_pos = zft_div_blksz(vtbl->size, vtbl->blk_sz);
+       (void)seek_block(0, block_pos, pos);
+       if (pos->volume_pos != vtbl->size) {
+               zft_just_before_eof = 0;
+               zft_resid = 1;
+               /* we didn't managed to go there */
+               TRACE_ABORT(-EIO, ft_t_err, 
+                           "wanted file position " LL_X ", arrived at " LL_X, 
+                           LL(vtbl->size), LL(pos->volume_pos));
+       }
+       zft_just_before_eof = 1;
+       TRACE_EXIT 0; 
+}
+
+static int mt_bsfm(int *arg)
+{
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       result = fsfbsfm(-*arg, &zft_pos);
+       TRACE_EXIT result;
+}
+
+static int mt_fsfm(int *arg)
+{
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       result = fsfbsfm(*arg, &zft_pos);
+       TRACE_EXIT result;
+}
+
+static int mt_eom(int *dummy)
+{              
+       TRACE_FUN(ft_t_flow);
+       
+       zft_skip_to_eom(&zft_pos);
+       TRACE_EXIT 0;
+}
+
+static int mt_erase(int *dummy)
+{
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       result = zft_erase();
+       TRACE_EXIT result;
+}
+
+static int mt_ras2(int *dummy)
+{
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       result = -ENOSYS;
+       TRACE_EXIT result;
+} 
+
+/*  Sets the new blocksize in BYTES
+ *
+ */
+static int mt_setblk(int *new_size)
+{
+       TRACE_FUN(ft_t_flow);
+       
+       if((unsigned int)(*new_size) > ZFT_MAX_BLK_SZ) {
+               TRACE_ABORT(-EINVAL, ft_t_info,
+                           "desired blk_sz (%d) should be <= %d bytes",
+                           *new_size, ZFT_MAX_BLK_SZ);
+       }
+       if ((*new_size & (FT_SECTOR_SIZE-1)) != 0) {
+               TRACE_ABORT(-EINVAL, ft_t_info,
+                       "desired blk_sz (%d) must be a multiple of %d bytes",
+                           *new_size, FT_SECTOR_SIZE);
+       }
+       if (*new_size == 0) {
+               if (zft_use_compression) {
+                       TRACE_ABORT(-EINVAL, ft_t_info,
+                                   "Variable block size not yet "
+                                   "supported with compression");
+               }
+               *new_size = 1;
+       }
+       zft_blk_sz = *new_size;
+       TRACE_EXIT 0;
+} 
+
+static int mt_setdensity(int *arg)
+{
+       TRACE_FUN(ft_t_flow);
+       
+       SET_TRACE_LEVEL(*arg);
+       TRACE(TRACE_LEVEL, "tracing set to %d", TRACE_LEVEL);
+       if ((int)TRACE_LEVEL != *arg) {
+               TRACE_EXIT -EINVAL;
+       }
+       TRACE_EXIT 0;
+}          
+
+static int mt_seek(int *new_block_pos)
+{ 
+       int result= 0;        
+       TRACE_FUN(ft_t_any);
+       
+       result = seek_block(0, (__s64)*new_block_pos, &zft_pos);
+       TRACE_EXIT result;
+}
+
+/*  OK, this is totally different from SCSI, but the worst thing that can 
+ *  happen is that there is not enough defragmentated memory that can be 
+ *  allocated. Also, there is a hardwired limit of 16 dma buffers in the 
+ *  stock ftape module. This shouldn't bring the system down.
+ *
+ * NOTE: the argument specifies the total number of dma buffers to use.
+ *       The driver needs at least 3 buffers to function at all.
+ * 
+ */
+static int mt_setdrvbuffer(int *cnt)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (*cnt < 3) {
+               TRACE_EXIT -EINVAL;
+       }
+       TRACE_CATCH(ftape_set_nr_buffers(*cnt),);
+       TRACE_EXIT 0;
+}
+/* return the block position from start of volume 
+ */
+static int mt_tell(int *arg)
+{
+       TRACE_FUN(ft_t_flow);
+       
+       *arg   = zft_div_blksz(zft_pos.volume_pos,
+                              zft_find_volume(zft_pos.seg_pos)->blk_sz);
+       TRACE_EXIT 0;
+}
+
+static int mt_compression(int *arg)
+{
+       TRACE_FUN(ft_t_flow);
+       
+       /*  Ok. We could also check whether compression is available at
+        *  all by trying to load the compression module.  We could
+        *  also check for a block size of 1 byte which is illegal
+        *  with compression.  Instead of doing it here we rely on
+        *  zftape_write() to do the proper checks.
+        */
+       if ((unsigned int)*arg > 1) {
+               TRACE_EXIT -EINVAL;
+       }
+       if (*arg != 0 && zft_blk_sz == 1) { /* variable block size */
+               TRACE_ABORT(-EINVAL, ft_t_info,
+                           "Compression not yet supported "
+                           "with variable block size");
+       }
+       zft_mt_compression  = *arg;
+       if ((zft_unit & ZFT_ZIP_MODE) == 0) {
+               zft_use_compression = zft_mt_compression;
+       }
+       TRACE_EXIT 0;
+}
+
+/*  check whether write access is allowed. Write access is denied when
+ *  + zft_write_protected == 1 -- this accounts for either hard write 
+ *                                protection of the cartridge or for 
+ *                                O_RDONLY access mode of the tape device
+ *  + zft_offline == 1         -- this meany that there is either no tape 
+ *                                or that the MTOFFLINE ioctl has been 
+ *                                previously issued (`soft eject')
+ *  + ft_formatted == 0        -- this means that the cartridge is not
+ *                                formatted
+ *  Then we distinuguish two cases. When zft_qic_mode is TRUE, then we try
+ *  to emulate a `traditional' (aka SCSI like) UN*X tape device. Therefore we
+ *  deny writes when
+ *  + zft_qic_mode ==1 && 
+ *       (!zft_tape_at_lbot() &&   -- tape no at logical BOT
+ *        !(zft_tape_at_eom() ||   -- tape not at logical EOM (or EOD)
+ *          (zft_tape_at_eom() &&
+ *           zft_old_ftape())))    -- we can't add new volume to tapes 
+ *                                    written by old ftape because ftape
+ *                                    don't use the volume table
+ *
+ *  when the drive is in true raw mode (aka /dev/rawft0) then we don't 
+ *  care about LBOT and EOM conditions. This device is intended for a 
+ *  user level program that wants to truly implement the QIC-80 compliance
+ *  at the logical data layout level of the cartridge, i.e. implement all
+ *  that volume table and volume directory stuff etc.<
+ */
+int zft_check_write_access(zft_position *pos)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (zft_offline) { /* offline includes no_tape */
+               TRACE_ABORT(-ENXIO,
+                           ft_t_info, "tape is offline or no cartridge");
+       }
+       if (!ft_formatted) {
+               TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted");
+       } 
+       if (zft_write_protected) {
+               TRACE_ABORT(-EACCES, ft_t_info, "cartridge write protected");
+       } 
+       if (zft_qic_mode) {
+               /*  check BOT condition */
+               if (!zft_tape_at_lbot(pos)) {
+                       /*  protect cartridges written by old ftape if
+                        *  not at BOT because they use the vtbl
+                        *  segment for storing data
+                        */
+                       if (zft_old_ftape) {
+                               TRACE_ABORT(-EACCES, ft_t_warn, 
+      "Cannot write to cartridges written by old ftape when not at BOT");
+                       }
+                       /*  not at BOT, but allow writes at EOD, of course
+                        */
+                       if (!zft_tape_at_eod(pos)) {
+                               TRACE_ABORT(-EACCES, ft_t_info,
+                                           "tape not at BOT and not at EOD");
+                       }
+               }
+               /*  fine. Now the tape is either at BOT or at EOD. */
+       }
+       /* or in raw mode in which case we don't care about BOT and EOD */
+       TRACE_EXIT 0;
+}
+
+/*  decide when we should lock the module in memory, even when calling
+ *  the release routine. This really is necessary for use with
+ *  kerneld.
+ *
+ *  NOTE: we MUST NOT use zft_write_protected, because this includes
+ *  the file access mode as well which has no meaning with our 
+ *  asynchronous update scheme.
+ *
+ *  Ugly, ugly. We need to look the module if we changed the block size.
+ *  How sad! Need persistent modules storage!
+ *
+ *  NOTE: I don't want to lock the module if the number of dma buffers 
+ *  has been changed. It's enough! Stop the story! Give me persisitent
+ *  module storage! Do it!
+ */
+int zft_dirty(void)
+{
+       if (!ft_formatted || zft_offline) { 
+               /* cannot be dirty if not formatted or offline */
+               return 0;
+       }
+       if (zft_blk_sz != CONFIG_ZFT_DFLT_BLK_SZ) {
+               /* blocksize changed, must lock */
+               return 1;
+       }
+       if (zft_mt_compression != 0) {
+               /* compression mode with /dev/qft, must lock */
+               return 1;
+       }
+       if (!zft_header_read) {
+               /* tape is logical at BOT, no lock */
+               return 0;
+       }
+       if (!zft_tape_at_lbot(&zft_pos)) {
+               /* somewhere inside a volume, lock tape */
+               return 1;
+       }
+       if (zft_volume_table_changed || zft_header_changed) {
+               /* header segments dirty if tape not write protected */
+               return !(ft_write_protected || zft_old_ftape);
+       }
+       return 0;
+}
+
+/*      OPEN routine called by kernel-interface code
+ *
+ *      NOTE: this is also called by mt_reset() with dev_minor == -1
+ *            to fake a reopen after a reset.
+ */
+int _zft_open(unsigned int dev_minor, unsigned int access_mode)
+{
+       static unsigned int tape_unit;
+       static unsigned int file_access_mode;
+       int result;
+       TRACE_FUN(ft_t_flow);
+
+       if ((int)dev_minor == -1) {
+               /* fake reopen */
+               zft_unit    = tape_unit;
+               access_mode = file_access_mode;
+               zft_init_driver(); /* reset all static data to defaults */
+       } else {
+               tape_unit        = dev_minor;
+               file_access_mode = access_mode;
+               if ((result = ftape_enable(FTAPE_SEL(dev_minor))) < 0) {
+                       TRACE_ABORT(-ENXIO, ft_t_err,
+                                   "ftape_enable failed: %d", result);
+               }
+               if (ft_new_tape || ft_no_tape || !ft_formatted ||
+                   (FTAPE_SEL(zft_unit) != FTAPE_SEL(dev_minor)) ||
+                   (zft_unit & ZFT_RAW_MODE) != (dev_minor & ZFT_RAW_MODE)) {
+                       /* reset all static data to defaults,
+                        */
+                       zft_init_driver(); 
+               }
+               zft_unit = dev_minor;
+       }
+       zft_set_flags(zft_unit); /* decode the minor bits */
+       if (zft_blk_sz == 1 && zft_use_compression) {
+               ftape_disable(); /* resets ft_no_tape */
+               TRACE_ABORT(-ENODEV, ft_t_warn, "Variable block size not yet "
+                           "supported with compression");
+       }
+       /*  no need for most of the buffers when no tape or not
+        *  formatted.  for the read/write operations, it is the
+        *  regardless whether there is no tape, a not-formatted tape
+        *  or the whether the driver is soft offline.  
+        *  Nevertheless we allow some ioctls with non-formatted tapes, 
+        *  like rewind and reset.
+        */
+       if (ft_no_tape || !ft_formatted) {
+               zft_uninit_mem();
+       }
+       if (ft_no_tape) {
+               zft_offline = 1; /* so we need not test two variables */
+       }
+       if ((access_mode == O_WRONLY || access_mode == O_RDWR) &&
+           (ft_write_protected || ft_no_tape)) {
+               ftape_disable(); /* resets ft_no_tape */
+               TRACE_ABORT(ft_no_tape ? -ENXIO : -EROFS,
+                           ft_t_warn, "wrong access mode %s cartridge",
+                           ft_no_tape ? "without a" : "with write protected");
+       }
+       zft_write_protected = (access_mode == O_RDONLY || 
+                              ft_write_protected != 0);
+       if (zft_write_protected) {
+               TRACE(ft_t_noise,
+                     "read only access mode: %d, "
+                     "drive write protected: %d", 
+                     access_mode == O_RDONLY,
+                     ft_write_protected != 0);
+       }
+       if (!zft_offline) {
+               TRACE_CATCH(zft_vmalloc_once(&zft_deblock_buf,FT_SEGMENT_SIZE),
+                           ftape_disable());
+       }
+       /* zft_seg_pos should be greater than the vtbl segpos but not
+        * if in compatability mode and only after we read in the
+        * header segments
+        *
+        * might also be a problem if the user makes a backup with a
+        * *qft* device and rewinds it with a raw device.
+        */
+       if (zft_qic_mode         &&
+           !zft_old_ftape       &&
+           zft_pos.seg_pos >= 0 &&
+           zft_header_read      && 
+           zft_pos.seg_pos <= ft_first_data_segment) {
+               TRACE(ft_t_noise, "you probably mixed up the zftape devices!");
+               zft_reset_position(&zft_pos); 
+       }
+       TRACE_EXIT 0;
+}
+
+/*      RELEASE routine called by kernel-interface code
+ */
+int _zft_close(void)
+{
+       int result = 0;
+       TRACE_FUN(ft_t_flow);
+       
+       if (zft_offline) {
+               /* call the hardware release routine. Puts the drive offline */
+               ftape_disable();
+               TRACE_EXIT 0;
+       }
+       if (!(ft_write_protected || zft_old_ftape)) {
+               result = zft_flush_buffers();
+               TRACE(ft_t_noise, "writing file mark at current position");
+               if (zft_qic_mode && zft_close_volume(&zft_pos) == 0) {
+                       zft_move_past_eof(&zft_pos);
+               }
+               if ((zft_tape_at_lbot(&zft_pos) ||
+                    !(zft_unit & FTAPE_NO_REWIND))) {
+                       if (result >= 0) {
+                               result = zft_update_header_segments();
+                       } else {
+                               TRACE(ft_t_err,
+                               "Error: unable to update header segments");
+                       }
+               }
+       }
+       ftape_abort_operation();
+       if (!(zft_unit & FTAPE_NO_REWIND)) {
+               TRACE(ft_t_noise, "rewinding tape");
+               if (ftape_seek_to_bot() < 0 && result >= 0) {
+                       result = -EIO; /* keep old value */
+               }
+               zft_reset_position(&zft_pos);
+       } 
+       zft_zap_read_buffers();
+       /*  now free up memory as much as possible. We don't destroy
+        *  the deblock buffer if it containes a valid segment.
+        */
+       if (zft_deblock_segment == -1) {
+               zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE); 
+       }
+       /* high level driver status, forces creation of a new volume
+        * when calling ftape_write again and not zft_just_before_eof
+        */
+       zft_io_state = zft_idle;  
+       if (going_offline) {
+               zft_init_driver();
+               zft_uninit_mem();
+               going_offline = 0;
+               zft_offline   = 1;
+       } else if (zft_dirty()) {
+               TRACE(ft_t_noise, "Keeping module locked in memory because:\n"
+                     KERN_INFO "header segments need updating: %s\n"
+                     KERN_INFO "tape not at BOT              : %s",
+                     (zft_volume_table_changed || zft_header_changed) 
+                     ? "yes" : "no",
+                     zft_tape_at_lbot(&zft_pos) ? "no" : "yes");
+       } else if (zft_cmpr_lock(0 /* don't load */) == 0) {
+               (*zft_cmpr_ops->reset)(); /* unlock it again */
+       }
+       zft_memory_stats();
+       /* call the hardware release routine. Puts the drive offline */
+       ftape_disable();
+       TRACE_EXIT result;
+}
+
+/*
+ *  the wrapper function around the wrapper MTIOCTOP ioctl
+ */
+static int mtioctop(struct mtop *mtop, int arg_size)
+{
+       int result = 0;
+       fun_entry *mt_fun_entry;
+       TRACE_FUN(ft_t_flow);
+       
+       if (arg_size != sizeof(struct mtop) || mtop->mt_op >= NR_MT_CMDS) {
+               TRACE_EXIT -EINVAL;
+       }
+       TRACE(ft_t_noise, "calling MTIOCTOP command: %s",
+             mt_funs[mtop->mt_op].name);
+       mt_fun_entry= &mt_funs[mtop->mt_op];
+       zft_resid = mtop->mt_count;
+       if (!mt_fun_entry->offline && zft_offline) {
+               if (ft_no_tape) {
+                       TRACE_ABORT(-ENXIO, ft_t_info, "no tape present");
+               } else {
+                       TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline");
+               }
+       }
+       if (!mt_fun_entry->not_formatted && !ft_formatted) {
+               TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted");
+       }
+       if (!mt_fun_entry->write_protected) {
+               TRACE_CATCH(zft_check_write_access(&zft_pos),);
+       }
+       if (mt_fun_entry->need_idle_state && !(zft_offline || !ft_formatted)) {
+               TRACE_CATCH(zft_def_idle_state(),);
+       }
+       if (!zft_qic_mode && !mt_fun_entry->raw_mode) {
+               TRACE_ABORT(-EACCES, ft_t_info, 
+"Drive needs to be in QIC-80 compatibility mode for this command");
+       }
+       result = (mt_fun_entry->function)(&mtop->mt_count);
+       if (zft_tape_at_lbot(&zft_pos)) {
+               TRACE_CATCH(zft_update_header_segments(),);
+       }
+       if (result >= 0) {
+               zft_resid = 0;
+       }
+       TRACE_EXIT result;
+}
+
+/*
+ *  standard MTIOCGET ioctl
+ */
+static int mtiocget(struct mtget *mtget, int arg_size)
+{
+       const zft_volinfo *volume;
+       __s64 max_tape_pos;
+       TRACE_FUN(ft_t_flow);
+       
+       if (arg_size != sizeof(struct mtget)) {
+               TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d",
+                           arg_size);
+       }
+       mtget->mt_type  = ft_drive_type.vendor_id + 0x800000;
+       mtget->mt_dsreg = ft_last_status.space;
+       mtget->mt_erreg = ft_last_error.space; /* error register */
+       mtget->mt_resid = zft_resid; /* residuum of writes, reads and
+                                     * MTIOCTOP commands 
+                                     */
+       if (!zft_offline) { /* neither no_tape nor soft offline */
+               mtget->mt_gstat = GMT_ONLINE(~0UL);
+               /* should rather return the status of the cartridge
+                * than the access mode of the file, therefor use
+                * ft_write_protected, not zft_write_protected 
+                */
+               if (ft_write_protected) {
+                       mtget->mt_gstat |= GMT_WR_PROT(~0UL);
+               }
+               if(zft_header_read) { /* this catches non-formatted */
+                       volume = zft_find_volume(zft_pos.seg_pos);
+                       mtget->mt_fileno = volume->count;
+                       max_tape_pos = zft_capacity - zft_blk_sz;
+                       if (zft_use_compression) {
+                               max_tape_pos -= ZFT_CMPR_OVERHEAD;
+                       }
+                       if (zft_tape_at_eod(&zft_pos)) {
+                               mtget->mt_gstat |= GMT_EOD(~0UL);
+                       }
+                       if (zft_pos.tape_pos > max_tape_pos) {
+                               mtget->mt_gstat |= GMT_EOT(~0UL);
+                       }
+                       mtget->mt_blkno = zft_div_blksz(zft_pos.volume_pos,
+                                                       volume->blk_sz);
+                       if (zft_just_before_eof) {
+                               mtget->mt_gstat |= GMT_EOF(~0UL);
+                       }
+                       if (zft_tape_at_lbot(&zft_pos)) {
+                               mtget->mt_gstat |= GMT_BOT(~0UL);
+                       }
+               } else {
+                       mtget->mt_fileno = mtget->mt_blkno = -1;
+                       if (mtget->mt_dsreg & QIC_STATUS_AT_BOT) {
+                               mtget->mt_gstat |= GMT_BOT(~0UL);
+                       }
+               }
+       } else {
+               if (ft_no_tape) {
+                       mtget->mt_gstat = GMT_DR_OPEN(~0UL);
+               } else {
+                       mtget->mt_gstat = 0UL;
+               }
+               mtget->mt_fileno = mtget->mt_blkno = -1;
+       }
+       TRACE_EXIT 0;
+}
+
+#ifdef MTIOCRDFTSEG
+/*
+ *  Read a floppy tape segment. This is useful for manipulating the
+ *  volume table, and read the old header segment before re-formatting
+ *  the cartridge.
+ */
+static int mtiocrdftseg(struct mtftseg * mtftseg, int arg_size)
+{
+       TRACE_FUN(ft_t_flow);
+       
+       TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCRDFTSEG");
+       if (zft_qic_mode) {
+               TRACE_ABORT(-EACCES, ft_t_info,
+                           "driver needs to be in raw mode for this ioctl");
+       } 
+       if (arg_size != sizeof(struct mtftseg)) {
+               TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d",
+                           arg_size);
+       }
+       if (zft_offline) {
+               TRACE_EXIT -ENXIO;
+       }
+       if (mtftseg->mt_mode != FT_RD_SINGLE &&
+           mtftseg->mt_mode != FT_RD_AHEAD) {
+               TRACE_ABORT(-EINVAL, ft_t_info, "invalid read mode");
+       }
+       if (!ft_formatted) {
+               TRACE_EXIT -EACCES; /* -ENXIO ? */
+
+       }
+       if (!zft_header_read) {
+               TRACE_CATCH(zft_def_idle_state(),);
+       }
+       if (mtftseg->mt_segno > ft_last_data_segment) {
+               TRACE_ABORT(-EINVAL, ft_t_info, "segment number is too large");
+       }
+       mtftseg->mt_result = ftape_read_segment(mtftseg->mt_segno,
+                                               zft_deblock_buf,
+                                               mtftseg->mt_mode);
+       if (mtftseg->mt_result < 0) {
+               /*  a negativ result is not an ioctl error. if
+                *  the user wants to read damaged tapes,
+                *  it's up to her/him
+                */
+               TRACE_EXIT 0;
+       }
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+       if (copy_to_user(mtftseg->mt_data,
+                        zft_deblock_buf,
+                        mtftseg->mt_result) != 0) {
+               TRACE_EXIT -EFAULT;
+       }
+#else
+       TRACE_CATCH(verify_area(VERIFY_WRITE, mtftseg->mt_data,
+                               mtftseg->mt_result),);
+       memcpy_tofs(mtftseg->mt_data, zft_deblock_buf, 
+                   mtftseg->mt_result);
+#endif
+       TRACE_EXIT 0;
+}
+#endif
+
+#ifdef MTIOCWRFTSEG
+/*
+ *  write a floppy tape segment. This version features writing of
+ *  deleted address marks, and gracefully ignores the (software)
+ *  ft_formatted flag to support writing of header segments after
+ *  formatting.
+ */
+static int mtiocwrftseg(struct mtftseg * mtftseg, int arg_size)
+{
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCWRFTSEG");
+       if (zft_write_protected || zft_qic_mode) {
+               TRACE_EXIT -EACCES;
+       } 
+       if (arg_size != sizeof(struct mtftseg)) {
+               TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d",
+                           arg_size);
+       }
+       if (zft_offline) {
+               TRACE_EXIT -ENXIO;
+       }
+       if (mtftseg->mt_mode != FT_WR_ASYNC   && 
+           mtftseg->mt_mode != FT_WR_MULTI   &&
+           mtftseg->mt_mode != FT_WR_SINGLE  &&
+           mtftseg->mt_mode != FT_WR_DELETE) {
+               TRACE_ABORT(-EINVAL, ft_t_info, "invalid write mode");
+       }
+       /*
+        *  We don't check for ft_formatted, because this gives
+        *  only the software status of the driver.
+        *
+        *  We assume that the user knows what it is
+        *  doing. And rely on the low level stuff to fail
+        *  when the tape isn't formatted. We only make sure
+        *  that The header segment buffer is allocated,
+        *  because it holds the bad sector map.
+        */
+       if (zft_hseg_buf == NULL) {
+               TRACE_EXIT -ENXIO;
+       }
+       if (mtftseg->mt_mode != FT_WR_DELETE) {
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+               if (copy_from_user(zft_deblock_buf, 
+                                  mtftseg->mt_data,
+                                  FT_SEGMENT_SIZE) != 0) {
+                       TRACE_EXIT -EFAULT;
+               }
+#else
+               TRACE_CATCH(verify_area(VERIFY_READ, 
+                                       mtftseg->mt_data, 
+                                       FT_SEGMENT_SIZE),);
+               memcpy_fromfs(zft_deblock_buf, mtftseg->mt_data,
+                             FT_SEGMENT_SIZE);
+#endif
+       }
+       mtftseg->mt_result = ftape_write_segment(mtftseg->mt_segno, 
+                                                zft_deblock_buf,
+                                                mtftseg->mt_mode);
+       if (mtftseg->mt_result >= 0 && mtftseg->mt_mode == FT_WR_SINGLE) {
+               /*  
+                *  a negativ result is not an ioctl error. if
+                *  the user wants to write damaged tapes,
+                *  it's up to her/him
+                */
+               if ((result = ftape_loop_until_writes_done()) < 0) {
+                       mtftseg->mt_result = result;
+               }
+       }
+       TRACE_EXIT 0;
+}
+#endif
+  
+#ifdef MTIOCVOLINFO
+/*
+ *  get information about volume positioned at.
+ */
+static int mtiocvolinfo(struct mtvolinfo *volinfo, int arg_size)
+{
+       const zft_volinfo *volume;
+       TRACE_FUN(ft_t_flow);
+       
+       TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCVOLINFO");
+       if (arg_size != sizeof(struct mtvolinfo)) {
+               TRACE_ABORT(-EINVAL,
+                           ft_t_info, "bad argument size: %d", arg_size);
+       }
+       if (zft_offline) {
+               TRACE_EXIT -ENXIO;
+       }
+       if (!ft_formatted) {
+               TRACE_EXIT -EACCES;
+       }
+       TRACE_CATCH(zft_def_idle_state(),);
+       volume = zft_find_volume(zft_pos.seg_pos);
+       volinfo->mt_volno   = volume->count;
+       volinfo->mt_blksz   = volume->blk_sz == 1 ? 0 : volume->blk_sz;
+       volinfo->mt_size    = volume->size >> 10;
+       volinfo->mt_rawsize = ((zft_calc_tape_pos(volume->end_seg + 1) >> 10) -
+                              (zft_calc_tape_pos(volume->start_seg) >> 10));
+       volinfo->mt_cmpr    = volume->use_compression;
+       TRACE_EXIT 0;
+}
+#endif
+
+#ifdef ZFT_OBSOLETE  
+static int mtioc_zftape_getblksz(struct mtblksz *blksz, int arg_size)
+{
+       TRACE_FUN(ft_t_flow);
+       
+       TRACE(ft_t_noise, "\n"
+             KERN_INFO "Mag tape ioctl command: MTIOC_ZTAPE_GETBLKSZ\n"
+             KERN_INFO "This ioctl is here merely for compatibility.\n"
+             KERN_INFO "Please use MTIOCVOLINFO instead");
+       if (arg_size != sizeof(struct mtblksz)) {
+               TRACE_ABORT(-EINVAL,
+                           ft_t_info, "bad argument size: %d", arg_size);
+       }
+       if (zft_offline) {
+               TRACE_EXIT -ENXIO;
+       }
+       if (!ft_formatted) {
+               TRACE_EXIT -EACCES;
+       }
+       TRACE_CATCH(zft_def_idle_state(),);
+       blksz->mt_blksz = zft_find_volume(zft_pos.seg_pos)->blk_sz;
+       TRACE_EXIT 0;
+}
+#endif
+
+#ifdef MTIOCGETSIZE
+/*
+ *  get the capacity of the tape cartridge.
+ */
+static int mtiocgetsize(struct mttapesize *size, int arg_size)
+{
+       TRACE_FUN(ft_t_flow);
+       
+       TRACE(ft_t_noise, "Mag tape ioctl command: MTIOC_ZFTAPE_GETSIZE");
+       if (arg_size != sizeof(struct mttapesize)) {
+               TRACE_ABORT(-EINVAL,
+                           ft_t_info, "bad argument size: %d", arg_size);
+       }
+       if (zft_offline) {
+               TRACE_EXIT -ENXIO;
+       }
+       if (!ft_formatted) {
+               TRACE_EXIT -EACCES;
+       }
+       TRACE_CATCH(zft_def_idle_state(),);
+       size->mt_capacity = (unsigned int)(zft_capacity>>10);
+       size->mt_used     = (unsigned int)(zft_get_eom_pos()>>10);
+       TRACE_EXIT 0;
+}
+#endif
+
+static int mtiocpos(struct mtpos *mtpos, int arg_size)
+{
+       int result;
+       TRACE_FUN(ft_t_flow);
+       
+       TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCPOS");
+       if (arg_size != sizeof(struct mtpos)) {
+               TRACE_ABORT(-EINVAL,
+                           ft_t_info, "bad argument size: %d", arg_size);
+       }
+       result = mt_tell((int *)&mtpos->mt_blkno);
+       TRACE_EXIT result;
+}
+
+#ifdef MTIOCFTFORMAT
+/*
+ * formatting of floppy tape cartridges. This is intended to be used
+ * together with the MTIOCFTCMD ioctl and the new mmap feature 
+ */
+
+/* 
+ *  This function uses ftape_decode_header_segment() to inform the low
+ *  level ftape module about the new parameters.
+ *
+ *  It erases the hseg_buf. The calling process must specify all
+ *  parameters to assure proper operation.
+ *
+ *  return values: -EINVAL - wrong argument size
+ *                 -EINVAL - if ftape_decode_header_segment() failed.
+ */
+static int set_format_parms(struct ftfmtparms *p, __u8 *hseg_buf)
+{
+       ft_trace_t old_level = TRACE_LEVEL;
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_SETPARMS");
+       memset(hseg_buf, 0, FT_SEGMENT_SIZE);
+       PUT4(hseg_buf, FT_SIGNATURE, FT_HSEG_MAGIC);
+
+       /*  fill in user specified parameters
+        */
+       hseg_buf[FT_FMT_CODE] = (__u8)p->ft_fmtcode;
+       PUT2(hseg_buf, FT_SPT, p->ft_spt);
+       hseg_buf[FT_TPC]      = (__u8)p->ft_tpc;
+       hseg_buf[FT_FHM]      = (__u8)p->ft_fhm;
+       hseg_buf[FT_FTM]      = (__u8)p->ft_ftm;
+
+       /*  fill in sane defaults to make ftape happy.
+        */ 
+       hseg_buf[FT_FSM] = (__u8)128; /* 128 is hard wired all over ftape */
+       if (p->ft_fmtcode == fmt_big) {
+               PUT4(hseg_buf, FT_6_HSEG_1,   0);
+               PUT4(hseg_buf, FT_6_HSEG_2,   1);
+               PUT4(hseg_buf, FT_6_FRST_SEG, 2);
+               PUT4(hseg_buf, FT_6_LAST_SEG, p->ft_spt * p->ft_tpc - 1);
+       } else {
+               PUT2(hseg_buf, FT_HSEG_1,    0);
+               PUT2(hseg_buf, FT_HSEG_2,    1);
+               PUT2(hseg_buf, FT_FRST_SEG,  2);
+               PUT2(hseg_buf, FT_LAST_SEG, p->ft_spt * p->ft_tpc - 1);
+       }
+
+       /*  Synchronize with the low level module. This is particularly
+        *  needed for unformatted cartridges as the QIC std was previously 
+        *  unknown BUT is needed to set data rate and to calculate timeouts.
+        */
+       TRACE_CATCH(ftape_calibrate_data_rate(p->ft_qicstd&QIC_TAPE_STD_MASK),
+                   _res = -EINVAL);
+
+       /*  The following will also recalcualte the timeouts for the tape
+        *  length and QIC std we want to format to.
+        *  abort with -EINVAL rather than -EIO
+        */
+       SET_TRACE_LEVEL(ft_t_warn);
+       TRACE_CATCH(ftape_decode_header_segment(hseg_buf),
+                   SET_TRACE_LEVEL(old_level); _res = -EINVAL);
+       SET_TRACE_LEVEL(old_level);
+       TRACE_EXIT 0;
+}
+
+/*
+ *  Return the internal SOFTWARE status of the kernel driver. This does
+ *  NOT query the tape drive about its status.
+ */
+static int get_format_parms(struct ftfmtparms *p, __u8 *hseg_buffer)
+{
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_GETPARMS");
+       p->ft_qicstd  = ft_qic_std;
+       p->ft_fmtcode = ft_format_code;
+       p->ft_fhm     = hseg_buffer[FT_FHM];
+       p->ft_ftm     = hseg_buffer[FT_FTM];
+       p->ft_spt     = ft_segments_per_track;
+       p->ft_tpc     = ft_tracks_per_tape;
+       TRACE_EXIT 0;
+}
+
+static int mtiocftformat(struct mtftformat *mtftformat, int arg_size)
+{
+       int result;
+       union fmt_arg *arg = &mtftformat->fmt_arg;
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTFORMAT");
+       if (zft_offline) {
+               if (ft_no_tape) {
+                       TRACE_ABORT(-ENXIO, ft_t_info, "no tape present");
+               } else {
+                       TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline");
+               }
+       }
+       if (zft_qic_mode) {
+               TRACE_ABORT(-EACCES, ft_t_info,
+                           "driver needs to be in raw mode for this ioctl");
+       } 
+       if (zft_hseg_buf == NULL) {
+               TRACE_CATCH(zft_vcalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),);
+       }
+       zft_header_read = 0;
+       switch(mtftformat->fmt_op) {
+       case FTFMT_SET_PARMS:
+               TRACE_CATCH(set_format_parms(&arg->fmt_parms, zft_hseg_buf),);
+               TRACE_EXIT 0;
+       case FTFMT_GET_PARMS:
+               TRACE_CATCH(get_format_parms(&arg->fmt_parms, zft_hseg_buf),);
+               TRACE_EXIT 0;
+       case FTFMT_FORMAT_TRACK:
+               if ((ft_formatted && zft_check_write_access(&zft_pos) < 0) ||
+                   (!ft_formatted && zft_write_protected)) {
+                       TRACE_ABORT(-EACCES, ft_t_info, "Write access denied");
+               }
+               TRACE_CATCH(ftape_format_track(arg->fmt_track.ft_track,
+                                              arg->fmt_track.ft_gap3),);
+               TRACE_EXIT 0;
+       case FTFMT_STATUS:
+               TRACE_CATCH(ftape_format_status(&arg->fmt_status.ft_segment),);
+               TRACE_EXIT 0;
+       case FTFMT_VERIFY:
+               TRACE_CATCH(ftape_verify_segment(arg->fmt_verify.ft_segment,
+                               (SectorMap *)&arg->fmt_verify.ft_bsm),);
+               TRACE_EXIT 0;
+       default:
+               TRACE_ABORT(-EINVAL, ft_t_err, "Invalid format operation");
+       }
+       TRACE_EXIT result;
+}
+#endif
+
+#ifdef MTIOCFTCMD
+/*
+ *  send a QIC-117 command to the drive, with optional timeouts,
+ *  parameter and result bits. This is intended to be used together
+ *  with the formatting ioctl.
+ */
+static int mtiocftcmd(struct mtftcmd *ftcmd, int arg_size)
+{
+       int i;
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTCMD");
+       if (!suser()) {
+               TRACE_ABORT(-EPERM, ft_t_info,
+                           "only the superuser may send raw qic-117 commands");
+       }
+       if (zft_qic_mode) {
+               TRACE_ABORT(-EACCES, ft_t_info,
+                           "driver needs to be in raw mode for this ioctl");
+       } 
+       if (arg_size != sizeof(struct mtftcmd)) {
+               TRACE_ABORT(-EINVAL,
+                           ft_t_info, "bad argument size: %d", arg_size);
+       }
+       if (ftcmd->ft_wait_before) {
+               TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_before,
+                                            &ftcmd->ft_status),);
+       }
+       if (ftcmd->ft_status & QIC_STATUS_ERROR)
+               goto ftmtcmd_error;
+       if (ftcmd->ft_result_bits != 0) {
+               TRACE_CATCH(ftape_report_operation(&ftcmd->ft_result,
+                                                  ftcmd->ft_cmd,
+                                                  ftcmd->ft_result_bits),);
+       } else {
+               TRACE_CATCH(ftape_command(ftcmd->ft_cmd),);
+               if (ftcmd->ft_status & QIC_STATUS_ERROR)
+                       goto ftmtcmd_error;
+               for (i = 0; i < ftcmd->ft_parm_cnt; i++) {
+                       TRACE_CATCH(ftape_parameter(ftcmd->ft_parms[i]&0x0f),);
+                       if (ftcmd->ft_status & QIC_STATUS_ERROR)
+                               goto ftmtcmd_error;
+               }
+       }
+       if (ftcmd->ft_wait_after != 0) {
+               TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_after,
+                                            &ftcmd->ft_status),);
+       }
+ftmtcmd_error:        
+       if (ftcmd->ft_status & QIC_STATUS_ERROR) {
+               TRACE(ft_t_noise, "error status set");
+               TRACE_CATCH(ftape_report_error(&ftcmd->ft_error,
+                                              &ftcmd->ft_cmd, 1),);
+       }
+       TRACE_EXIT 0; /* this is not an i/o error */
+}
+#endif
+
+/*  IOCTL routine called by kernel-interface code
+ */
+int _zft_ioctl(unsigned int command, void * arg)
+{
+       int result;
+       union { struct mtop       mtop;
+               struct mtget      mtget;
+               struct mtpos      mtpos;
+#ifdef MTIOCRDFTSEG
+               struct mtftseg    mtftseg;
+#endif
+#ifdef MTIOCVOLINFO
+               struct mtvolinfo  mtvolinfo;
+#endif
+#ifdef MTIOCGETSIZE
+               struct mttapesize mttapesize;
+#endif
+#ifdef MTIOCFTFORMAT
+               struct mtftformat mtftformat;
+#endif
+#ifdef ZFT_OBSOLETE
+               struct mtblksz mtblksz;
+#endif
+#ifdef MTIOCFTCMD
+               struct mtftcmd mtftcmd;
+#endif
+       } krnl_arg;
+       int arg_size = _IOC_SIZE(command);
+       int dir = _IOC_DIR(command);
+       TRACE_FUN(ft_t_flow);
+
+       /* This check will only catch arguments that are too large !
+        */
+       if (dir & (_IOC_READ | _IOC_WRITE) && arg_size > sizeof(krnl_arg)) {
+               TRACE_ABORT(-EINVAL,
+                           ft_t_info, "bad argument size: %d", arg_size);
+       }
+       if (dir & _IOC_WRITE) {
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+               if (copy_from_user(&krnl_arg, arg, arg_size) != 0) {
+                       TRACE_EXIT -EFAULT;
+               }
+#else
+               TRACE_CATCH(verify_area(VERIFY_READ, arg, arg_size),);
+               memcpy_fromfs(&krnl_arg, arg, arg_size);
+#endif
+       }
+       TRACE(ft_t_flow, "called with ioctl command: 0x%08x", command);
+       switch (command) {
+       case MTIOCTOP:
+               result = mtioctop(&krnl_arg.mtop, arg_size);
+               break;
+       case MTIOCGET:
+               result = mtiocget(&krnl_arg.mtget, arg_size);
+               break;
+       case MTIOCPOS:
+               result = mtiocpos(&krnl_arg.mtpos, arg_size);
+               break;
+#ifdef MTIOCVOLINFO
+       case MTIOCVOLINFO:
+               result = mtiocvolinfo(&krnl_arg.mtvolinfo, arg_size);
+               break;
+#endif
+#ifdef ZFT_OBSOLETE
+       case MTIOC_ZFTAPE_GETBLKSZ:
+               result = mtioc_zftape_getblksz(&krnl_arg.mtblksz, arg_size);
+               break;
+#endif
+#ifdef MTIOCRDFTSEG
+       case MTIOCRDFTSEG: /* read a segment via ioctl */
+               result = mtiocrdftseg(&krnl_arg.mtftseg, arg_size);
+               break;
+#endif
+#ifdef MTIOCWRFTSEG
+       case MTIOCWRFTSEG: /* write a segment via ioctl */
+               result = mtiocwrftseg(&krnl_arg.mtftseg, arg_size);
+               break;
+#endif
+#ifdef MTIOCGETSIZE
+       case MTIOCGETSIZE:
+               result = mtiocgetsize(&krnl_arg.mttapesize, arg_size);
+               break;
+#endif
+#ifdef MTIOCFTFORMAT
+       case MTIOCFTFORMAT:
+               result = mtiocftformat(&krnl_arg.mtftformat, arg_size);
+               break;
+#endif
+#ifdef MTIOCFTCMD
+       case MTIOCFTCMD:
+               result = mtiocftcmd(&krnl_arg.mtftcmd, arg_size);
+               break;
+#endif
+       default:
+               result = -EINVAL;
+               break;
+       }
+       if ((result >= 0) && (dir & _IOC_READ)) {
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+               if (copy_to_user(arg, &krnl_arg, arg_size) != 0) {
+                       TRACE_EXIT -EFAULT;
+               }
+#else
+               TRACE_CATCH(verify_area(VERIFY_WRITE, arg, arg_size),);
+               memcpy_tofs(arg, &krnl_arg, arg_size);
+#endif
+       }
+       TRACE_EXIT result;
+}
diff --git a/drivers/char/ftape/zftape/zftape-ctl.h b/drivers/char/ftape/zftape/zftape-ctl.h
new file mode 100644 (file)
index 0000000..91a2f2d
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef _ZFTAPE_CTL_H
+#define _ZFTAPE_CTL_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version. 
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-ctl.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:02 $
+ *
+ *      This file contains the non-standard IOCTL related definitions
+ *      for the QIC-40/80 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/ioctl.h>
+#include <linux/mtio.h>
+
+#include "../zftape/zftape-rw.h"
+
+#ifdef CONFIG_ZFTAPE_MODULE
+#define ftape_status (*zft_status)
+#endif
+
+extern int zft_offline;
+extern int zft_mt_compression;
+extern int zft_write_protected;
+extern int zft_header_read;
+extern unsigned int zft_unit;
+extern int zft_resid;
+
+extern void zft_reset_position(zft_position *pos);
+extern int  zft_check_write_access(zft_position *pos);
+extern int  zft_def_idle_state(void);
+extern int  zft_dirty(void);
+
+/*  hooks for the VFS interface 
+ */
+extern int  _zft_open(unsigned int dev_minor, unsigned int access_mode);
+extern int  _zft_close(void);
+extern int  _zft_ioctl(unsigned int command, void *arg);
+#endif
+
+
+
diff --git a/drivers/char/ftape/zftape/zftape-eof.c b/drivers/char/ftape/zftape/zftape-eof.c
new file mode 100644 (file)
index 0000000..0dfd27f
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ *   I use these routines just to decide when I have to fake a 
+ *   volume-table to preserve compatability to original ftape.
+ */
+/*
+ *      Copyright (C) 1994-1995 Bas Laarhoven.
+ *      
+ *      Modified for zftape 1996, 1997 Claus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-eof.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:02 $
+ *
+ *      This file contains the eof mark handling code
+ *      for the QIC-40/80 floppy-tape driver for Linux.
+ */
+
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/zftape.h>
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-eof.h"
+
+/*      Global vars.
+ */
+
+/* a copy of the failed sector log from the header segment.
+ */
+eof_mark_union *zft_eof_map;
+
+/* number of eof marks (entries in bad sector log) on tape.
+ */
+int zft_nr_eof_marks = -1;
+
+
+/*      Local vars.
+ */
+
+static char linux_tape_label[] = "Linux raw format V";
+enum { 
+       min_fmt_version = 1, max_fmt_version = 2 
+};
+static unsigned ftape_fmt_version = 0;
+
+
+/* Ftape (mis)uses the bad sector log to record end-of-file marks.
+ * Initially (when the tape is erased) all entries in the bad sector
+ * log are added to the tape's bad sector map. The bad sector log then
+ * is cleared.
+ *
+ * The bad sector log normally contains entries of the form: 
+ * even 16-bit word: segment number of bad sector 
+ * odd 16-bit word: encoded date
+ * There can be a total of 448 entries (1792 bytes).
+ *
+ * My guess is that no program is using this bad sector log (the *
+ * format seems useless as there is no indication of the bad sector
+ * itself, only the segment) However, if any program does use the bad
+ * sector log, the format used by ftape will let the program think
+ * there are some bad sectors and no harm is done.
+ *  
+ * The eof mark entries that ftape stores in the bad sector log: even
+ * 16-bit word: segment number of eof mark odd 16-bit word: sector
+ * number of eof mark [1..32]
+ *  
+ * The zft_eof_map as maintained is a sorted list of eof mark entries.
+ *
+ *
+ * The tape name field in the header segments is used to store a linux
+ * tape identification string and a version number.  This way the tape
+ * can be recognized as a Linux raw format tape when using tools under
+ * other OS's.
+ *
+ * 'Wide' QIC tapes (format code 4) don't have a failed sector list
+ * anymore. That space is used for the (longer) bad sector map that
+ * now is a variable length list too.  We now store our end-of-file
+ * marker list after the bad-sector-map on tape. The list is delimited
+ * by a (__u32) 0 entry.
+ */
+
+int zft_ftape_validate_label(char *label)
+{
+       static char tmp_label[45];
+       int result = 0;
+       TRACE_FUN(ft_t_any);
+       
+       memcpy(tmp_label, label, FT_LABEL_SZ);
+       tmp_label[FT_LABEL_SZ] = '\0';
+       TRACE(ft_t_noise, "tape  label = `%s'", tmp_label);
+       ftape_fmt_version = 0;
+       if (memcmp(label, linux_tape_label, strlen(linux_tape_label)) == 0) {
+               int pos = strlen(linux_tape_label);
+               while (label[pos] >= '0' && label[pos] <= '9') {
+                       ftape_fmt_version *= 10;
+                       ftape_fmt_version = label[ pos++] - '0';
+               }
+               result = (ftape_fmt_version >= min_fmt_version &&
+                         ftape_fmt_version <= max_fmt_version);
+       }
+       TRACE(ft_t_noise, "format version = %d", ftape_fmt_version);
+       TRACE_EXIT result;
+}
+
+static __u8 * find_end_of_eof_list(__u8 * ptr, __u8 * limit)
+{
+       while (ptr + 3 < limit) {
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,0,0)
+               if (get_unaligned((__u32*)ptr)) {
+                       ++(__u32*)ptr;
+               } else {
+                       return ptr;
+               }
+#else
+               if (*(__u32*)ptr) {
+                       ++(__u32*)ptr;
+               } else {
+                       return ptr;
+               }
+#endif
+       }
+       return NULL;
+}
+
+void zft_ftape_extract_file_marks(__u8* address)
+{
+       int i;
+       TRACE_FUN(ft_t_any);
+       
+       zft_eof_map = NULL;
+       if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
+               __u8* end;
+               __u8* start = ftape_find_end_of_bsm_list(address);
+
+               zft_nr_eof_marks = 0;
+               if (start) {
+                       start += 3; /* skip end of list mark */
+                       end = find_end_of_eof_list(start, 
+                                                  address + FT_SEGMENT_SIZE);
+                       if (end && end - start <= FT_FSL_SIZE) {
+                               zft_nr_eof_marks = ((end - start) / 
+                                                   sizeof(eof_mark_union));
+                               zft_eof_map = (eof_mark_union *)start;
+                       } else {
+                               TRACE(ft_t_err,
+                                     "EOF Mark List is too long or damaged!");
+                       }
+               } else {
+                       TRACE(ft_t_err, 
+                             "Bad Sector List is too long or damaged !");
+               }
+       } else {
+               zft_eof_map = (eof_mark_union *)&address[FT_FSL];
+               zft_nr_eof_marks = GET2(address, FT_FSL_CNT);
+       }
+       TRACE(ft_t_noise, "number of file marks: %d", zft_nr_eof_marks);
+       if (ftape_fmt_version == 1) {
+               TRACE(ft_t_info, "swapping version 1 fields");
+               /* version 1 format uses swapped sector and segment
+                * fields, correct that !  
+                */
+               for (i = 0; i < zft_nr_eof_marks; ++i) {
+                       __u16 tmp = GET2(&zft_eof_map[i].mark.segment,0);
+                       PUT2(&zft_eof_map[i].mark.segment, 0, 
+                            GET2(&zft_eof_map[i].mark.date,0));
+                       PUT2(&zft_eof_map[i].mark.date, 0, tmp);
+               }
+       }
+       for (i = 0; i < zft_nr_eof_marks; ++i) {
+               TRACE(ft_t_noise, "eof mark: %5d/%2d",
+                       GET2(&zft_eof_map[i].mark.segment, 0), 
+                       GET2(&zft_eof_map[i].mark.date,0));
+       }
+       TRACE_EXIT;
+}
+
+void zft_clear_ftape_file_marks(void)
+{
+       TRACE_FUN(ft_t_flow);
+       /*  Clear failed sector log: remove all tape marks. We
+        *  don't use old ftape-style EOF-marks.
+        */
+       TRACE(ft_t_info, "Clearing old ftape's eof map");
+       memset(zft_eof_map, 0, zft_nr_eof_marks * sizeof(__u32));
+       zft_nr_eof_marks = 0;
+       PUT2(zft_hseg_buf, FT_FSL_CNT, 0); /* nr of eof-marks */
+       zft_header_changed = 1;
+       zft_update_label(zft_hseg_buf);
+       TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/zftape/zftape-eof.h b/drivers/char/ftape/zftape/zftape-eof.h
new file mode 100644 (file)
index 0000000..26568c2
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef _ZFTAPE_EOF_H
+#define _ZFTAPE_EOF_H
+
+/*
+ * Copyright (C) 1994-1995 Bas Laarhoven.
+ * adaptaed for zftape 1996, 1997 by Claus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-eof.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:03 $
+ *
+ *      Definitions and declarations for the end of file markers
+ *      for the QIC-40/80 floppy-tape driver for Linux.
+ */
+
+#include <linux/ftape-header-segment.h>
+#include "../zftape/zftape-buffers.h"
+/*  failed sector log size (only used if format code != 4).
+ */
+
+typedef union {
+       ft_fsl_entry mark;
+       __u32 entry;
+} eof_mark_union;
+/*      ftape-eof.c defined global vars.
+ */
+extern int zft_nr_eof_marks;
+extern eof_mark_union *zft_eof_map;
+
+/*      ftape-eof.c defined global functions.
+ */
+extern void zft_ftape_extract_file_marks(__u8* address);
+extern int  zft_ftape_validate_label(char* label);
+extern void zft_clear_ftape_file_marks(void);
+
+#endif
diff --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c
new file mode 100644 (file)
index 0000000..c6a4f45
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ *      Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ *      This file contains the code that registers the zftape frontend 
+ *      to the ftape floppy tape driver for Linux
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <asm/segment.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+#include <linux/fcntl.h>
+#include <linux/wrapper.h>
+
+#include <linux/zftape.h>
+#if LINUX_VERSION_CODE >=KERNEL_VER(2,1,16)
+#include <linux/init.h>
+#else
+#define __initdata
+#define __initfunc(__arg) __arg
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-buffers.h"
+#include "../zftape/zftape_syms.h"
+
+char zft_src[] __initdata = "$Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-init.c,v $";
+char zft_rev[] __initdata = "$Revision: 1.8 $";
+char zft_dat[] __initdata = "$Date: 1997/11/06 00:48:56 $";
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+MODULE_AUTHOR("(c) 1996, 1997 Claus-Justus Heine "
+             "(claus@momo.math.rwth-aachen.de)");
+MODULE_DESCRIPTION(ZFTAPE_VERSION " - "
+                  "VFS interface for the Linux floppy tape driver. "
+                  "Support for QIC-113 compatible volume table "
+                  "and builtin compression (lzrw3 algorithm)");
+MODULE_SUPPORTED_DEVICE("char-major-27");
+#endif
+
+/*      Global vars.
+ */
+struct zft_cmpr_ops *zft_cmpr_ops = NULL;
+const ftape_info *zft_status;
+
+/*      Local vars.
+ */
+static int busy_flag = 0;
+static int orig_sigmask;
+
+/*  the interface to the kernel vfs layer
+ */
+
+/* Note about llseek():
+ *
+ * st.c and tpqic.c update fp->f_pos but don't implment llseek() and
+ * initialize the llseek component of the file_ops struct with NULL.
+ * This means that the user will get the default seek, but the tape
+ * device will not respect the new position, but happily read from the
+ * old position. Think a zftape specific llseek() function would be
+ * better, returning -ESPIPE. TODO.
+ */
+
+static int  zft_open (struct inode *ino, struct file *filep);
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31)
+static int zft_close(struct inode *ino, struct file *filep);
+#else
+static void zft_close(struct inode *ino, struct file *filep);
+#endif
+static int  zft_ioctl(struct inode *ino, struct file *filep,
+                     unsigned int command, unsigned long arg);
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,56)
+static int  zft_mmap(struct file *filep, struct vm_area_struct *vma);
+#else
+static int  zft_mmap(struct inode *ino, struct file *filep,
+                    struct vm_area_struct *vma);
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+static ssize_t zft_read (struct file *fp, char *buff,
+                        size_t req_len, loff_t *ppos);
+static ssize_t zft_write(struct file *fp, const char *buff,
+                        size_t req_len, loff_t *ppos);
+#elif LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long zft_read (struct inode *ino, struct file *fp, char *buff,
+                     unsigned long req_len);
+static long zft_write(struct inode *ino, struct file *fp, const char *buff,
+                     unsigned long req_len);
+#else
+static int  zft_read (struct inode *ino, struct file *fp, char *buff,
+                     int req_len); 
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,3,0)
+static int  zft_write(struct inode *ino, struct file *fp, const char *buff,
+                     int req_len);
+#else
+static int  zft_write(struct inode *ino, struct file *fp, char *buff,
+                     int req_len);
+#endif
+#endif
+
+static struct file_operations zft_cdev =
+{
+       NULL,                   /* llseek */
+       zft_read,               /* read */
+       zft_write,              /* write */
+       NULL,                   /* readdir */
+       NULL,                   /* select */
+       zft_ioctl,              /* ioctl */
+       zft_mmap,               /* mmap */
+       zft_open,               /* open */
+       zft_close,              /* release */
+       NULL,                   /* fsync */
+};
+
+/*      Open floppy tape device
+ */
+static int zft_open(struct inode *ino, struct file *filep)
+{
+       int result;
+       TRACE_FUN(ft_t_flow);
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+       if (!MOD_IN_USE) {
+               MOD_INC_USE_COUNT; /* lock module in memory */
+       }
+#else
+       MOD_INC_USE_COUNT; /*  sets MOD_VISITED and MOD_USED_ONCE,
+                           *  locking is done with can_unload()
+                           */
+#endif
+       TRACE(ft_t_flow, "called for minor %d", MINOR(ino->i_rdev));
+       if (busy_flag) {
+               TRACE_ABORT(-EBUSY, ft_t_warn, "failed: already busy");
+       }
+       busy_flag = 1;
+       if ((MINOR(ino->i_rdev) & ~(ZFT_MINOR_OP_MASK | FTAPE_NO_REWIND))
+            > 
+           FTAPE_SEL_D) {
+               busy_flag = 0;
+#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+               if (!zft_dirty()) {
+                       MOD_DEC_USE_COUNT; /* unlock module in memory */
+               }
+#endif
+               TRACE_ABORT(-ENXIO, ft_t_err, "failed: illegal unit nr");
+       }
+       orig_sigmask = current->blocked;
+       current->blocked = _BLOCK_ALL;
+       result = _zft_open(MINOR(ino->i_rdev), filep->f_flags & O_ACCMODE);
+       if (result < 0) {
+               current->blocked = orig_sigmask; /* restore mask */
+               busy_flag = 0;
+#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+               if (!zft_dirty()) {
+                       MOD_DEC_USE_COUNT; /* unlock module in memory */
+               }
+#endif
+               TRACE_ABORT(result, ft_t_err, "_ftape_open failed");
+       } else {
+               /* Mask signals that will disturb proper operation of the
+                * program that is calling.
+                */
+               current->blocked = orig_sigmask | _DO_BLOCK;
+               TRACE_EXIT 0;
+       }
+}
+
+/*      Close floppy tape device
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31)
+static int zft_close(struct inode *ino, struct file *filep)
+#else
+static void zft_close(struct inode *ino, struct file *filep)
+#endif
+{
+       int result;
+       TRACE_FUN(ft_t_flow);
+
+       if (!busy_flag || MINOR(ino->i_rdev) != zft_unit) {
+               TRACE(ft_t_err, "failed: not busy or wrong unit");
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31)
+               TRACE_EXIT 0;
+#else
+               TRACE_EXIT; /* keep busy_flag !(?) */
+#endif
+       }
+       current->blocked = _BLOCK_ALL;
+       result = _zft_close();
+       if (result < 0) {
+               TRACE(ft_t_err, "_zft_close failed");
+       }
+       current->blocked = orig_sigmask; /* restore before open state */
+       busy_flag = 0;
+#if defined(MODULE) && LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+       if (!zft_dirty()) {
+               MOD_DEC_USE_COUNT; /* unlock module in memory */
+       }
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,31)
+       TRACE_EXIT 0;
+#else
+       TRACE_EXIT;
+#endif
+}
+
+/*      Ioctl for floppy tape device
+ */
+static int zft_ioctl(struct inode *ino, struct file *filep,
+                    unsigned int command, unsigned long arg)
+{
+       int result = -EIO;
+       int old_sigmask;
+       TRACE_FUN(ft_t_flow);
+
+       if (!busy_flag || MINOR(ino->i_rdev) != zft_unit || ft_failure) {
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "failed: not busy, failure or wrong unit");
+       }
+       old_sigmask = current->blocked; /* save mask */
+       current->blocked = _BLOCK_ALL;
+       /* This will work as long as sizeof(void *) == sizeof(long) */
+       result = _zft_ioctl(command, (void *) arg);
+       current->blocked = old_sigmask; /* restore mask */
+       TRACE_EXIT result;
+}
+
+/*      Ioctl for floppy tape device
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,56)
+static int  zft_mmap(struct file *filep, struct vm_area_struct *vma)
+#else
+static int zft_mmap(struct inode *ino,
+                   struct file *filep,
+                   struct vm_area_struct *vma)
+#endif
+{
+       int result = -EIO;
+       int old_sigmask;
+       TRACE_FUN(ft_t_flow);
+
+       if (!busy_flag || 
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,56)
+           MINOR(filep->f_dentry->d_inode->i_rdev) != zft_unit || 
+#else
+           MINOR(ino->i_rdev) != zft_unit ||
+#endif
+           ft_failure)
+       {
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "failed: not busy, failure or wrong unit");
+       }
+       old_sigmask = current->blocked; /* save mask */
+       current->blocked = _BLOCK_ALL;
+       if ((result = ftape_mmap(vma)) >= 0) {
+#ifndef MSYNC_BUG_WAS_FIXED
+               static struct vm_operations_struct dummy = { NULL, };
+               vma->vm_ops = &dummy;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,45)
+               vma->vm_dentry = dget(filep->f_dentry);
+#else
+               vma_set_inode (vma, ino);
+               inode_inc_count (ino);
+#endif
+       }
+       current->blocked = old_sigmask; /* restore mask */
+       TRACE_EXIT result;
+}
+
+/*      Read from floppy tape device
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+static ssize_t zft_read(struct file *fp, char *buff,
+                       size_t req_len, loff_t *ppos)
+#elif LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long zft_read(struct inode *ino, struct file *fp, char *buff,
+                    unsigned long req_len)
+#else
+static int  zft_read(struct inode *ino, struct file *fp, char *buff,
+                    int req_len)
+#endif
+{
+       int result = -EIO;
+       int old_sigmask;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+       struct inode *ino = fp->f_dentry->d_inode;
+#endif
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_data_flow, "called with count: %ld", (unsigned long)req_len);
+       if (!busy_flag || MINOR(ino->i_rdev) != zft_unit || ft_failure) {
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "failed: not busy, failure or wrong unit");
+       }
+       old_sigmask = current->blocked; /* save mask */
+       current->blocked = _BLOCK_ALL;
+       result = _zft_read(buff, req_len);
+       current->blocked = old_sigmask; /* restore mask */
+       TRACE(ft_t_data_flow, "return with count: %d", result);
+       TRACE_EXIT result;
+}
+
+/*      Write to tape device
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+static ssize_t zft_write(struct file *fp, const char *buff,
+                        size_t req_len, loff_t *ppos)
+#elif LINUX_VERSION_CODE >= KERNEL_VER(2,1,0)
+static long zft_write(struct inode *ino, struct file *fp, const char *buff,
+                     unsigned long req_len)
+#elif LINUX_VERSION_CODE >= KERNEL_VER(1,3,0)
+static int  zft_write(struct inode *ino, struct file *fp, const char *buff,
+                     int req_len)
+#else
+static int  zft_write(struct inode *ino, struct file *fp, char *buff,
+                     int req_len)
+#endif
+{
+       int result = -EIO;
+       int old_sigmask;
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,60)
+       struct inode *ino = fp->f_dentry->d_inode;
+#endif
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_flow, "called with count: %ld", (unsigned long)req_len);
+       if (!busy_flag || MINOR(ino->i_rdev) != zft_unit || ft_failure) {
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "failed: not busy, failure or wrong unit");
+       }
+       old_sigmask = current->blocked; /* save mask */
+       current->blocked = _BLOCK_ALL;
+       result = _zft_write(buff, req_len);
+       current->blocked = old_sigmask; /* restore mask */
+       TRACE(ft_t_data_flow, "return with count: %d", result);
+       TRACE_EXIT result;
+}
+
+/*                    END OF VFS INTERFACE 
+ *          
+ *****************************************************************************/
+
+/*  driver/module initialization
+ */
+
+/*  the compression module has to call this function to hook into the zftape 
+ *  code
+ */
+int zft_cmpr_register(struct zft_cmpr_ops *new_ops)
+{
+       TRACE_FUN(ft_t_flow);
+       
+       if (zft_cmpr_ops != NULL) {
+               TRACE_EXIT -EBUSY;
+       } else {
+               zft_cmpr_ops = new_ops;
+               TRACE_EXIT 0;
+       }
+}
+
+struct zft_cmpr_ops *zft_cmpr_unregister(void)
+{
+       struct zft_cmpr_ops *old_ops = zft_cmpr_ops;
+       TRACE_FUN(ft_t_flow);
+
+       zft_cmpr_ops = NULL;
+       TRACE_EXIT old_ops;
+}
+
+/*  lock the zft-compressor() module.
+ */
+int zft_cmpr_lock(int try_to_load)
+{
+       if (zft_cmpr_ops == NULL) {
+#ifdef CONFIG_KERNELD
+               if (try_to_load) {
+                       request_module("zft-compressor");
+                       if (zft_cmpr_ops == NULL) {
+                               return -ENOSYS;
+                       }
+               } else {
+                       return -ENOSYS;
+               }
+#else
+               return -ENOSYS;
+#endif
+       }
+       (*zft_cmpr_ops->lock)();
+       return 0;
+}
+
+#ifdef CONFIG_ZFT_COMPRESSOR
+extern int zft_compressor_init(void);
+#endif
+
+/*  Called by modules package when installing the driver or by kernel
+ *  during the initialization phase
+ */
+__initfunc(int zft_init(void))
+{
+       TRACE_FUN(ft_t_flow);
+
+#ifdef MODULE
+       printk(KERN_INFO ZFTAPE_VERSION "\n");
+        if (TRACE_LEVEL >= ft_t_info) {
+               printk(
+KERN_INFO
+"(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n"
+KERN_INFO
+"vfs interface for ftape floppy tape driver.\n"
+KERN_INFO
+"Support for QIC-113 compatible volume table, dynamic memory allocation\n"
+KERN_INFO
+"and builtin compression (lzrw3 algorithm).\n"
+KERN_INFO
+"Compiled for Linux version %s"
+#ifdef MODVERSIONS
+                      " with versioned symbols"
+#endif
+                      "\n", UTS_RELEASE);
+        }
+#else /* !MODULE */
+       /* print a short no-nonsense boot message */
+       printk(KERN_INFO ZFTAPE_VERSION " for Linux " UTS_RELEASE "\n");
+#endif /* MODULE */
+       TRACE(ft_t_info, "zft_init @ 0x%p", zft_init);
+       TRACE(ft_t_info,
+             "installing zftape VFS interface for ftape driver ...");
+       TRACE_CATCH(register_chrdev(QIC117_TAPE_MAJOR, "zft", &zft_cdev),);
+#if LINUX_VERSION_CODE >= KERNEL_VER(1,2,0) 
+# if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+       register_symtab(&zft_symbol_table); /* add global zftape symbols */
+# endif
+#endif
+#ifdef CONFIG_ZFT_COMPRESSOR
+       (void)zft_compressor_init();
+#endif
+       zft_status = ftape_get_status(); /*  fetch global data of ftape 
+                                         *  hardware driver 
+                                         */
+       TRACE_EXIT 0;
+}
+
+
+#ifdef MODULE
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) && defined(MODULE)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+/* Called by modules package before trying to unload the module
+ */
+static int can_unload(void)
+{
+       return (zft_dirty() || busy_flag) ? -EBUSY : 0;
+}
+#endif
+/* Called by modules package when installing the driver
+ */
+int init_module(void)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+       if (!mod_member_present(&__this_module, can_unload)) {
+               return -EBUSY;
+       }
+       __this_module.can_unload = can_unload;
+#endif
+       return zft_init();
+}
+
+/* Called by modules package when removing the driver 
+ */
+void cleanup_module(void)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) {
+               TRACE(ft_t_warn, "failed");
+       } else {
+               TRACE(ft_t_info, "successful");
+       }
+       zft_uninit_mem(); /* release remaining memory, if any */
+        printk(KERN_INFO "zftape successfully unloaded.\n");
+       TRACE_EXIT;
+}
+
+#endif /* MODULE */
diff --git a/drivers/char/ftape/zftape/zftape-init.h b/drivers/char/ftape/zftape/zftape-init.h
new file mode 100644 (file)
index 0000000..39d83f7
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef _ZFTAPE_INIT_H
+#define _ZFTAPE_INIT_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-init.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:05 $
+ *
+ * This file contains definitions and macro for the vfs 
+ * interface defined by zftape
+ *
+ */
+
+#include <linux/ftape-header-segment.h>
+
+#include "../lowlevel/ftape-tracing.h"
+#include "../lowlevel/ftape-ctl.h"
+#include "../lowlevel/ftape-read.h"
+#include "../lowlevel/ftape-write.h"
+#include "../lowlevel/ftape-bsm.h"
+#include "../lowlevel/ftape-io.h"
+#include "../lowlevel/ftape-buffer.h"
+#include "../lowlevel/ftape-format.h"
+
+#include "../zftape/zftape-rw.h"
+
+#ifdef MODULE
+#define ftape_status (*zft_status)
+#endif
+
+extern const  ftape_info *zft_status; /* needed for zftape-vtbl.h */
+
+#include "../zftape/zftape-vtbl.h"
+
+struct zft_cmpr_ops {
+       int (*write)(int *write_cnt,
+                    __u8 *dst_buf, const int seg_sz,
+                    const __u8 *src_buf, const int req_len, 
+                    const zft_position *pos, const zft_volinfo *volume);
+       int (*read)(int *read_cnt,
+                   __u8 *dst_buf, const int req_len,
+                   const __u8 *src_buf, const int seg_sz,
+                   const zft_position *pos, const zft_volinfo *volume);
+       int (*seek)(unsigned int new_block_pos,
+                   zft_position *pos, const zft_volinfo *volume,
+                   __u8 *buffer);
+       void (*lock)   (void);
+       void (*reset)  (void);
+       void (*cleanup)(void);
+};
+
+extern struct zft_cmpr_ops *zft_cmpr_ops;
+/* zftape-init.c defined global functions.
+ */
+extern int                  zft_cmpr_register(struct zft_cmpr_ops *new_ops);
+extern struct zft_cmpr_ops *zft_cmpr_unregister(void);
+extern int                  zft_cmpr_lock(int try_to_load);
+
+#ifdef MODULE
+
+asmlinkage extern int  init_module(void);
+asmlinkage extern void cleanup_module(void);
+
+#endif
+
+#endif
+
+
diff --git a/drivers/char/ftape/zftape/zftape-read.c b/drivers/char/ftape/zftape/zftape-read.c
new file mode 100644 (file)
index 0000000..c7d319f
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ *      Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-read.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:06 $
+ *
+ *      This file contains the high level reading code
+ *      for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/*      Global vars.
+ */
+int zft_just_before_eof = 0;
+
+/*      Local vars.
+ */
+static int buf_len_rd = 0;
+
+void zft_zap_read_buffers(void)
+{
+       buf_len_rd = 0;
+}
+
+int zft_read_header_segments(void)      
+{
+       TRACE_FUN(ft_t_flow);
+
+       zft_header_read = 0;
+       TRACE_CATCH(zft_vmalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),);
+       TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),);
+       TRACE(ft_t_info, "Segments written since first format: %d",
+             (int)GET4(zft_hseg_buf, FT_SEG_CNT));
+       zft_qic113 = (ft_format_code != fmt_normal &&
+                     ft_format_code != fmt_1100ft &&
+                     ft_format_code != fmt_425ft);
+       TRACE(ft_t_info, "ft_first_data_segment: %d, ft_last_data_segment: %d", 
+             ft_first_data_segment, ft_last_data_segment);
+       zft_capacity = zft_get_capacity();
+       zft_old_ftape = zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]);
+       if (zft_old_ftape) {
+               TRACE(ft_t_info, 
+"Found old ftaped tape, emulating eof marks, entering read-only mode");
+               zft_ftape_extract_file_marks(zft_hseg_buf);
+               TRACE_CATCH(zft_fake_volume_headers(zft_eof_map, 
+                                                   zft_nr_eof_marks),);
+       } else {
+               /* the specs say that the volume table must be
+                * initialized with zeroes during formatting, so it
+                * MUST be readable, i.e. contain vaid ECC
+                * information.  
+                */
+               TRACE_CATCH(ftape_read_segment(ft_first_data_segment, 
+                                              zft_deblock_buf, 
+                                              FT_RD_SINGLE),);
+               TRACE_CATCH(zft_extract_volume_headers(zft_deblock_buf),);
+       }
+       zft_header_read = 1;
+       zft_set_flags(zft_unit);
+       zft_reset_position(&zft_pos);
+       TRACE_EXIT 0;
+}
+
+int zft_fetch_segment_fraction(const unsigned int segment, void *buffer,
+                              const ft_read_mode_t read_mode,
+                              const unsigned int start,
+                              const unsigned int size)
+{
+       int seg_sz;
+       TRACE_FUN(ft_t_flow);
+
+       if (segment == zft_deblock_segment) {
+               TRACE(ft_t_data_flow,
+                     "re-using segment %d already in deblock buffer",
+                     segment);
+               seg_sz = zft_get_seg_sz(segment);
+               if (start > seg_sz) {
+                       TRACE_ABORT(-EINVAL, ft_t_bug,
+                                   "trying to read beyond end of segment:\n"
+                                   KERN_INFO "seg_sz : %d\n"
+                                   KERN_INFO "start  : %d\n"
+                                   KERN_INFO "segment: %d",
+                                   seg_sz, start, segment);
+               }
+               if ((start + size) > seg_sz) {
+                       TRACE_EXIT seg_sz - start;
+               }
+               TRACE_EXIT size;
+       }
+       seg_sz = ftape_read_segment_fraction(segment, buffer, read_mode,
+                                            start, size);
+       TRACE(ft_t_data_flow, "segment %d, result %d", segment, seg_sz);
+       if ((int)seg_sz >= 0 && start == 0 && size == FT_SEGMENT_SIZE) {
+               /*  this implicitly assumes that we are always called with
+                *  buffer == zft_deblock_buf 
+                */
+               zft_deblock_segment = segment;
+       } else {
+               zft_deblock_segment = -1;
+       }
+       TRACE_EXIT seg_sz;
+}
+
+/*
+ * out:
+ *
+ * int *read_cnt: the number of bytes we removed from the
+ *                zft_deblock_buf (result)
+ *
+ * int *to_do   : the remaining size of the read-request. Is changed.
+ *
+ * in:
+ *
+ * char *buff      : buff is the address of the upper part of the user
+ *                   buffer, that hasn't been filled with data yet.
+ * int buf_pos_read: copy of buf_pos_rd
+ * int buf_len_read: copy of buf_len_rd
+ * char *zft_deblock_buf: ftape_zft_deblock_buf
+ *
+ * returns the amount of data actually copied to the user-buffer
+ *
+ * to_do MUST NOT SHRINK except to indicate an EOT. In this case to_do
+ * has to be set to 0. We cannot return -ENOSPC, because we return the
+ * amount of data actually * copied to the user-buffer
+ */
+static int zft_simple_read (int *read_cnt, 
+                           __u8  *dst_buf, 
+                           const int to_do, 
+                           const __u8 *src_buf, 
+                           const int seg_sz, 
+                           const zft_position *pos,
+                           const zft_volinfo *volume)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (seg_sz - pos->seg_byte_pos < to_do) {
+               *read_cnt = seg_sz - pos->seg_byte_pos;
+       } else {
+               *read_cnt = to_do;
+       }
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+       if (copy_to_user(dst_buf, 
+                        src_buf + pos->seg_byte_pos, *read_cnt) != 0) {
+               TRACE_EXIT -EFAULT;
+       }
+#else
+       TRACE_CATCH(verify_area(VERIFY_WRITE, dst_buf, *read_cnt),);
+       memcpy_tofs(dst_buf, src_buf +  pos->seg_byte_pos, *read_cnt);
+#endif
+       TRACE(ft_t_noise, "nr bytes just read: %d", *read_cnt);
+       TRACE_EXIT *read_cnt;
+}
+
+/* req_len: gets clipped due to EOT of EOF.
+ * req_clipped: is a flag indicating whether req_len was clipped or not
+ * volume: contains information on current volume (blk_sz etc.)
+ */
+static int check_read_access(int *req_len, 
+                            const zft_volinfo **volume,
+                            int *req_clipped, 
+                            const zft_position *pos)
+{
+       static __s64 remaining = 0;
+       static int eod;
+       TRACE_FUN(ft_t_flow);
+       
+       if (zft_io_state != zft_reading) {
+               if (zft_offline) { /* offline includes no_tape */
+                       TRACE_ABORT(-ENXIO, ft_t_warn,
+                                   "tape is offline or no cartridge");
+               }
+               if (!ft_formatted) {
+                       TRACE_ABORT(-EACCES,
+                                   ft_t_warn, "tape is not formatted");
+               }
+               /*  now enter defined state, read header segment if not
+                *  already done and flush write buffers
+                */
+               TRACE_CATCH(zft_def_idle_state(),);
+               zft_io_state = zft_reading;
+               if (zft_tape_at_eod(pos)) {
+                       eod = 1;
+                       TRACE_EXIT 1;
+               }
+               eod = 0;
+               *volume = zft_find_volume(pos->seg_pos);
+               /* get the space left until EOF */
+               remaining = zft_check_for_eof(*volume, pos);
+               buf_len_rd = 0;
+               TRACE(ft_t_noise, "remaining: " LL_X ", vol_no: %d",
+                     LL(remaining), (*volume)->count);
+       } else if (zft_tape_at_eod(pos)) {
+               if (++eod > 2) {
+                       TRACE_EXIT -EIO; /* st.c also returns -EIO */
+               } else {
+                       TRACE_EXIT 1;
+               }
+       }
+       if ((*req_len % (*volume)->blk_sz) != 0) {
+               /*  this message is informational only. The user gets the
+                *  proper return value
+                */
+               TRACE_ABORT(-EINVAL, ft_t_info,
+                           "req_len %d not a multiple of block size %d",
+                           *req_len, (*volume)->blk_sz);
+       }
+       /* As GNU tar doesn't accept partial read counts when the
+        * multiple volume flag is set, we make sure to return the
+        * requested amount of data. Except, of course, at the end of
+        * the tape or file mark.  
+        */
+       remaining -= *req_len;
+       if (remaining <= 0) {
+               TRACE(ft_t_noise, 
+                     "clipped request from %d to %d.", 
+                     *req_len, (int)(*req_len + remaining));
+               *req_len += remaining;
+               *req_clipped = 1;
+       } else {
+               *req_clipped = 0;
+       }
+       TRACE_EXIT 0;
+}
+
+/* this_segs_size: the current segment's size.
+ * buff: the USER-SPACE buffer provided by the calling function.
+ * req_len: how much data should be read at most.
+ * volume: contains information on current volume (blk_sz etc.)
+ */  
+static int empty_deblock_buf(__u8 *usr_buf, const int req_len,
+                            const __u8 *src_buf, const int seg_sz,
+                            zft_position *pos,
+                            const zft_volinfo *volume)
+{
+       int cnt;
+       int result = 0;
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_data_flow, "this_segs_size: %d", seg_sz);
+       if (zft_use_compression && volume->use_compression) {
+               TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),);
+               TRACE_CATCH(result= (*zft_cmpr_ops->read)(&cnt,
+                                                         usr_buf, req_len,
+                                                         src_buf, seg_sz,
+                                                         pos, volume),);
+       } else {                                  
+               TRACE_CATCH(result= zft_simple_read (&cnt,
+                                                    usr_buf, req_len,
+                                                    src_buf, seg_sz,
+                                                    pos, volume),);
+       }
+       pos->volume_pos   += result;
+        pos->tape_pos     += cnt;
+       pos->seg_byte_pos += cnt;
+       buf_len_rd        -= cnt; /* remaining bytes in buffer */
+       TRACE(ft_t_data_flow, "buf_len_rd: %d, cnt: %d", buf_len_rd, cnt);
+       if(pos->seg_byte_pos >= seg_sz) {
+               pos->seg_pos++;
+               pos->seg_byte_pos = 0;
+       }
+       TRACE(ft_t_data_flow, "bytes moved out of deblock-buffer: %d", cnt);
+       TRACE_EXIT result;
+}
+
+
+/* note: we store the segment id of the segment that is inside the
+ * deblock buffer. This spares a lot of ftape_read_segment()s when we
+ * use small block-sizes. The block-size may be 1kb (SECTOR_SIZE). In
+ * this case a MTFSR 28 maybe still inside the same segment.
+ */
+int _zft_read(char* buff, int req_len)
+{
+       int req_clipped;
+       int result     = 0;
+       int bytes_read = 0;
+       static unsigned int seg_sz = 0;
+       static const zft_volinfo *volume = NULL;
+       TRACE_FUN(ft_t_flow);
+       
+       zft_resid = req_len;
+       result = check_read_access(&req_len, &volume,
+                                  &req_clipped, &zft_pos);
+       switch(result) {
+       case 0: 
+               break; /* nothing special */
+       case 1: 
+               TRACE(ft_t_noise, "EOD reached");
+               TRACE_EXIT 0;   /* EOD */
+       default:
+               TRACE_ABORT(result, ft_t_noise,
+                           "check_read_access() failed with result %d",
+                           result);
+               TRACE_EXIT result;
+       }
+       while (req_len > 0) { 
+               /*  Allow escape from this loop on signal !
+                */
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+               /* buf_len_rd == 0 means that we need to read a new
+                * segment.
+                */
+               if (buf_len_rd == 0) {
+                       while((result = zft_fetch_segment(zft_pos.seg_pos,
+                                                         zft_deblock_buf,
+                                                         FT_RD_AHEAD)) == 0) {
+                               zft_pos.seg_pos ++;
+                               zft_pos.seg_byte_pos = 0;
+                       }
+                       if (result < 0) {
+                               zft_resid -= bytes_read;
+                               TRACE_ABORT(result, ft_t_noise,
+                                           "zft_fetch_segment(): %d",
+                                           result);
+                       }
+                       seg_sz = result;
+                       buf_len_rd = seg_sz - zft_pos.seg_byte_pos;
+               }
+               TRACE_CATCH(result = empty_deblock_buf(buff, 
+                                                      req_len,
+                                                      zft_deblock_buf, 
+                                                      seg_sz, 
+                                                      &zft_pos,
+                                                      volume),
+                           zft_resid -= bytes_read);
+               TRACE(ft_t_data_flow, "bytes just read: %d", result);
+               bytes_read += result; /* what we got so far       */
+               buff       += result; /* index in user-buffer     */
+               req_len    -= result; /* what's left from req_len */
+       } /* while (req_len  > 0) */
+       if (req_clipped) {
+               TRACE(ft_t_data_flow,
+                     "maybe partial count because of eof mark");
+               if (zft_just_before_eof && bytes_read == 0) {
+                       /* req_len was > 0, but user didn't get
+                        * anything the user has read in the eof-mark 
+                        */
+                       zft_move_past_eof(&zft_pos);
+                       ftape_abort_operation();
+               } else {
+                       /* don't skip to the next file before the user
+                        * tried to read a second time past EOF Just
+                        * mark that we are at EOF and maybe decrement
+                        * zft_seg_pos to stay in the same volume;
+                        */
+                       zft_just_before_eof = 1;
+                       zft_position_before_eof(&zft_pos, volume);
+                       TRACE(ft_t_noise, "just before eof");
+               }
+       }
+       zft_resid -= result; /* for MTSTATUS       */
+       TRACE_EXIT bytes_read;
+}
diff --git a/drivers/char/ftape/zftape/zftape-read.h b/drivers/char/ftape/zftape/zftape-read.h
new file mode 100644 (file)
index 0000000..ad2e9db
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef _ZFTAPE_READ_H
+#define _ZFTAPE_READ_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-read.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:07 $
+ *
+ *      This file contains the definitions for the read functions
+ *      for the zftape driver for Linux.
+ *
+ */
+
+#include "../lowlevel/ftape-read.h"
+
+/*      ftape-read.c defined global vars.
+ */
+extern int zft_just_before_eof;
+       
+/*      ftape-read.c defined global functions.
+ */
+extern void zft_zap_read_buffers(void);
+extern int  zft_read_header_segments(void);
+extern int  zft_fetch_segment_fraction(const unsigned int segment,
+                                      void *buffer,
+                                      const ft_read_mode_t read_mode,
+                                      const unsigned int start,
+                                      const unsigned int size);
+#define zft_fetch_segment(segment, address, read_mode)         \
+       zft_fetch_segment_fraction(segment, address, read_mode, \
+                                  0, FT_SEGMENT_SIZE)
+/*   hook for the VFS interface
+ */
+extern int  _zft_read(char* buff, int req_len);
+
+#endif /* _ZFTAPE_READ_H */
diff --git a/drivers/char/ftape/zftape/zftape-rw.c b/drivers/char/ftape/zftape/zftape-rw.c
new file mode 100644 (file)
index 0000000..d8ae9f1
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ *      Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-rw.c,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:08 $
+ *
+ *      This file contains some common code for the r/w code for
+ *      zftape.
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+#include <linux/zftape.h>
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/*      Global vars.
+ */
+
+__u8 *zft_deblock_buf = NULL;
+__u8 *zft_hseg_buf    = NULL;
+int zft_deblock_segment = -1;
+zft_status_enum zft_io_state = zft_idle;
+int zft_header_changed         = 0;
+int zft_bad_sector_map_changed = 0;
+int zft_qic113 = 0; /* conform to old specs. and old zftape */
+int zft_use_compression        = 0;
+zft_position zft_pos = {
+       -1, /* seg_pos */
+       0,  /* seg_byte_pos */
+       0,  /* tape_pos */
+       0   /* volume_pos */
+};
+unsigned int zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ;
+__s64 zft_capacity      = 0;
+
+unsigned int zft_written_segments = 0;
+int zft_label_changed = 0;
+
+/*      Local vars.
+ */
+
+unsigned int zft_get_seg_sz(unsigned int segment)
+{
+       int size;
+       TRACE_FUN(ft_t_any);
+       
+       size = FT_SEGMENT_SIZE - 
+               count_ones(ftape_get_bad_sector_entry(segment))*FT_SECTOR_SIZE;
+       if (size > 0) {
+               TRACE_EXIT (unsigned)size; 
+       } else {
+               TRACE_EXIT 0;
+       }
+}
+
+/* ftape_set_flags(). Claus-Justus Heine, 1994/1995
+ */
+void zft_set_flags(unsigned minor_unit)
+{     
+       TRACE_FUN(ft_t_flow);
+       
+       zft_use_compression = zft_qic_mode = 0;
+       switch (minor_unit & ZFT_MINOR_OP_MASK) {
+       case (ZFT_Q80_MODE | ZFT_ZIP_MODE):
+       case ZFT_ZIP_MODE:
+               zft_use_compression = 1;
+       case 0:
+       case ZFT_Q80_MODE:
+               zft_qic_mode = 1;
+               if (zft_mt_compression) { /* override the default */
+                       zft_use_compression = 1;
+               }
+               break;
+       case ZFT_RAW_MODE:
+               TRACE(ft_t_noise, "switching to raw mode");
+               break;
+       default:
+               TRACE(ft_t_warn, "Warning:\n"
+                     KERN_INFO "Wrong combination of minor device bits.\n"
+                     KERN_INFO "Switching to raw read-only mode.");
+               zft_write_protected = 1;
+               break;
+       }
+       TRACE_EXIT;
+}
+
+/* computes the segment and byte offset inside the segment
+ * corresponding to tape_pos.
+ *
+ * tape_pos gives the offset in bytes from the beginning of the
+ * ft_first_data_segment *seg_byte_pos is the offset in the current
+ * segment in bytes
+ *
+ * Of, if this routine was called often one should cache the last data
+ * pos it was called with, but actually this is only needed in
+ * ftape_seek_block(), that is, almost never.
+ */
+int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos)
+{
+       int segment;
+       int seg_sz;
+       TRACE_FUN(ft_t_flow);
+       
+       if (tape_pos == 0) {
+               *seg_byte_pos = 0;
+               segment = ft_first_data_segment;
+       } else {
+               seg_sz = 0;
+               
+               for (segment = ft_first_data_segment; 
+                    ((tape_pos > 0) && (segment <= ft_last_data_segment));
+                    segment++) {
+                       seg_sz = zft_get_seg_sz(segment); 
+                       tape_pos -= seg_sz;
+               }
+               if(tape_pos >= 0) {
+                       /* the case tape_pos > != 0 means that the
+                        * argument tape_pos lies beyond the EOT.
+                        */
+                       *seg_byte_pos= 0;
+               } else { /* tape_pos < 0 */
+                       segment--;
+                       *seg_byte_pos= tape_pos + seg_sz;
+               }
+       }
+       TRACE_EXIT(segment);
+}
+
+/* ftape_calc_tape_pos().
+ *
+ * computes the offset in bytes from the beginning of the
+ * ft_first_data_segment inverse to ftape_calc_seg_byte_coord
+ *
+ * We should do some caching. But how:
+ *
+ * Each time the header segments are read in, this routine is called
+ * with ft_tracks_per_tape*segments_per_track argumnet. So this should be
+ * the time to reset the cache.
+ *
+ * Also, it might be in the future that the bad sector map gets
+ * changed.  -> reset the cache
+ */
+static int seg_pos    = 0;
+static __s64 tape_pos = 0;
+
+__s64 zft_get_capacity(void)
+{
+       seg_pos  = ft_first_data_segment;
+       tape_pos = 0;
+
+       while (seg_pos <= ft_last_data_segment) {
+               tape_pos += zft_get_seg_sz(seg_pos ++);
+       }
+       return tape_pos;
+}
+
+__s64 zft_calc_tape_pos(int segment)
+{
+       int d1, d2, d3;
+       TRACE_FUN(ft_t_any);
+       
+       if (segment > ft_last_data_segment) {
+               TRACE_EXIT zft_capacity;
+       }
+       if (segment < ft_first_data_segment) {
+               TRACE_EXIT 0;
+       }
+       d2 = segment - seg_pos;
+       if (-d2 > 10) {
+               d1 = segment - ft_first_data_segment;
+               if (-d2 > d1) {
+                       tape_pos = 0;
+                       seg_pos = ft_first_data_segment;
+                       d2 = d1;
+               }
+       }
+       if (d2 > 10) {
+               d3 = ft_last_data_segment - segment;
+               if (d2 > d3) {
+                       tape_pos = zft_capacity;
+                       seg_pos  = ft_last_data_segment + 1;
+                       d2 = -d3;
+               }
+       }               
+       if (d2 > 0) {
+               while (seg_pos < segment) {
+                       tape_pos +=  zft_get_seg_sz(seg_pos++);
+               }
+       } else {
+               while (seg_pos > segment) {
+                       tape_pos -=  zft_get_seg_sz(--seg_pos);
+               }
+       }
+       TRACE(ft_t_noise, "new cached pos: %d", seg_pos);
+
+       TRACE_EXIT tape_pos;
+}
+
+/* copy Z-label string to buffer, keeps track of the correct offset in
+ * `buffer' 
+ */
+void zft_update_label(__u8 *buffer)
+{ 
+       TRACE_FUN(ft_t_flow);
+       
+       if (strncmp(&buffer[FT_LABEL], ZFTAPE_LABEL, 
+                   sizeof(ZFTAPE_LABEL)-1) != 0) {
+               TRACE(ft_t_info, "updating label from \"%s\" to \"%s\"",
+                     &buffer[FT_LABEL], ZFTAPE_LABEL);
+               strcpy(&buffer[FT_LABEL], ZFTAPE_LABEL);
+               memset(&buffer[FT_LABEL] + sizeof(ZFTAPE_LABEL) - 1, ' ', 
+                      FT_LABEL_SZ - sizeof(ZFTAPE_LABEL + 1));
+               PUT4(buffer, FT_LABEL_DATE, 0);
+               zft_label_changed = zft_header_changed = 1; /* changed */
+       }
+       TRACE_EXIT;
+}
+
+int zft_verify_write_segments(unsigned int segment, 
+                             __u8 *data, size_t size,
+                             __u8 *buffer)
+{
+       int result;
+       __u8 *write_buf;
+       __u8 *src_buf;
+       int single;
+       int seg_pos;
+       int seg_sz;
+       int remaining;
+       ft_write_mode_t write_mode;
+       TRACE_FUN(ft_t_flow);
+
+       seg_pos   = segment;
+       seg_sz    = zft_get_seg_sz(seg_pos);
+       src_buf   = data;
+       single    = size <= seg_sz;
+       remaining = size;
+       do {
+               TRACE(ft_t_noise, "\n"
+                     KERN_INFO "remaining: %d\n"
+                     KERN_INFO "seg_sz   : %d\n"
+                     KERN_INFO "segment  : %d",
+                     remaining, seg_sz, seg_pos);
+               if (remaining == seg_sz) {
+                       write_buf = src_buf;
+                       write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI;
+                       remaining = 0;
+               } else if (remaining > seg_sz) {
+                       write_buf = src_buf;
+                       write_mode = FT_WR_ASYNC; /* don't start tape */
+                       remaining -= seg_sz;
+               } else { /* remaining < seg_sz */
+                       write_buf = buffer;
+                       memcpy(write_buf, src_buf, remaining);
+                       memset(&write_buf[remaining],'\0',seg_sz-remaining);
+                       write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI;
+                       remaining = 0;
+               }
+               if ((result = ftape_write_segment(seg_pos, 
+                                                 write_buf, 
+                                                 write_mode)) != seg_sz) {
+                       TRACE(ft_t_err, "Error: "
+                             "Couldn't write segment %d", seg_pos);
+                       TRACE_EXIT result < 0 ? result : -EIO; /* bail out */
+               }
+               zft_written_segments ++;
+               seg_sz = zft_get_seg_sz(++seg_pos);
+               src_buf += result;
+       } while (remaining > 0);
+       if (ftape_get_status()->fti_state == writing) {
+               TRACE_CATCH(ftape_loop_until_writes_done(),);
+               TRACE_CATCH(ftape_abort_operation(),);
+               zft_prevent_flush();
+       }
+       seg_pos = segment;
+       src_buf = data;
+       remaining = size;
+       do {
+               TRACE_CATCH(result = ftape_read_segment(seg_pos, buffer, 
+                                                       single ? FT_RD_SINGLE
+                                                       : FT_RD_AHEAD),);
+               if (memcmp(src_buf, buffer, 
+                          remaining > result ? result : remaining) != 0) {
+                       TRACE_ABORT(-EIO, ft_t_err,
+                                   "Failed to verify written segment %d",
+                                   seg_pos);
+               }
+               remaining -= result;
+               TRACE(ft_t_noise, "verify successful:\n"
+                     KERN_INFO "segment  : %d\n"
+                     KERN_INFO "segsize  : %d\n"
+                     KERN_INFO "remaining: %d",
+                     seg_pos, result, remaining);
+               src_buf   += seg_sz;
+               seg_pos++;
+       } while (remaining > 0);
+       TRACE_EXIT size;
+}
+
+
+/* zft_erase().  implemented compression-handling
+ *
+ * calculate the first data-segment when using/not using compression.
+ *
+ * update header-segment and compression-map-segment.
+ */
+int zft_erase(void)
+{
+       int result = 0;
+       TRACE_FUN(ft_t_flow);
+       
+       if (!zft_header_read) {
+               TRACE_CATCH(zft_vmalloc_once((void **)&zft_hseg_buf,
+                                            FT_SEGMENT_SIZE),);
+               /* no need to read the vtbl and compression map */
+               TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),);
+               if ((zft_old_ftape = 
+                    zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]))) {
+                       zft_ftape_extract_file_marks(zft_hseg_buf);
+               }
+               TRACE(ft_t_noise,
+                     "ft_first_data_segment: %d, ft_last_data_segment: %d", 
+                     ft_first_data_segment, ft_last_data_segment);
+               zft_qic113 = (ft_format_code != fmt_normal &&
+                             ft_format_code != fmt_1100ft &&
+                             ft_format_code != fmt_425ft);
+       }
+       if (zft_old_ftape) {
+               zft_clear_ftape_file_marks();
+               zft_old_ftape = 0; /* no longer old ftape */
+       }
+       PUT2(zft_hseg_buf, FT_CMAP_START, 0);
+       zft_volume_table_changed = 1;
+       zft_capacity = zft_get_capacity();
+       zft_init_vtbl();
+       /* the rest must be done in ftape_update_header_segments 
+        */
+       zft_header_read = 1;
+       zft_header_changed = 1; /* force update of timestamp */
+       result = zft_update_header_segments();
+
+       ftape_abort_operation();
+
+       zft_reset_position(&zft_pos);
+       zft_set_flags (zft_unit);
+       TRACE_EXIT result;
+}
+
+unsigned int zft_get_time(void) 
+{
+       unsigned int date = FT_TIME_STAMP(2097, 11, 30, 23, 59, 59); /* fun */
+       return date;
+}
diff --git a/drivers/char/ftape/zftape/zftape-rw.h b/drivers/char/ftape/zftape/zftape-rw.h
new file mode 100644 (file)
index 0000000..a862286
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef _ZFTAPE_RW_H
+#define _ZFTAPE_RW_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-rw.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:09 $
+ *
+ *      This file contains the definitions for the read and write
+ *      functions for the QIC-117 floppy-tape driver for Linux.
+ *
+ */
+
+#include "../zftape/zftape-buffers.h"
+
+#define SEGMENTS_PER_TAPE  (ft_segments_per_track * ft_tracks_per_tape)
+
+/*  QIC-113 Rev. G says that `a maximum of 63488 raw bytes may be
+ *  compressed into a single frame'.
+ *  Maybe we should stick to 32kb to make it more `beautiful'
+ */
+#define ZFT_MAX_BLK_SZ           (62*1024) /* bytes */
+#if !defined(CONFIG_ZFT_DFLT_BLK_SZ)
+# define CONFIG_ZFT_DFLT_BLK_SZ   (10*1024) /* bytes, default of gnu tar */
+#elif CONFIG_ZFT_DFLT_BLK_SZ == 0
+# undef  CONFIG_ZFT_DFLT_BLK_SZ
+# define CONFIG_ZFT_DFLT_BLK_SZ 1
+#elif (CONFIG_ZFT_DFLT_BLK_SZ % 1024) != 0
+# error CONFIG_ZFT_DFLT_BLK_SZ must be 1 or a multiple of 1024
+#endif
+/* The *optional* compression routines need some overhead per tape
+ *  block for their purposes. Instead of asking the actual compression
+ *  implementation how much it needs, we restrict this overhead to be
+ *  maximal of ZFT_CMPT_OVERHEAD size. We need this for EOT
+ *  conditions. The tape is assumed to be logical at EOT when the
+ *  distance from the physical EOT is less than 
+ *  one tape block + ZFT_CMPR_OVERHEAD 
+ */
+#define ZFT_CMPR_OVERHEAD 16        /* bytes */
+
+typedef enum
+{ 
+       zft_idle = 0,
+       zft_reading,
+       zft_writing,
+} zft_status_enum;
+
+typedef struct               /*  all values measured in bytes */
+{
+       int   seg_pos;       /*  segment currently positioned at */
+       int   seg_byte_pos;  /*  offset in current segment */ 
+       __s64 tape_pos;      /*  real offset from BOT */
+       __s64 volume_pos;    /*  pos. in uncompressed data stream in
+                             *  current volume 
+                             */
+} zft_position; 
+
+extern zft_position zft_pos;
+extern __u8 *zft_deblock_buf;
+extern __u8 *zft_hseg_buf;
+extern int zft_deblock_segment;
+extern zft_status_enum zft_io_state;
+extern int zft_header_changed;
+extern int zft_bad_sector_map_changed;
+extern int zft_qic113; /* conform to old specs. and old zftape */
+extern int zft_use_compression;
+extern unsigned int zft_blk_sz;
+extern __s64 zft_capacity;
+extern unsigned int zft_written_segments;
+extern int zft_label_changed;
+
+/*  zftape-rw.c exported functions
+ */
+extern unsigned int zft_get_seg_sz(unsigned int segment);
+extern void  zft_set_flags(unsigned int minor_unit);
+extern int   zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos);
+extern __s64 zft_calc_tape_pos(int segment);
+extern __s64 zft_get_capacity(void);
+extern void  zft_update_label(__u8 *buffer);
+extern int   zft_erase(void);
+extern int   zft_verify_write_segments(unsigned int segment, 
+                                      __u8 *data, size_t size, __u8 *buffer);
+extern unsigned int zft_get_time(void);
+#endif /* _ZFTAPE_RW_H */
+
diff --git a/drivers/char/ftape/zftape/zftape-vtbl.c b/drivers/char/ftape/zftape/zftape-vtbl.c
new file mode 100644 (file)
index 0000000..cf0ef23
--- /dev/null
@@ -0,0 +1,758 @@
+/*
+ *      Copyright (c) 1995-1997 Claus-Justus Heine 
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.c,v $
+ * $Revision: 1.7.6.1 $
+ * $Date: 1997/11/24 13:48:31 $
+ *
+ *      This file defines a volume table as defined in various QIC
+ *      standards.
+ * 
+ *      This is a minimal implementation, just allowing ordinary DOS
+ *      :( prgrams to identify the cartridge as used.
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/segment.h>
+
+#include <linux/zftape.h>
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+#define ZFT_CMAP_HACK /* leave this defined to hide the compression map */
+
+/*
+ *  global variables 
+ */
+int zft_qic_mode   = 1; /* use the vtbl */
+int zft_old_ftape  = 0; /* prevents old ftaped tapes to be overwritten */
+int zft_volume_table_changed = 0; /* for write_header_segments() */
+
+/*
+ *  private variables (only exported for inline functions)
+ */
+LIST_HEAD(zft_vtbl);
+
+/*  We could also allocate these dynamically when extracting the volume table
+ *  sizeof(zft_volinfo) is about 32 or something close to that
+ */
+static zft_volinfo  tape_vtbl = { {NULL, NULL}, 0, };
+static zft_volinfo  eot_vtbl  = { {NULL, NULL}, 0, };
+static zft_volinfo *cur_vtbl = NULL;
+
+inline void zft_new_vtbl_entry(void)
+{
+       struct list_head *tmp = &zft_last_vtbl->node;
+       zft_volinfo *new = zft_kmalloc(sizeof(zft_volinfo));
+
+       list_add(&new->node, tmp);
+       new->count = zft_eom_vtbl->count ++;
+}
+
+void zft_free_vtbl(void)
+{
+       for (;;) {
+               struct list_head *tmp = zft_vtbl.prev;
+               zft_volinfo *vtbl;
+
+               if (tmp == &zft_vtbl)
+                       break;
+               list_del(tmp);
+               vtbl = list_entry(tmp, zft_volinfo, node);
+               zft_kfree(vtbl, sizeof(zft_volinfo));
+       }
+       INIT_LIST_HEAD(&zft_vtbl);
+       cur_vtbl = NULL;
+}
+
+/*  initialize vtbl, called by ftape_new_cartridge()
+ */
+void zft_init_vtbl(void)
+{ 
+       zft_volinfo *new;
+
+       zft_free_vtbl();
+       
+       /*  Create the two dummy vtbl entries
+        */
+       new = zft_kmalloc(sizeof(zft_volinfo));
+       list_add(&new->node, &zft_vtbl);
+       new = zft_kmalloc(sizeof(zft_volinfo));
+       list_add(&new->node, &zft_vtbl);
+       zft_head_vtbl->end_seg   = ft_first_data_segment;
+       zft_head_vtbl->blk_sz    = zft_blk_sz;
+       zft_head_vtbl->count     = -1;
+       zft_eom_vtbl->start_seg  = ft_first_data_segment + 1;
+       zft_eom_vtbl->end_seg    = ft_last_data_segment + 1;
+       zft_eom_vtbl->blk_sz     = zft_blk_sz;
+       zft_eom_vtbl->count      = 0;
+
+       /*  Reset the pointer for zft_find_volume()
+        */
+       cur_vtbl = zft_eom_vtbl;
+
+       /* initialize the dummy vtbl entries for zft_qic_mode == 0
+        */
+       eot_vtbl.start_seg       = ft_last_data_segment + 1;
+       eot_vtbl.end_seg         = ft_last_data_segment + 1;
+       eot_vtbl.blk_sz          = zft_blk_sz;
+       eot_vtbl.count           = -1;
+       tape_vtbl.start_seg = ft_first_data_segment;
+       tape_vtbl.end_seg   = ft_last_data_segment;
+       tape_vtbl.blk_sz    = zft_blk_sz;
+       tape_vtbl.size      = zft_capacity;
+       tape_vtbl.count     = 0;
+}
+
+/* check for a valid VTBL signature. 
+ */
+static int vtbl_signature_valid(__u8 signature[4])
+{
+       const char *vtbl_ids[] = VTBL_IDS; /* valid signatures */
+       int j;
+       
+       for (j = 0; 
+            (j < NR_ITEMS(vtbl_ids)) && (memcmp(signature, vtbl_ids[j], 4) != 0);
+            j++);
+       return j < NR_ITEMS(vtbl_ids);
+}
+
+/* We used to store the block-size of the volume in the volume-label,
+ * using the keyword "blocksize". The blocksize written to the
+ * volume-label is in bytes.
+ *
+ * We use this now only for compatability with old zftape version. We
+ * store the blocksize directly as binary number in the vendor
+ * extension part of the volume entry.
+ */
+static int check_volume_label(const char *label, int *blk_sz)
+{ 
+       int valid_format;
+       char *blocksize;
+       TRACE_FUN(ft_t_flow);
+       
+       TRACE(ft_t_noise, "called with \"%s\" / \"%s\"", label, ZFT_VOL_NAME);
+       if (strncmp(label, ZFT_VOL_NAME, strlen(ZFT_VOL_NAME)) != 0) {
+               *blk_sz = 1; /* smallest block size that we allow */
+               valid_format = 0;
+       } else {
+               TRACE(ft_t_noise, "got old style zftape vtbl entry");
+               /* get the default blocksize */
+               /* use the kernel strstr()   */
+               blocksize= strstr(label, " blocksize ");
+               if (blocksize) {
+                       blocksize += strlen(" blocksize ");
+                       for(*blk_sz= 0; 
+                           *blocksize >= '0' && *blocksize <= '9'; 
+                           blocksize++) {
+                               *blk_sz *= 10;
+                               *blk_sz += *blocksize - '0';
+                       }
+                       if (*blk_sz > ZFT_MAX_BLK_SZ) {
+                               *blk_sz= 1;
+                               valid_format= 0;
+                       } else {
+                               valid_format = 1;
+                       }
+               } else {
+                       *blk_sz= 1;
+                       valid_format= 0;
+               }
+       }
+       TRACE_EXIT valid_format;
+}
+
+/*   check for a zftape volume
+ */
+static int check_volume(__u8 *entry, zft_volinfo *volume)
+{
+       TRACE_FUN(ft_t_flow);
+       
+       if(strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG,
+                  strlen(ZFTAPE_SIG)) == 0) {
+               TRACE(ft_t_noise, "got new style zftape vtbl entry");
+               volume->blk_sz = GET2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ);
+               volume->qic113 = entry[VTBL_EXT+EXT_ZFTAPE_QIC113];
+               TRACE_EXIT 1;
+       } else {
+               TRACE_EXIT check_volume_label(&entry[VTBL_DESC], &volume->blk_sz);
+       }
+}
+
+
+/* create zftape specific vtbl entry, the volume bounds are inserted
+ * in the calling function, zft_create_volume_headers()
+ */
+static void create_zft_volume(__u8 *entry, zft_volinfo *vtbl)
+{
+       TRACE_FUN(ft_t_flow);
+
+       memset(entry, 0, VTBL_SIZE);
+       memcpy(&entry[VTBL_SIG], VTBL_ID, 4);
+       sprintf(&entry[VTBL_DESC], ZFT_VOL_NAME" %03d", vtbl->count);
+       entry[VTBL_FLAGS] = (VTBL_FL_NOT_VERIFIED | VTBL_FL_SEG_SPANNING);
+       entry[VTBL_M_NO] = 1; /* multi_cartridge_count */
+       strcpy(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG);
+       PUT2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ, vtbl->blk_sz);
+       if (zft_qic113) {
+               PUT8(entry, VTBL_DATA_SIZE, vtbl->size);
+               entry[VTBL_CMPR] = VTBL_CMPR_UNREG; 
+               if (vtbl->use_compression) { /* use compression: */
+                       entry[VTBL_CMPR] |= VTBL_CMPR_USED;
+               }
+               entry[VTBL_EXT+EXT_ZFTAPE_QIC113] = 1;
+       } else {
+               PUT4(entry, VTBL_DATA_SIZE, vtbl->size);
+               entry[VTBL_K_CMPR] = VTBL_CMPR_UNREG; 
+               if (vtbl->use_compression) { /* use compression: */
+                       entry[VTBL_K_CMPR] |= VTBL_CMPR_USED;
+               }
+       }
+       if (ft_format_code == fmt_big) {
+               /* SCSI like vtbl, store the number of used
+                * segments as 4 byte value 
+                */
+               PUT4(entry, VTBL_SCSI_SEGS, vtbl->end_seg-vtbl->start_seg + 1);
+       } else {
+               /* normal, QIC-80MC like vtbl 
+                */
+               PUT2(entry, VTBL_START, vtbl->start_seg);
+               PUT2(entry, VTBL_END, vtbl->end_seg);
+       }
+       TRACE_EXIT;
+}
+
+/* this one creates the volume headers for each volume. It is assumed
+ * that buffer already contains the old volume-table, so that vtbl
+ * entries without the zft_volume flag set can savely be ignored.
+ */
+void zft_create_volume_headers(__u8 *buffer)
+{   
+       __u8 *entry;
+       struct list_head *tmp;
+       zft_volinfo *vtbl;
+       TRACE_FUN(ft_t_flow);
+       
+#ifdef ZFT_CMAP_HACK
+       if((strncmp(&buffer[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG,
+                   strlen(ZFTAPE_SIG)) == 0) && 
+          buffer[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) {
+               TRACE(ft_t_noise, "deleting cmap volume");
+               memmove(buffer, buffer + VTBL_SIZE,
+                       FT_SEGMENT_SIZE - VTBL_SIZE);
+       }
+#endif
+       entry = buffer;
+       for (tmp = zft_head_vtbl->node.next;
+            tmp != &zft_eom_vtbl->node;
+            tmp = tmp->next) {
+               vtbl = list_entry(tmp, zft_volinfo, node);
+               /* we now fill in the values only for newly created volumes.
+                */
+               if (vtbl->new_volume) {
+                       create_zft_volume(entry, vtbl);
+                       vtbl->new_volume = 0; /* clear the flag */
+               }
+               
+               DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], vtbl);
+               entry += VTBL_SIZE;
+       }
+       memset(entry, 0, FT_SEGMENT_SIZE - zft_eom_vtbl->count * VTBL_SIZE);
+       TRACE_EXIT;
+}
+
+/*  write volume table to tape. Calls zft_create_volume_headers()
+ */
+int zft_update_volume_table(unsigned int segment)
+{
+       int result = 0;
+       __u8 *verify_buf = NULL;
+       TRACE_FUN(ft_t_flow);
+
+       TRACE_CATCH(result = ftape_read_segment(ft_first_data_segment, 
+                                               zft_deblock_buf,
+                                               FT_RD_SINGLE),);
+       zft_create_volume_headers(zft_deblock_buf);
+       TRACE(ft_t_noise, "writing volume table segment %d", segment);
+       if (zft_vmalloc_once(&verify_buf, FT_SEGMENT_SIZE) == 0) {
+               TRACE_CATCH(zft_verify_write_segments(segment, 
+                                                     zft_deblock_buf, result,
+                                                     verify_buf),
+                           zft_vfree(&verify_buf, FT_SEGMENT_SIZE));
+               zft_vfree(&verify_buf, FT_SEGMENT_SIZE);
+       } else {
+               TRACE_CATCH(ftape_write_segment(segment, zft_deblock_buf, 
+                                               FT_WR_SINGLE),);
+       }
+       TRACE_EXIT 0;
+}
+
+/* non zftape volumes are handled in raw mode. Thus we need to
+ * calculate the raw amount of data contained in those segments.  
+ */
+static void extract_alien_volume(__u8 *entry, zft_volinfo *vtbl)
+{
+       TRACE_FUN(ft_t_flow);
+
+       vtbl->size  = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) -
+                      zft_calc_tape_pos(zft_last_vtbl->start_seg));
+       vtbl->use_compression = 0;
+       vtbl->qic113 = zft_qic113;
+       if (vtbl->qic113) {
+               TRACE(ft_t_noise, 
+                     "Fake alien volume's size from " LL_X " to " LL_X, 
+                     LL(GET8(entry, VTBL_DATA_SIZE)), LL(vtbl->size));
+       } else {
+               TRACE(ft_t_noise,
+                     "Fake alien volume's size from %d to " LL_X, 
+                     (int)GET4(entry, VTBL_DATA_SIZE), LL(vtbl->size));
+       }
+       TRACE_EXIT;
+}
+
+
+/* extract an zftape specific volume
+ */
+static void extract_zft_volume(__u8 *entry, zft_volinfo *vtbl)
+{
+       TRACE_FUN(ft_t_flow);
+
+       if (vtbl->qic113) {
+               vtbl->size = GET8(entry, VTBL_DATA_SIZE);
+               vtbl->use_compression = 
+                       (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0; 
+       } else {
+               vtbl->size = GET4(entry, VTBL_DATA_SIZE);
+               if (entry[VTBL_K_CMPR] & VTBL_CMPR_UNREG) {
+                       vtbl->use_compression = 
+                               (entry[VTBL_K_CMPR] & VTBL_CMPR_USED) != 0;
+               } else if (entry[VTBL_CMPR] & VTBL_CMPR_UNREG) {
+                       vtbl->use_compression = 
+                               (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0; 
+               } else {
+                       TRACE(ft_t_warn, "Geeh! There is something wrong:\n"
+                             KERN_INFO "QIC compression (Rev = K): %x\n"
+                             KERN_INFO "QIC compression (Rev > K): %x",
+                             entry[VTBL_K_CMPR], entry[VTBL_CMPR]);
+               }
+       }
+       TRACE_EXIT;
+}
+
+/* extract the volume table from buffer. "buffer" must already contain
+ * the vtbl-segment 
+ */
+int zft_extract_volume_headers(__u8 *buffer)
+{                            
+        __u8 *entry;
+       TRACE_FUN(ft_t_flow);
+       
+       zft_init_vtbl();
+       entry = buffer;
+#ifdef ZFT_CMAP_HACK
+       if ((strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG,
+                    strlen(ZFTAPE_SIG)) == 0) &&
+           entry[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) {
+               TRACE(ft_t_noise, "ignoring cmap volume");
+               entry += VTBL_SIZE;
+       } 
+#endif
+       /* the end of the vtbl is indicated by an invalid signature 
+        */
+       while (vtbl_signature_valid(&entry[VTBL_SIG]) &&
+              (entry - buffer) < FT_SEGMENT_SIZE) {
+               zft_new_vtbl_entry();
+               if (ft_format_code == fmt_big) {
+                       /* SCSI like vtbl, stores only the number of
+                        * segments used 
+                        */
+                       unsigned int num_segments= GET4(entry, VTBL_SCSI_SEGS);
+                       zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg;
+                       zft_last_vtbl->end_seg = 
+                               zft_last_vtbl->start_seg + num_segments - 1;
+               } else {
+                       /* `normal', QIC-80 like vtbl 
+                        */
+                       zft_last_vtbl->start_seg = GET2(entry, VTBL_START);
+                       zft_last_vtbl->end_seg   = GET2(entry, VTBL_END);
+               }
+               zft_eom_vtbl->start_seg  = zft_last_vtbl->end_seg + 1;
+               /* check if we created this volume and get the
+                * blk_sz 
+                */
+               zft_last_vtbl->zft_volume = check_volume(entry, zft_last_vtbl);
+               if (zft_last_vtbl->zft_volume == 0) {
+                       extract_alien_volume(entry, zft_last_vtbl);
+               } else {
+                       extract_zft_volume(entry, zft_last_vtbl);
+               }
+               DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], zft_last_vtbl);
+               entry +=VTBL_SIZE;
+       }
+#if 0
+/*
+ *  undefine to test end of tape handling
+ */
+       zft_new_vtbl_entry();
+       zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg;
+       zft_last_vtbl->end_seg   = ft_last_data_segment - 10;
+       zft_last_vtbl->blk_sz          = zft_blk_sz;
+       zft_last_vtbl->zft_volume      = 1;
+       zft_last_vtbl->qic113          = zft_qic113;
+       zft_last_vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1)
+                              - zft_calc_tape_pos(zft_last_vtbl->start_seg));
+#endif
+       TRACE_EXIT 0;
+}
+
+/* this functions translates the failed_sector_log, misused as
+ * EOF-marker list, into a virtual volume table. The table mustn't be
+ * written to tape, because this would occupy the first data segment,
+ * which should be the volume table, but is actualy the first segment
+ * that is filled with data (when using standard ftape).  We assume,
+ * that we get a non-empty failed_sector_log.
+ */
+int zft_fake_volume_headers (eof_mark_union *eof_map, int num_failed_sectors)
+{
+       unsigned int segment, sector;
+       int have_eom = 0;
+       int vol_no;
+       TRACE_FUN(ft_t_flow);
+
+       if ((num_failed_sectors >= 2) &&
+           (GET2(&eof_map[num_failed_sectors - 1].mark.segment, 0) 
+            == 
+            GET2(&eof_map[num_failed_sectors - 2].mark.segment, 0) + 1) &&
+           (GET2(&eof_map[num_failed_sectors - 1].mark.date, 0) == 1)) {
+               /* this should be eom. We keep the remainder of the
+                * tape as another volume.
+                */
+               have_eom = 1;
+       }
+       zft_init_vtbl();
+       zft_eom_vtbl->start_seg = ft_first_data_segment;
+       for(vol_no = 0; vol_no < num_failed_sectors - have_eom; vol_no ++) {
+               zft_new_vtbl_entry();
+
+               segment = GET2(&eof_map[vol_no].mark.segment, 0);
+               sector  = GET2(&eof_map[vol_no].mark.date, 0);
+
+               zft_last_vtbl->start_seg  = zft_eom_vtbl->start_seg;
+               zft_last_vtbl->end_seg    = segment;
+               zft_eom_vtbl->start_seg  = segment + 1;
+               zft_last_vtbl->blk_sz     = 1;
+               zft_last_vtbl->size       = 
+                       (zft_calc_tape_pos(zft_last_vtbl->end_seg)
+                        - zft_calc_tape_pos(zft_last_vtbl->start_seg)
+                        + (sector-1) * FT_SECTOR_SIZE);
+               TRACE(ft_t_noise, 
+                     "failed sector log: segment: %d, sector: %d", 
+                     segment, sector);
+               DUMP_VOLINFO(ft_t_noise, "Faked volume", zft_last_vtbl);
+       }
+       if (!have_eom) {
+               zft_new_vtbl_entry();
+               zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg;
+               zft_last_vtbl->end_seg   = ft_last_data_segment;
+               zft_eom_vtbl->start_seg  = ft_last_data_segment + 1;
+               zft_last_vtbl->size      = zft_capacity;
+               zft_last_vtbl->size     -= zft_calc_tape_pos(zft_last_vtbl->start_seg);
+               zft_last_vtbl->blk_sz    = 1;
+               DUMP_VOLINFO(ft_t_noise, "Faked volume",zft_last_vtbl);
+       }
+       TRACE_EXIT 0;
+}
+
+/* update the internal volume table
+ *
+ * if before start of last volume: erase all following volumes if
+ * inside a volume: set end of volume to infinity
+ *
+ * this function is intended to be called every time _ftape_write() is
+ * called
+ *
+ * return: 0 if no new volume was created, 1 if a new volume was
+ * created
+ *
+ * NOTE: we don't need to check for zft_mode as ftape_write() does
+ * that already. This function gets never called without accessing
+ * zftape via the *qft* devices 
+ */
+
+int zft_open_volume(zft_position *pos, int blk_sz, int use_compression)
+{ 
+       TRACE_FUN(ft_t_flow);
+       
+       if (!zft_qic_mode) {
+               TRACE_EXIT 0;
+       }
+       if (zft_tape_at_lbot(pos)) {
+               zft_init_vtbl();
+               if(zft_old_ftape) {
+                       /* clear old ftape's eof marks */
+                       zft_clear_ftape_file_marks();
+                       zft_old_ftape = 0; /* no longer old ftape */
+               }
+               zft_reset_position(pos);
+       }
+       if (pos->seg_pos != zft_last_vtbl->end_seg + 1) {
+               TRACE_ABORT(-EIO, ft_t_bug, 
+                     "BUG: seg_pos: %d, zft_last_vtbl->end_seg: %d", 
+                     pos->seg_pos, zft_last_vtbl->end_seg);
+       }                            
+       TRACE(ft_t_noise, "create new volume");
+       if (zft_eom_vtbl->count >= ZFT_MAX_VOLUMES) {
+               TRACE_ABORT(-ENOSPC, ft_t_err,
+                           "Error: maxmimal number of volumes exhausted "
+                           "(maxmimum is %d)", ZFT_MAX_VOLUMES);
+       }
+       zft_new_vtbl_entry();
+       pos->volume_pos = pos->seg_byte_pos = 0;
+       zft_last_vtbl->start_seg       = pos->seg_pos;
+       zft_last_vtbl->end_seg         = ft_last_data_segment; /* infinity */
+       zft_last_vtbl->blk_sz          = blk_sz;
+       zft_last_vtbl->size            = zft_capacity;
+       zft_last_vtbl->zft_volume      = 1;
+       zft_last_vtbl->use_compression = use_compression;
+       zft_last_vtbl->qic113          = zft_qic113;
+       zft_last_vtbl->new_volume      = 1;
+       zft_last_vtbl->open            = 1;
+       zft_volume_table_changed = 1;
+       zft_eom_vtbl->start_seg  = ft_last_data_segment + 1;
+       TRACE_EXIT 0;
+}
+
+/*  perform mtfsf, mtbsf, not allowed without zft_qic_mode
+ */
+int zft_skip_volumes(int count, zft_position *pos)
+{ 
+       const zft_volinfo *vtbl;
+       TRACE_FUN(ft_t_flow);
+       
+       TRACE(ft_t_noise, "count: %d", count);
+       
+       vtbl= zft_find_volume(pos->seg_pos);
+       while (count > 0 && vtbl != zft_eom_vtbl) {
+               vtbl = list_entry(vtbl->node.next, zft_volinfo, node);
+               count --;
+       }
+       while (count < 0 && vtbl != zft_first_vtbl) {
+               vtbl = list_entry(vtbl->node.prev, zft_volinfo, node);
+               count ++;
+       }
+       pos->seg_pos        = vtbl->start_seg;
+       pos->seg_byte_pos   = 0;
+       pos->volume_pos     = 0;
+       pos->tape_pos       = zft_calc_tape_pos(pos->seg_pos);
+       zft_just_before_eof = vtbl->size == 0;
+       if (zft_cmpr_ops) {
+               (*zft_cmpr_ops->reset)();
+       }
+       zft_deblock_segment = -1; /* no need to keep cache */
+       TRACE(ft_t_noise, "repositioning to:\n"
+             KERN_INFO "zft_seg_pos        : %d\n"
+             KERN_INFO "zft_seg_byte_pos   : %d\n"
+             KERN_INFO "zft_tape_pos       : " LL_X "\n"
+             KERN_INFO "zft_volume_pos     : " LL_X "\n"
+             KERN_INFO "file number        : %d",
+             pos->seg_pos, pos->seg_byte_pos, 
+             LL(pos->tape_pos), LL(pos->volume_pos), vtbl->count);
+       zft_resid = count < 0 ? -count : count;
+       TRACE_EXIT zft_resid ? -EINVAL : 0;
+}
+
+/* the following simply returns the raw data position of the EOM
+ * marker, MTIOCSIZE ioctl 
+ */
+__s64 zft_get_eom_pos(void)
+{
+       if (zft_qic_mode) {
+               return zft_calc_tape_pos(zft_eom_vtbl->start_seg);
+       } else {
+               /* there is only one volume in raw mode */
+               return zft_capacity;
+       }
+}
+
+/* skip to eom, used for MTEOM
+ */
+void zft_skip_to_eom(zft_position *pos)
+{
+       TRACE_FUN(ft_t_flow);
+       pos->seg_pos      = zft_eom_vtbl->start_seg;
+       pos->seg_byte_pos = 
+               pos->volume_pos     = 
+               zft_just_before_eof = 0;
+       pos->tape_pos = zft_calc_tape_pos(pos->seg_pos);
+       TRACE(ft_t_noise, "ftape positioned to segment %d, data pos " LL_X, 
+             pos->seg_pos, LL(pos->tape_pos));
+       TRACE_EXIT;
+}
+
+/*  write an EOF-marker by setting zft_last_vtbl->end_seg to seg_pos.
+ *  NOTE: this function assumes that zft_last_vtbl points to a valid
+ *  vtbl entry
+ *
+ *  NOTE: this routine always positions before the EOF marker
+ */
+int zft_close_volume(zft_position *pos)
+{
+       TRACE_FUN(ft_t_any);
+
+       if (zft_vtbl_empty || !zft_last_vtbl->open) { /* should not happen */
+               TRACE(ft_t_noise, "There are no volumes to finish");
+               TRACE_EXIT -EIO;
+       }
+       if (pos->seg_byte_pos == 0 && 
+           pos->seg_pos != zft_last_vtbl->start_seg) {
+               pos->seg_pos --;
+               pos->seg_byte_pos      = zft_get_seg_sz(pos->seg_pos);
+       }
+       zft_last_vtbl->end_seg   = pos->seg_pos;
+       zft_last_vtbl->size      = pos->volume_pos;
+       zft_volume_table_changed = 1;
+       zft_just_before_eof      = 1;
+       zft_eom_vtbl->start_seg  = zft_last_vtbl->end_seg + 1;
+       zft_last_vtbl->open      = 0; /* closed */
+       TRACE_EXIT 0;
+}
+
+/* write count file-marks at current position. 
+ *
+ *  The tape is positioned after the eof-marker, that is at byte 0 of
+ *  the segment following the eof-marker
+ *
+ *  this function is only allowed in zft_qic_mode
+ *
+ *  Only allowed when tape is at BOT or EOD.
+ */
+int zft_weof(unsigned int count, zft_position *pos)
+{
+       
+       TRACE_FUN(ft_t_flow);
+
+       if (!count) { /* write zero EOF marks should be a real no-op */
+               TRACE_EXIT 0;
+       }
+       zft_volume_table_changed = 1;
+       if (zft_tape_at_lbot(pos)) {
+               zft_init_vtbl();
+               if(zft_old_ftape) {
+                       /* clear old ftape's eof marks */
+                       zft_clear_ftape_file_marks();
+                       zft_old_ftape = 0;    /* no longer old ftape */
+               }
+       }
+       if (zft_last_vtbl->open) {
+               zft_close_volume(pos);
+               zft_move_past_eof(pos);
+               count --;
+       }
+       /* now it's easy, just append eof-marks, that is empty
+        * volumes, to the end of the already recorded media.
+        */
+       while (count > 0 && 
+              pos->seg_pos <= ft_last_data_segment && 
+              zft_eom_vtbl->count < ZFT_MAX_VOLUMES) {
+               TRACE(ft_t_noise,
+                     "Writing zero sized file at segment %d", pos->seg_pos);
+               zft_new_vtbl_entry();
+               zft_last_vtbl->start_seg       = pos->seg_pos;
+               zft_last_vtbl->end_seg         = pos->seg_pos;
+               zft_last_vtbl->size            = 0;
+               zft_last_vtbl->blk_sz          = zft_blk_sz;
+               zft_last_vtbl->zft_volume      = 1;
+               zft_last_vtbl->use_compression = 0;
+               pos->tape_pos += zft_get_seg_sz(pos->seg_pos);
+               zft_eom_vtbl->start_seg = ++ pos->seg_pos;
+               count --;
+       } 
+       if (count > 0) {
+               /*  there are two possibilities: end of tape, or the
+                *  maximum number of files is exhausted.
+                */
+               zft_resid = count;
+               TRACE(ft_t_noise,"Number of marks NOT written: %d", zft_resid);
+               if (zft_eom_vtbl->count == ZFT_MAX_VOLUMES) {
+                       TRACE_ABORT(-EINVAL, ft_t_warn,
+                                   "maximum allowed number of files "
+                                   "exhausted: %d", ZFT_MAX_VOLUMES);
+               } else {
+                       TRACE_ABORT(-ENOSPC,
+                                   ft_t_noise, "reached end of tape");
+               }
+       }
+       TRACE_EXIT 0;
+}
+
+const zft_volinfo *zft_find_volume(unsigned int seg_pos)
+{
+       TRACE_FUN(ft_t_flow);
+       
+       TRACE(ft_t_any, "called with seg_pos %d",seg_pos);
+       if (!zft_qic_mode) {
+               if (seg_pos > ft_last_data_segment) {
+                       TRACE_EXIT &eot_vtbl;
+               }
+               tape_vtbl.blk_sz =  zft_blk_sz;
+               TRACE_EXIT &tape_vtbl;
+       }
+       if (seg_pos < zft_first_vtbl->start_seg) {
+               TRACE_EXIT (cur_vtbl = zft_first_vtbl);
+       }
+       while (seg_pos > cur_vtbl->end_seg) {
+               cur_vtbl = list_entry(cur_vtbl->node.next, zft_volinfo, node);
+               TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg);
+       }
+       while (seg_pos < cur_vtbl->start_seg) {
+               cur_vtbl = list_entry(cur_vtbl->node.prev, zft_volinfo, node);
+               TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg);
+       }
+       if (seg_pos > cur_vtbl->end_seg || seg_pos < cur_vtbl->start_seg) {
+               TRACE(ft_t_bug, "This cannot happen");
+       }
+       DUMP_VOLINFO(ft_t_noise, "", cur_vtbl);
+       TRACE_EXIT cur_vtbl;
+}
+
+/* this function really assumes that we are just before eof
+ */
+void zft_move_past_eof(zft_position *pos)
+{ 
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_noise, "old seg. pos: %d", pos->seg_pos);
+       pos->tape_pos += zft_get_seg_sz(pos->seg_pos++) - pos->seg_byte_pos;
+       pos->seg_byte_pos = 0;
+       pos->volume_pos   = 0;
+       if (zft_cmpr_ops) {
+               (*zft_cmpr_ops->reset)();
+       }
+       zft_just_before_eof =  0;
+       zft_deblock_segment = -1; /* no need to cache it anymore */
+       TRACE(ft_t_noise, "new seg. pos: %d", pos->seg_pos);
+       TRACE_EXIT;
+}
diff --git a/drivers/char/ftape/zftape/zftape-vtbl.h b/drivers/char/ftape/zftape/zftape-vtbl.h
new file mode 100644 (file)
index 0000000..7248db1
--- /dev/null
@@ -0,0 +1,229 @@
+#ifndef _ZFTAPE_VTBL_H
+#define _ZFTAPE_VTBL_H
+
+/*
+ *      Copyright (c) 1995-1997  Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.h,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/28 14:30:09 $
+ *
+ *      This file defines a volume table as defined in the QIC-80
+ *      development standards.
+ */
+
+#include <linux/list.h>
+
+#include "../lowlevel/ftape-tracing.h"
+
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-rw.h"
+
+#define VTBL_SIZE 128 /* bytes */
+
+/* The following are offsets in the vtbl.  */
+#define VTBL_SIG   0
+#define VTBL_START 4
+#define VTBL_END   6
+#define VTBL_DESC  8
+#define VTBL_DATE  52
+#define VTBL_FLAGS 56
+#define VTBL_FL_VENDOR_SPECIFIC (1<<0)
+#define VTBL_FL_MUTLI_CARTRIDGE (1<<1)
+#define VTBL_FL_NOT_VERIFIED    (1<<2)
+#define VTBL_FL_REDIR_INHIBIT   (1<<3)
+#define VTBL_FL_SEG_SPANNING    (1<<4)
+#define VTBL_FL_DIRECTORY_LAST  (1<<5)
+#define VTBL_FL_RESERVED_6      (1<<6)
+#define VTBL_FL_RESERVED_7      (1<<7)
+#define VTBL_M_NO  57
+#define VTBL_EXT   58
+#define EXT_ZFTAPE_SIG     0
+#define EXT_ZFTAPE_BLKSZ  10
+#define EXT_ZFTAPE_CMAP   12
+#define EXT_ZFTAPE_QIC113 13
+#define VTBL_PWD   84
+#define VTBL_DIR_SIZE 92
+#define VTBL_DATA_SIZE 96
+#define VTBL_OS_VERSION 104
+#define VTBL_SRC_DRIVE  106
+#define VTBL_DEV        122
+#define VTBL_RESERVED_1 123
+#define VTBL_CMPR       124
+#define VTBL_CMPR_UNREG 0x3f
+#define VTBL_CMPR_USED  0x80
+#define VTBL_FMT        125
+#define VTBL_RESERVED_2 126
+#define VTBL_RESERVED_3 127
+/* compatability with pre revision K */
+#define VTBL_K_CMPR     120 
+
+/*  the next is used by QIC-3020 tapes with format code 6 (>2^16
+ *  segments) It is specified in QIC-113, Rev. G, Section 5 (SCSI
+ *  volume table). The difference is simply, that we only store the
+ *  number of segments used, not the starting segment.
+ */
+#define VTBL_SCSI_SEGS  4 /* is a 4 byte value */
+
+/*  one vtbl is 128 bytes, that results in a maximum number of
+ *  29*1024/128 = 232 volumes.
+ */
+#define ZFT_MAX_VOLUMES (FT_SEGMENT_SIZE/VTBL_SIZE)
+#define VTBL_ID  "VTBL"
+#define VTBL_IDS { VTBL_ID, "XTBL", "UTID", "EXVT" } /* other valid ids */
+#define ZFT_VOL_NAME "zftape volume" /* volume label used by me */
+#define ZFTAPE_SIG "LINUX ZFT"
+
+/*  global variables
+ */
+typedef struct zft_internal_vtbl
+{
+       struct list_head node;
+       int          count;
+       unsigned int start_seg;         /* 32 bits are enough for now */
+       unsigned int end_seg;           /* 32 bits are enough for now */
+       __s64        size;              /* uncompressed size */
+        unsigned int blk_sz;            /* block size for this volume */
+       unsigned int zft_volume     :1; /* zftape created this volume */
+       unsigned int use_compression:1; /* compressed volume  */
+       unsigned int qic113         :1; /* layout of compressed block
+                                        * info and vtbl conforms to
+                                        * QIC-113, Rev. G 
+                                        */
+       unsigned int new_volume     :1; /* it was created by us, this
+                                        * run.  this allows the
+                                        * fields that aren't really
+                                        * used by zftape to be filled
+                                        * in by some user level
+                                        * program.
+                                        */
+       unsigned int open           :1; /* just in progress of being 
+                                        * written
+                                        */
+} zft_volinfo;
+
+extern struct list_head zft_vtbl;
+#define zft_head_vtbl  list_entry(zft_vtbl.next, zft_volinfo, node)
+#define zft_eom_vtbl   list_entry(zft_vtbl.prev, zft_volinfo, node)
+#define zft_last_vtbl  list_entry(zft_eom_vtbl->node.prev, zft_volinfo, node)
+#define zft_first_vtbl list_entry(zft_head_vtbl->node.next, zft_volinfo, node)
+#define zft_vtbl_empty (zft_eom_vtbl->node.prev == &zft_head_vtbl->node)
+
+#define DUMP_VOLINFO(level, desc, info)                                        \
+{                                                                      \
+       char tmp[21];                                                   \
+       strncpy(tmp, desc, 20);                                         \
+       tmp[20] = '\0';                                                 \
+       TRACE(level, "Volume %d:\n"                                     \
+             KERN_INFO "description  : %s\n"                           \
+             KERN_INFO "first segment: %d\n"                           \
+             KERN_INFO "last  segment: %d\n"                           \
+             KERN_INFO "size         : " LL_X "\n"                     \
+             KERN_INFO "block size   : %d\n"                           \
+             KERN_INFO "compression  : %d\n"                           \
+             KERN_INFO "zftape volume: %d\n"                           \
+             KERN_INFO "QIC-113 conf.: %d",                            \
+             (info)->count, tmp, (info)->start_seg, (info)->end_seg,   \
+             LL((info)->size), (info)->blk_sz,                         \
+             (info)->use_compression != 0, (info)->zft_volume != 0,    \
+             (info)->qic113 != 0);                                     \
+}
+
+extern int zft_qic_mode;
+extern int zft_old_ftape;
+extern int zft_volume_table_changed;
+
+/* exported functions */
+extern void  zft_init_vtbl             (void);
+extern void  zft_free_vtbl             (void);
+extern void  zft_new_vtbl_entry        (void);
+extern int   zft_extract_volume_headers(__u8 *buffer);
+extern int   zft_update_volume_table   (unsigned int segment);
+extern int   zft_open_volume           (zft_position *pos,
+                                       int blk_sz, int use_compression);
+extern int   zft_close_volume          (zft_position *pos);
+extern const zft_volinfo *zft_find_volume(unsigned int seg_pos);
+extern int   zft_skip_volumes          (int count, zft_position *pos);
+extern __s64 zft_get_eom_pos           (void);
+extern void  zft_skip_to_eom           (zft_position *pos);
+extern int   zft_fake_volume_headers   (eof_mark_union *eof_map, 
+                                       int num_failed_sectors);
+extern int   zft_weof                  (unsigned int count, zft_position *pos);
+extern void  zft_move_past_eof         (zft_position *pos);
+
+extern inline int   zft_tape_at_eod         (const zft_position *pos);
+extern inline int   zft_tape_at_lbot        (const zft_position *pos);
+extern inline void  zft_position_before_eof (zft_position *pos, 
+                                            const zft_volinfo *volume);
+extern inline __s64 zft_check_for_eof(const zft_volinfo *vtbl,
+                                     const zft_position *pos);
+
+/* this function decrements the zft_seg_pos counter if we are right
+ * at the beginning of a segment. This is to handel fsfm/bsfm -- we
+ * need to position before the eof mark.  NOTE: zft_tape_pos is not
+ * changed 
+ */
+extern inline void zft_position_before_eof(zft_position *pos, 
+                                          const zft_volinfo *volume)
+{ 
+       TRACE_FUN(ft_t_flow);
+
+       if (pos->seg_pos == volume->end_seg + 1 &&  pos->seg_byte_pos == 0) {
+               pos->seg_pos --;
+               pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos);
+       }
+       TRACE_EXIT;
+}
+
+/*  Mmmh. Is the position at the end of the last volume, that is right
+ *  before the last EOF mark also logical an EOD condition?
+ */
+extern inline int zft_tape_at_eod(const zft_position *pos)
+{ 
+       TRACE_FUN(ft_t_any);
+
+       if (zft_qic_mode) {
+               TRACE_EXIT (pos->seg_pos >= zft_eom_vtbl->start_seg ||
+                           zft_last_vtbl->open);
+       } else {
+               TRACE_EXIT pos->seg_pos > ft_last_data_segment;
+       }
+}
+
+extern inline int zft_tape_at_lbot(const zft_position *pos)
+{
+       if (zft_qic_mode) {
+               return (pos->seg_pos <= zft_first_vtbl->start_seg &&
+                       pos->volume_pos == 0);
+       } else {
+               return (pos->seg_pos <= ft_first_data_segment && 
+                       pos->volume_pos == 0);
+       }
+}
+
+/* This one checks for EOF.  return remaing space (may be negative) 
+ */
+extern inline __s64 zft_check_for_eof(const zft_volinfo *vtbl,
+                                     const zft_position *pos)
+{     
+       return (__s64)(vtbl->size - pos->volume_pos);
+}
+
+#endif /* _ZFTAPE_VTBL_H */
diff --git a/drivers/char/ftape/zftape/zftape-write.c b/drivers/char/ftape/zftape/zftape-write.c
new file mode 100644 (file)
index 0000000..46f1ecc
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ *      Copyright (C) 1996, 1997 Claus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-write.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/11/06 00:50:29 $
+ *
+ *      This file contains the writing code
+ *      for the QIC-117 floppy-tape driver for Linux.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#include <linux/zftape.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6)
+#include <asm/uaccess.h>
+#else
+#include <asm/segment.h>
+#endif
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-eof.h"
+#include "../zftape/zftape-ctl.h"
+#include "../zftape/zftape-write.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-rw.h"
+#include "../zftape/zftape-vtbl.h"
+
+/*      Global vars.
+ */
+
+/*      Local vars.
+ */
+static int last_write_failed = 0;
+static int need_flush = 0;
+
+void zft_prevent_flush(void)
+{
+       need_flush = 0;
+}
+
+static int zft_write_header_segments(__u8* buffer)
+{
+       int header_1_ok = 0;
+       int header_2_ok = 0;
+       unsigned int time_stamp;
+       TRACE_FUN(ft_t_noise);
+       
+       TRACE_CATCH(ftape_abort_operation(),);
+       ftape_seek_to_bot();    /* prevents extra rewind */
+       if (GET4(buffer, 0) != FT_HSEG_MAGIC) {
+               TRACE_ABORT(-EIO, ft_t_err,
+                           "wrong header signature found, aborting");
+       } 
+       /*   Be optimistic: */
+       PUT4(buffer, FT_SEG_CNT,
+            zft_written_segments + GET4(buffer, FT_SEG_CNT) + 2);
+       if ((time_stamp = zft_get_time()) != 0) {
+               PUT4(buffer, FT_WR_DATE, time_stamp);
+               if (zft_label_changed) {
+                       PUT4(buffer, FT_LABEL_DATE, time_stamp);
+               }
+       }
+       TRACE(ft_t_noise,
+             "writing first header segment %d", ft_header_segment_1);
+       header_1_ok = zft_verify_write_segments(ft_header_segment_1, 
+                                               buffer, FT_SEGMENT_SIZE,
+                                               zft_deblock_buf) >= 0;
+       TRACE(ft_t_noise,
+             "writing second header segment %d", ft_header_segment_2);
+       header_2_ok = zft_verify_write_segments(ft_header_segment_2, 
+                                               buffer, FT_SEGMENT_SIZE,
+                                               zft_deblock_buf) >= 0;
+       if (!header_1_ok) {
+               TRACE(ft_t_warn, "Warning: "
+                     "update of first header segment failed");
+       }
+       if (!header_2_ok) {
+               TRACE(ft_t_warn, "Warning: "
+                     "update of second header segment failed");
+       }
+       if (!header_1_ok && !header_2_ok) {
+               TRACE_ABORT(-EIO, ft_t_err, "Error: "
+                     "update of both header segments failed.");
+       }
+       TRACE_EXIT 0;
+}
+
+int zft_update_header_segments(void)
+{
+       TRACE_FUN(ft_t_noise);
+       
+       /*  must NOT use zft_write_protected, as it also includes the
+        *  file access mode. But we also want to update when soft
+        *  write protection is enabled (O_RDONLY)
+        */
+       if (ft_write_protected || zft_old_ftape) {
+               TRACE_ABORT(0, ft_t_noise, "Tape set read-only: no update");
+       } 
+       if (!zft_header_read) {
+               TRACE_ABORT(0, ft_t_noise, "Nothing to update");
+       }
+       if (!zft_header_changed) {
+               zft_header_changed = zft_written_segments > 0;
+       }
+       if (!zft_header_changed && !zft_volume_table_changed) {
+               TRACE_ABORT(0, ft_t_noise, "Nothing to update");
+       }
+       TRACE(ft_t_noise, "Updating header segments");
+       if (ftape_get_status()->fti_state == writing) {
+               TRACE_CATCH(ftape_loop_until_writes_done(),);
+       }
+       TRACE_CATCH(ftape_abort_operation(),);
+       
+       zft_deblock_segment = -1; /* invalidate the cache */
+       if (zft_header_changed) {
+               TRACE_CATCH(zft_write_header_segments(zft_hseg_buf),);
+       }
+       if (zft_volume_table_changed) {
+               TRACE_CATCH(zft_update_volume_table(ft_first_data_segment),);
+       }
+       zft_header_changed =
+               zft_volume_table_changed = 
+               zft_label_changed        =
+               zft_written_segments     = 0;
+       TRACE_CATCH(ftape_abort_operation(),);
+       ftape_seek_to_bot();
+       TRACE_EXIT 0;
+}
+
+static int read_merge_buffer(int seg_pos, __u8 *buffer, int offset, int seg_sz)
+{
+       int result = 0;
+       const ft_trace_t old_tracing = TRACE_LEVEL;
+       TRACE_FUN(ft_t_flow);
+       
+       if (zft_qic_mode) {
+               /*  writing in the middle of a volume is NOT allowed
+                *
+                */
+               TRACE(ft_t_noise, "No need to read a segment");
+               memset(buffer + offset, 0, seg_sz - offset);
+               TRACE_EXIT 0;
+       }
+       TRACE(ft_t_any, "waiting");
+       ftape_start_writing(FT_WR_MULTI);
+       TRACE_CATCH(ftape_loop_until_writes_done(),);
+       
+       TRACE(ft_t_noise, "trying to read segment %d from offset %d",
+             seg_pos, offset);
+       SET_TRACE_LEVEL(ft_t_bug);
+       result = zft_fetch_segment_fraction(seg_pos, buffer, 
+                                           FT_RD_SINGLE,
+                                           offset, seg_sz - offset);
+       SET_TRACE_LEVEL(old_tracing);
+       if (result != (seg_sz - offset)) {
+               TRACE(ft_t_noise, "Ignore error: read_segment() result: %d",
+                     result);
+               memset(buffer + offset, 0, seg_sz - offset);
+       }
+       TRACE_EXIT 0;
+}
+
+/* flush the write buffer to tape and write an eof-marker at the
+ * current position if not in raw mode.  This function always
+ * positions the tape before the eof-marker.  _ftape_close() should
+ * then advance to the next segment.
+ *
+ * the parameter "finish_volume" describes whether to position before
+ * or after the possibly created file-mark. We always position after
+ * the file-mark when called from ftape_close() and a flush was needed
+ * (that is ftape_write() was the last tape operation before calling
+ * ftape_flush) But we always position before the file-mark when this
+ * function get's called from outside ftape_close() 
+ */
+int zft_flush_buffers(void)
+{
+       int result;
+       int data_remaining;
+       int this_segs_size;
+       TRACE_FUN(ft_t_flow);
+
+       TRACE(ft_t_data_flow,
+             "entered, ftape_state = %d", ftape_get_status()->fti_state);
+       if (ftape_get_status()->fti_state != writing && !need_flush) {
+               TRACE_ABORT(0, ft_t_noise, "no need for flush");
+       }
+       zft_io_state = zft_idle; /*  triggers some initializations for the
+                                 *  read and write routines 
+                                 */
+       if (last_write_failed) {
+               ftape_abort_operation();
+               TRACE_EXIT -EIO;
+       }
+       TRACE(ft_t_noise, "flushing write buffers");
+       this_segs_size = zft_get_seg_sz(zft_pos.seg_pos);
+       if (this_segs_size == zft_pos.seg_byte_pos) {
+               zft_pos.seg_pos ++;
+               data_remaining = zft_pos.seg_byte_pos = 0;
+       } else {
+               data_remaining = zft_pos.seg_byte_pos;
+       }
+       /* If there is any data not written to tape yet, append zero's
+        * up to the end of the sector (if using compression) or merge
+        * it with the data existing on the tape Then write the
+        * segment(s) to tape.
+        */
+       TRACE(ft_t_noise, "Position:\n"
+             KERN_INFO "seg_pos  : %d\n"
+             KERN_INFO "byte pos : %d\n"
+             KERN_INFO "remaining: %d",
+             zft_pos.seg_pos, zft_pos.seg_byte_pos, data_remaining);
+       if (data_remaining > 0) {
+               do {
+                       this_segs_size = zft_get_seg_sz(zft_pos.seg_pos);
+                       if (this_segs_size > data_remaining) {
+                               TRACE_CATCH(read_merge_buffer(zft_pos.seg_pos,
+                                                             zft_deblock_buf,
+                                                             data_remaining,
+                                                             this_segs_size),
+                                           last_write_failed = 1);
+                       }
+                       result = ftape_write_segment(zft_pos.seg_pos, 
+                                                    zft_deblock_buf,
+                                                    FT_WR_MULTI);
+                       if (result != this_segs_size) {
+                               TRACE(ft_t_err, "flush buffers failed");
+                               zft_pos.tape_pos    -= zft_pos.seg_byte_pos;
+                               zft_pos.seg_byte_pos = 0;
+
+                               last_write_failed = 1;
+                               TRACE_EXIT result;
+                       }
+                       zft_written_segments ++;
+                       TRACE(ft_t_data_flow,
+                             "flush, moved out buffer: %d", result);
+                       /* need next segment for more data (empty segments?)
+                        */
+                       if (result < data_remaining) { 
+                               if (result > 0) {       
+                                       /* move remainder to buffer beginning 
+                                        */
+                                       memmove(zft_deblock_buf, 
+                                               zft_deblock_buf + result,
+                                               FT_SEGMENT_SIZE - result);
+                               }
+                       } 
+                       data_remaining -= result;
+                       zft_pos.seg_pos ++;
+               } while (data_remaining > 0);
+               TRACE(ft_t_any, "result: %d", result);
+               zft_deblock_segment = --zft_pos.seg_pos;
+               if (data_remaining == 0) {  /* first byte next segment */
+                       zft_pos.seg_byte_pos = this_segs_size;
+               } else { /* after data previous segment, data_remaining < 0 */
+                       zft_pos.seg_byte_pos = data_remaining + result;
+               }
+       } else {
+               TRACE(ft_t_noise, "zft_deblock_buf empty");
+               zft_pos.seg_pos --;
+               zft_pos.seg_byte_pos = zft_get_seg_sz (zft_pos.seg_pos);
+               ftape_start_writing(FT_WR_MULTI);
+       }
+       TRACE(ft_t_any, "waiting");
+       if ((result = ftape_loop_until_writes_done()) < 0) {
+               /* that's really bad. What to to with zft_tape_pos? 
+                */
+               TRACE(ft_t_err, "flush buffers failed");
+       }
+       TRACE(ft_t_any, "zft_seg_pos: %d, zft_seg_byte_pos: %d",
+             zft_pos.seg_pos, zft_pos.seg_byte_pos);
+       last_write_failed  =
+               need_flush = 0;
+       TRACE_EXIT result;
+}
+
+/* return-value: the number of bytes removed from the user-buffer
+ *
+ * out: 
+ *      int *write_cnt: how much actually has been moved to the
+ *                      zft_deblock_buf
+ *      int req_len  : MUST NOT BE CHANGED, except at EOT, in 
+ *                      which case it may be adjusted
+ * in : 
+ *      char *buff        : the user buffer
+ *      int buf_pos_write : copy of buf_len_wr int
+ *      this_segs_size    : the size in bytes of the actual segment
+ *                          char
+ *      *zft_deblock_buf   : zft_deblock_buf
+ */
+static int zft_simple_write(int *cnt,
+                           __u8 *dst_buf, const int seg_sz,
+                           const __u8 *src_buf, const int req_len, 
+                           const zft_position *pos,const zft_volinfo *volume)
+{
+       int space_left;
+       TRACE_FUN(ft_t_flow);
+
+       /* volume->size holds the tape capacity while volume is open */
+       if (pos->tape_pos + volume->blk_sz > volume->size) {
+               TRACE_EXIT -ENOSPC;
+       }
+       /*  remaining space in this segment, NOT zft_deblock_buf
+        */
+       space_left = seg_sz - pos->seg_byte_pos;
+       *cnt = req_len < space_left ? req_len : space_left;
+#if LINUX_VERSION_CODE > KERNEL_VER(2,1,3)
+       if (copy_from_user(dst_buf + pos->seg_byte_pos, src_buf, *cnt) != 0) {
+               TRACE_EXIT -EFAULT;
+       }
+#else
+       TRACE_CATCH(verify_area(VERIFY_READ, src_buf, *cnt),);
+       memcpy_fromfs(dst_buf + pos->seg_byte_pos, src_buf, *cnt);
+#endif
+       TRACE_EXIT *cnt;
+}
+
+static int check_write_access(int req_len,
+                             const zft_volinfo **volume,
+                             zft_position *pos,
+                             const unsigned int blk_sz)
+{
+       int result;
+       TRACE_FUN(ft_t_flow);
+
+       if ((req_len % zft_blk_sz) != 0) {
+               TRACE_ABORT(-EINVAL, ft_t_info,
+                           "write-count %d must be multiple of block-size %d",
+                           req_len, blk_sz);
+       }
+       if (zft_io_state == zft_writing) {
+               /*  all other error conditions have been checked earlier
+                */
+               TRACE_EXIT 0;
+       }
+       zft_io_state = zft_idle;
+       TRACE_CATCH(zft_check_write_access(pos),);
+       /*  If we haven't read the header segment yet, do it now.
+        *  This will verify the configuration, get the bad sector
+        *  table and read the volume table segment 
+        */
+       if (!zft_header_read) {
+               TRACE_CATCH(zft_read_header_segments(),);
+       }
+       /*  fine. Now the tape is either at BOT or at EOD,
+        *  Write start of volume now
+        */
+       TRACE_CATCH(zft_open_volume(pos, blk_sz, zft_use_compression),);
+       *volume = zft_find_volume(pos->seg_pos);
+       DUMP_VOLINFO(ft_t_noise, "", *volume);
+       zft_just_before_eof = 0;
+       /* now merge with old data if neccessary */
+       if (!zft_qic_mode && pos->seg_byte_pos != 0){
+               result = zft_fetch_segment(pos->seg_pos,
+                                          zft_deblock_buf,
+                                          FT_RD_SINGLE);
+               if (result < 0) {
+                       if (result == -EINTR || result == -ENOSPC) {
+                               TRACE_EXIT result;
+                       }
+                       TRACE(ft_t_noise, 
+                             "ftape_read_segment() result: %d. "
+                             "This might be normal when using "
+                             "a newly\nformatted tape", result);
+                       memset(zft_deblock_buf, '\0', pos->seg_byte_pos);
+               }
+       }
+       zft_io_state = zft_writing;
+       TRACE_EXIT 0;
+}
+
+static int fill_deblock_buf(__u8 *dst_buf, const int seg_sz,
+                           zft_position *pos, const zft_volinfo *volume,
+                           const char *usr_buf, const int req_len)
+{
+       int cnt = 0;
+       int result = 0;
+       TRACE_FUN(ft_t_flow);
+
+       if (seg_sz == 0) {
+               TRACE_ABORT(0, ft_t_data_flow, "empty segment");
+       }
+       TRACE(ft_t_data_flow, "\n"
+             KERN_INFO "remaining req_len: %d\n"
+             KERN_INFO "          buf_pos: %d", 
+             req_len, pos->seg_byte_pos);
+       /* zft_deblock_buf will not contain a valid segment any longer */
+       zft_deblock_segment = -1;
+       if (zft_use_compression) {
+               TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),);
+               TRACE_CATCH(result= (*zft_cmpr_ops->write)(&cnt,
+                                                          dst_buf, seg_sz,
+                                                          usr_buf, req_len,
+                                                          pos, volume),);
+       } else {
+               TRACE_CATCH(result= zft_simple_write(&cnt,
+                                                    dst_buf, seg_sz,
+                                                    usr_buf, req_len,
+                                                    pos, volume),);
+       }
+       pos->volume_pos   += result;
+       pos->seg_byte_pos += cnt;
+       pos->tape_pos     += cnt;
+       TRACE(ft_t_data_flow, "\n"
+             KERN_INFO "removed from user-buffer : %d bytes.\n"
+             KERN_INFO "copied to zft_deblock_buf: %d bytes.\n"
+             KERN_INFO "zft_tape_pos             : " LL_X " bytes.",
+             result, cnt, LL(pos->tape_pos));
+       TRACE_EXIT result;
+}
+
+
+/*  called by the kernel-interface routine "zft_write()"
+ */
+int _zft_write(const char* buff, int req_len)
+{
+       int result = 0;
+       int written = 0;
+       int write_cnt;
+       int seg_sz;
+       static const zft_volinfo *volume = NULL;
+       TRACE_FUN(ft_t_flow);
+       
+       zft_resid         = req_len;    
+       last_write_failed = 1; /* reset to 0 when successful */
+       /* check if write is allowed 
+        */
+       TRACE_CATCH(check_write_access(req_len, &volume,&zft_pos,zft_blk_sz),);
+       while (req_len > 0) {
+               /* Allow us to escape from this loop with a signal !
+                */
+               FT_SIGNAL_EXIT(_DONT_BLOCK);
+               seg_sz = zft_get_seg_sz(zft_pos.seg_pos);
+               if ((write_cnt = fill_deblock_buf(zft_deblock_buf,
+                                                 seg_sz,
+                                                 &zft_pos,
+                                                 volume,
+                                                 buff,
+                                                 req_len)) < 0) {
+                       zft_resid -= written;
+                       if (write_cnt == -ENOSPC) {
+                               /* leave the remainder to flush_buffers()
+                                */
+                               TRACE(ft_t_info, "No space left on device");
+                               last_write_failed = 0;
+                               if (!need_flush) {
+                                       need_flush = written > 0;
+                               }
+                               TRACE_EXIT written > 0 ? written : -ENOSPC;
+                       } else {
+                               TRACE_EXIT result;
+                       }
+               }
+               if (zft_pos.seg_byte_pos == seg_sz) {
+                       TRACE_CATCH(ftape_write_segment(zft_pos.seg_pos, 
+                                                       zft_deblock_buf,
+                                                       FT_WR_ASYNC),
+                                   zft_resid -= written);
+                       zft_written_segments ++;
+                       zft_pos.seg_byte_pos =  0;
+                       zft_deblock_segment  = zft_pos.seg_pos;
+                       ++zft_pos.seg_pos;
+               }
+               written += write_cnt;
+               buff    += write_cnt;
+               req_len -= write_cnt;
+       } /* while (req_len > 0) */
+       TRACE(ft_t_data_flow, "remaining in blocking buffer: %d",
+              zft_pos.seg_byte_pos);
+       TRACE(ft_t_data_flow, "just written bytes: %d", written);
+       last_write_failed = 0;
+       zft_resid -= written;
+       need_flush = need_flush || written > 0;
+       TRACE_EXIT written;               /* bytes written */
+}
diff --git a/drivers/char/ftape/zftape/zftape-write.h b/drivers/char/ftape/zftape/zftape-write.h
new file mode 100644 (file)
index 0000000..4a8d476
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef _ZFTAPE_WRITE_H
+#define _ZFTAPE_WRITE_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-write.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:13 $
+ *
+ *      This file contains the definitions for the write functions
+ *      for the zftape driver for Linux.
+ *
+ */
+
+extern int  zft_flush_buffers(void);
+extern int  zft_update_header_segments(void);
+extern void zft_prevent_flush(void);
+
+/*  hook for the VFS interface 
+ */
+extern int _zft_write(const char *buff, int req_len);
+#endif /* _ZFTAPE_WRITE_H */
diff --git a/drivers/char/ftape/zftape/zftape_syms.c b/drivers/char/ftape/zftape/zftape_syms.c
new file mode 100644 (file)
index 0000000..71b3175
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ *      Copyright (C) 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape_syms.c,v $
+ * $Revision: 1.3 $
+ * $Date: 1997/10/05 19:19:14 $
+ *
+ *      This file contains the the symbols that the zftape frontend to 
+ *      the ftape floppy tape driver exports 
+ */             
+
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/zftape.h>
+
+#include "../zftape/zftape-init.h"
+#include "../zftape/zftape-read.h"
+#include "../zftape/zftape-buffers.h"
+#include "../zftape/zftape-ctl.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,18)
+# define FT_KSYM(sym) EXPORT_SYMBOL(##sym);
+#else
+# define FT_KSYM(sym) X(##sym),
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+struct symbol_table zft_symbol_table = {
+#include <linux/symtab_begin.h>
+#endif
+/* zftape-init.c */
+FT_KSYM(zft_cmpr_register)
+FT_KSYM(zft_cmpr_unregister)
+/* zftape-read.c */
+FT_KSYM(zft_fetch_segment_fraction)
+/* zftape-buffers.c */
+FT_KSYM(zft_vmalloc_once)
+FT_KSYM(zft_vmalloc_always)
+FT_KSYM(zft_vfree)
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+#include <linux/symtab_end.h>
+};
+#endif
diff --git a/drivers/char/ftape/zftape/zftape_syms.h b/drivers/char/ftape/zftape/zftape_syms.h
new file mode 100644 (file)
index 0000000..26f8756
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _ZFTAPE_SYMS_H
+#define _ZFTAPE_SYMS_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape_syms.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:14 $
+ *
+ *      This file contains the definitions needed for the symbols
+ *      ftape exports
+ * 
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VER(2,1,18)
+#include <linux/module.h>
+/*      ftape-vfs.c defined global vars.
+ */
+
+extern struct symbol_table zft_symbol_table;
+#endif
+
+#endif
index ea5d4135674aa53b1fcdc1d1ab99f3b146e3978f..ef9af462b024680d13c2e268ddf758908cabc889 100644 (file)
@@ -622,10 +622,12 @@ static void init_r_port(int board, int aiop, int chan)
        rp_table[line] = info;
 }
 
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
 static int baud_table[] = {
        0, 50, 75, 110, 134, 150, 200, 300,
        600, 1200, 1800, 2400, 4800, 9600, 19200,
        38400, 57600, 115200, 230400, 460800, 0 };
+#endif
 
 /*
  * This routine configures a rocketport port so according to its
@@ -671,6 +673,7 @@ static void configure_r_port(struct r_port *info)
        }
        
        /* baud rate */
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
        i = cflag & CBAUD;
        if (i & CBAUDEX) {
                i &= ~CBAUDEX;
@@ -690,6 +693,11 @@ static void configure_r_port(struct r_port *info)
                        i += 4;
        }
        baud = baud_table[i] ? baud_table[i] : 9600;
+#else
+       baud = tty_get_baud_rate(info->tty);
+       if (!baud)
+               baud = 9600;
+#endif
        info->cps = baud / bits;
        sSetBaud(cp, (rp_baud_base/baud) - 1);
        
@@ -1182,7 +1190,7 @@ static void rp_set_termios(struct tty_struct *tty, struct termios *old_termios)
 /*
  * Here are the routines used by rp_ioctl
  */
-
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
 static void send_break(        struct r_port * info, int duration)
 {
        current->state = TASK_INTERRUPTIBLE;
@@ -1193,6 +1201,24 @@ static void send_break(  struct r_port * info, int duration)
        sClrBreak(&info->channel);
        sti();
 }
+#else
+static void rp_break(struct tty_struct *tty, int break_state)
+{
+       struct r_port * info = (struct r_port *)tty->driver_data;
+       unsigned long flags;
+       
+       if (rocket_paranoia_check(info, tty->device, "rp_break"))
+               return;
+
+       save_flags(flags); cli();
+       if (break_state == -1) {
+               sSendBreak(&info->channel);
+       } else {
+               sClrBreak(&info->channel);
+       }
+       restore_flags(flags);
+}
+#endif
 
 static int get_modem_info(struct r_port * info, unsigned int *value)
 {
@@ -1289,8 +1315,19 @@ static int set_config(struct r_port * info, struct rocket_config * new_info)
                        (new_serial.flags & ROCKET_FLAGS));
        info->close_delay = new_serial.close_delay;
        info->closing_wait = new_serial.closing_wait;
-       configure_r_port(info);
+
+#if (LINUX_VERSION_CODE >= 131393) /* Linux 2.1.65 */
+       if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
+               info->tty->alt_speed = 57600;
+       if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
+               info->tty->alt_speed = 115200;
+       if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
+               info->tty->alt_speed = 230400;
+       if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
+               info->tty->alt_speed = 460800;
+#endif
        
+       configure_r_port(info);
        return 0;
 }
 
@@ -1319,15 +1356,17 @@ static int get_ports(struct r_port * info, struct rocket_ports * retports)
 static int rp_ioctl(struct tty_struct *tty, struct file * file,
                    unsigned int cmd, unsigned long arg)
 {
-       int tmp;
        struct r_port * info = (struct r_port *)tty->driver_data;
-       int retval;
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
+       int retval, tmp;
+#endif
 
        if (cmd != RCKP_GET_PORTS &&
            rocket_paranoia_check(info, tty->device, "rp_ioctl"))
                return -ENODEV;
 
        switch (cmd) {
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
                case TCSBRK:    /* SVID version: non-zero arg --> no break */
                        retval = tty_check_change(tty);
                        if (retval)
@@ -1365,6 +1404,7 @@ static int rp_ioctl(struct tty_struct *tty, struct file * file,
                                ((tty->termios->c_cflag & ~CLOCAL) |
                                 (tmp ? CLOCAL : 0));
                        return 0;
+#endif
                case TIOCMGET:
                        return get_modem_info(info, (unsigned int *) arg);
                case TIOCMBIS:
@@ -2093,6 +2133,9 @@ __initfunc(int rp_init(void))
        rocket_driver.stop = rp_stop;
        rocket_driver.start = rp_start;
        rocket_driver.hangup = rp_hangup;
+#if (LINUX_VERSION_CODE >= 131393) /* Linux 2.1.65 */
+       rocket_driver.break_ctl = rp_break;
+#endif
 #if (LINUX_VERSION_CODE >= 131343)
        rocket_driver.send_xchar = rp_send_xchar;
        rocket_driver.wait_until_sent = rp_wait_until_sent;
index 5e8d14004f8939040c0b56d1836fbce2286161ee..077152b7eb158ef2f612d0da48ea1de47bb37f7e 100644 (file)
@@ -22,6 +22,9 @@
  *  1/97:  Extended dumb serial ports are a config option now.  
  *         Saves 4k.   Michael A. Griffith <grif@acm.org>
  * 
+ *  8/97: Fix bug in rs_set_termios with RTS
+ *        Stanislav V. Voronyi <stas@uanet.kharkov.ua>
+ *
  * This module exports the following rs232 io functions:
  *
  *     int rs_init(void);
@@ -319,13 +322,6 @@ static inline int serial_paranoia_check(struct async_struct *info,
        return 0;
 }
 
-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
-       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-       9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
-
 static inline unsigned int serial_in(struct async_struct *info, int offset)
 {
 #ifdef CONFIG_HUB6
@@ -541,7 +537,7 @@ static _INLINE_ void receive_chars(struct async_struct *info,
        ignore_char:
                *status = serial_inp(info, UART_LSR);
        } while (*status & UART_LSR_DR);
-       queue_task(&tty->flip.tqueue, &tq_timer);
+       tty_flip_buffer_push(tty);
 }
 
 static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
@@ -624,10 +620,10 @@ static _INLINE_ void check_modem_status(struct async_struct *info)
                else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
                           (info->flags & ASYNC_CALLOUT_NOHUP))) {
 #ifdef SERIAL_DEBUG_OPEN
-                       printk("scheduling hangup...");
+                       printk("doing serial hangup...");
 #endif
-                       queue_task(&info->tqueue_hangup,
-                                          &tq_scheduler);
+                       if (info->tty)
+                               tty_hangup(info->tty);
                }
        }
        if (info->flags & ASYNC_CTS_FLOW) {
@@ -907,28 +903,6 @@ static void do_softint(void *private_)
        }
 }
 
-/*
- * This routine is called from the scheduler tqueue when the interrupt
- * routine has signalled that a hangup has occurred.  The path of
- * hangup processing is:
- *
- *     serial interrupt routine -> (scheduler tqueue) ->
- *     do_serial_hangup() -> tty->hangup() -> rs_hangup()
- * 
- */
-static void do_serial_hangup(void *private_)
-{
-       struct async_struct     *info = (struct async_struct *) private_;
-       struct tty_struct       *tty;
-       
-       tty = info->tty;
-       if (!tty)
-               return;
-
-       tty_hangup(tty);
-}
-
-
 /*
  * This subroutine is called when the RS_TIMER goes off.  It is used
  * by the serial driver to handle ports that do not have an interrupt
@@ -1062,7 +1036,6 @@ static int startup(struct async_struct * info)
        unsigned short ICP;
 #endif
 
-
        page = get_free_page(GFP_KERNEL);
        if (!page)
                return -ENOMEM;
@@ -1365,9 +1338,9 @@ static void shutdown(struct async_struct * info)
 static void change_speed(struct async_struct *info)
 {
        unsigned short port;
-       int     quot = 0, baud_base;
+       int     quot = 0, baud_base, baud;
        unsigned cflag, cval, fcr = 0;
-       int     i, bits;
+       int     bits;
        unsigned long   flags;
 
        if (!info->tty || !info->tty->termios)
@@ -1401,37 +1374,20 @@ static void change_speed(struct async_struct *info)
 #endif
 
        /* Determine divisor based on baud rate */
-       i = cflag & CBAUD;
-       if (i & CBAUDEX) {
-               i &= ~CBAUDEX;
-               if (i < 1 || i > 4) 
-                       info->tty->termios->c_cflag &= ~CBAUDEX;
-               else
-                       i += 15;
-       }
-       if (i == 15) {
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                       i += 1;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                       i += 2;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-                       i += 3;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-                       i += 4;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
-                       quot = info->state->custom_divisor;
-       }
+       baud = tty_get_baud_rate(info->tty);
        baud_base = info->state->baud_base;
-       if (!quot) {
-               if (baud_table[i] == 134)
+       if (baud == 38400)
+               quot = info->state->custom_divisor;
+       else {
+               if (baud == 134)
                        /* Special case since 134 is really 134.5 */
                        quot = (2*baud_base / 269);
-               else if (baud_table[i])
-                       quot = baud_base / baud_table[i];
-               /* If the quotient is ever zero, default to 9600 bps */
-               if (!quot)
-                       quot = baud_base / 9600;
+               else if (baud)
+                       quot = baud_base / baud;
        }
+       /* If the quotient is ever zero, default to 9600 bps */
+       if (!quot)
+               quot = baud_base / 9600;
        info->quot = quot;
        info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
        info->timeout += HZ/50;         /* Add .02 seconds of slop */
@@ -1843,8 +1799,17 @@ check_and_exit:
        if (state->flags & ASYNC_INITIALIZED) {
                if (((old_state.flags & ASYNC_SPD_MASK) !=
                     (state->flags & ASYNC_SPD_MASK)) ||
-                   (old_state.custom_divisor != state->custom_divisor))
+                   (old_state.custom_divisor != state->custom_divisor)) {
+                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                               info->tty->alt_speed = 57600;
+                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                               info->tty->alt_speed = 115200;
+                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                               info->tty->alt_speed = 230400;
+                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                               info->tty->alt_speed = 460800;
                        change_speed(info);
+               }
        } else
                retval = startup(info);
        return retval;
@@ -1975,51 +1940,27 @@ static int do_autoconfig(struct async_struct * info)
        return 0;
 }
 
-
-/*
- * This routine sends a break character out the serial port.
- */
-static void send_break(        struct async_struct * info, int duration)
-{
-       if (!info->port)
-               return;
-       current->state = TASK_INTERRUPTIBLE;
-       current->timeout = jiffies + duration;
-#ifdef SERIAL_DEBUG_SEND_BREAK
-       printk("rs_send_break(%d) jiff=%lu...", duration, jiffies);
-#endif
-       cli();
-       serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
-       schedule();
-       serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
-       sti();
-#ifdef SERIAL_DEBUG_SEND_BREAK
-       printk("done jiffies=%lu\n", jiffies);
-#endif
-}
-
 /*
- * This routine sets the break condition on the serial port.
+ * rs_break() --- routine which turns the break handling on or off
  */
-static void begin_break(struct async_struct * info)
+static void rs_break(struct tty_struct *tty, int break_state)
 {
-       if (!info->port)
+       struct async_struct * info = (struct async_struct *)tty->driver_data;
+       unsigned long flags;
+       
+       if (serial_paranoia_check(info, tty->device, "rs_break"))
                return;
-       cli();
-       serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
-       sti();
-}
 
-/*
- * This routine clears the break condition on the serial port.
- */
-static void end_break(struct async_struct * info)
-{
        if (!info->port)
                return;
-       cli();
-       serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
-       sti();
+       save_flags(flags); cli();
+       if (break_state == -1)
+               serial_out(info, UART_LCR,
+                          serial_inp(info, UART_LCR) | UART_LCR_SBC);
+       else
+               serial_out(info, UART_LCR,
+                          serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+       restore_flags(flags);
 }
 
 /*
@@ -2186,7 +2127,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
 {
        int error;
        struct async_struct * info = (struct async_struct *)tty->driver_data;
-       int retval;
        struct async_icount cprev, cnow;        /* kernel counter temps */
        struct serial_icounter_struct *p_cuser; /* user space */
 
@@ -2202,53 +2142,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
        }
        
        switch (cmd) {
-               case TCSBRK:    /* SVID version: non-zero arg --> no break */
-                       retval = tty_check_change(tty);
-                       if (retval)
-                               return retval;
-                       tty_wait_until_sent(tty, 0);
-                       if (signal_pending(current))
-                               return -EINTR;
-                       if (!arg) {
-                               send_break(info, HZ/4); /* 1/4 second */
-                               if (signal_pending(current))
-                                       return -EINTR;
-                       }
-                       return 0;
-               case TCSBRKP:   /* support for POSIX tcsendbreak() */
-                       retval = tty_check_change(tty);
-                       if (retval)
-                               return retval;
-                       tty_wait_until_sent(tty, 0);
-                       if (signal_pending(current))
-                               return -EINTR;
-                       send_break(info, arg ? arg*(HZ/10) : HZ/4);
-                       if (signal_pending(current))
-                               return -EINTR;
-                       return 0;
-               case TIOCSBRK:
-                       retval = tty_check_change(tty);
-                       if (retval)
-                               return retval;
-                       tty_wait_until_sent(tty, 0);
-                       begin_break(info);
-                       return 0;
-               case TIOCCBRK:
-                       retval = tty_check_change(tty);
-                       if (retval)
-                               return retval;
-                       end_break(info);
-                       return 0;
-               case TIOCGSOFTCAR:
-                       return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
-               case TIOCSSOFTCAR:
-                       error = get_user(arg, (unsigned int *) arg);
-                       if (error)
-                               return error;
-                       tty->termios->c_cflag =
-                               ((tty->termios->c_cflag & ~CLOCAL) |
-                                (arg ? CLOCAL : 0));
-                       return 0;
                case TIOCMGET:
                        return get_modem_info(info, (unsigned int *) arg);
                case TIOCMBIS:
@@ -2379,8 +2272,8 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
        if (!(old_termios->c_cflag & CBAUD) &&
            (tty->termios->c_cflag & CBAUD)) {
                info->MCR |= UART_MCR_DTR;
-               if (!tty->hw_stopped ||
-                   !(tty->termios->c_cflag & CRTSCTS)) {
+               if (!(tty->termios->c_cflag & CRTSCTS) || 
+                   !test_bit(TTY_THROTTLED, &tty->flags)) {
                        info->MCR |= UART_MCR_RTS;
                }
                cli();
@@ -2617,10 +2510,8 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
                if (info->flags & ASYNC_CLOSING)
                        interruptible_sleep_on(&info->close_wait);
 #ifdef SERIAL_DO_RESTART
-               if (info->flags & ASYNC_HUP_NOTIFY)
-                       return -EAGAIN;
-               else
-                       return -ERESTARTSYS;
+               return ((info->flags & ASYNC_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS);
 #else
                return -EAGAIN;
 #endif
@@ -2758,8 +2649,6 @@ static int get_async_struct(int line, struct async_struct **ret_info)
        info->line = line;
        info->tqueue.routine = do_softint;
        info->tqueue.data = info;
-       info->tqueue_hangup.routine = do_serial_hangup;
-       info->tqueue_hangup.data = info;
        info->state = sstate;
        if (sstate->info) {
                kfree_s(info, sizeof(struct async_struct));
@@ -2807,7 +2696,22 @@ static int rs_open(struct tty_struct *tty, struct file * filp)
                else
                        tmp_buf = (unsigned char *) page;
        }
-       
+
+       /*
+        * If the port is the middle of closing, bail out now
+        */
+       if (tty_hung_up_p(filp) ||
+           (info->flags & ASYNC_CLOSING)) {
+               if (info->flags & ASYNC_CLOSING)
+                       interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+               return ((info->flags & ASYNC_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS);
+#else
+               return -EAGAIN;
+#endif
+       }
+
        /*
         * Start up serial port
         */
@@ -3201,7 +3105,6 @@ static void autoconfig(struct serial_state * state)
                scratch = serial_in(info, UART_IIR) >> 5;
                if (scratch == 7) {
                        serial_outp(info, UART_LCR, 0);
-                       serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
                        scratch = serial_in(info, UART_IIR) >> 5;
                        if (scratch == 6)
                                state->type = PORT_16750;
@@ -3316,6 +3219,7 @@ __initfunc(int rs_init(void))
        serial_driver.stop = rs_stop;
        serial_driver.start = rs_start;
        serial_driver.hangup = rs_hangup;
+       serial_driver.break_ctl = rs_break;
        serial_driver.wait_until_sent = rs_wait_until_sent;
        serial_driver.read_proc = rs_read_proc;
        
index 7fdbdf742c60ed7e769fa749cff3516ce391f025..bf22ca44d18d1855195c519e603cd07a26ccf06c 100644 (file)
@@ -366,14 +366,18 @@ static struct file_operations hung_up_tty_fops = {
        NULL            /* hung_up_tty_fasync */
 };
 
-void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
+void do_tty_hangup(void *data)
 {
-
+       struct tty_struct *tty = (struct tty_struct *) data;
        struct file * filp;
        struct task_struct *p;
+       unsigned long   flags;
 
        if (!tty)
                return;
+       
+       save_flags(flags); cli();
+       
        check_tty_count(tty, "do_tty_hangup");
        for (filp = inuse_filps; filp; filp = filp->f_next) {
                if (filp->private_data != tty)
@@ -387,7 +391,7 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
                if (filp->f_op != &tty_fops)
                        continue;
                tty_fasync(filp, 0);
-               filp->f_op = fops;
+               filp->f_op = &hung_up_tty_fops;
        }
        
        if (tty->ldisc.flush_buffer)
@@ -404,6 +408,8 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
         * Shutdown the current line discipline, and reset it to
         * N_TTY.
         */
+       if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
+               *tty->termios = tty->driver.init_termios;
        if (tty->ldisc.num != ldiscs[N_TTY].num) {
                if (tty->ldisc.close)
                        (tty->ldisc.close)(tty);
@@ -435,10 +441,9 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
        tty->session = 0;
        tty->pgrp = -1;
        tty->ctrl_status = 0;
-       if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
-               *tty->termios = tty->driver.init_termios;
        if (tty->driver.hangup)
                (tty->driver.hangup)(tty);
+       restore_flags(flags);
 }
 
 void tty_hangup(struct tty_struct * tty)
@@ -446,7 +451,7 @@ void tty_hangup(struct tty_struct * tty)
 #ifdef TTY_DEBUG_HANGUP
        printk("%s hangup...\n", tty_name(tty));
 #endif
-       do_tty_hangup(tty, &hung_up_tty_fops);
+       queue_task(&tty->tq_hangup, &tq_timer);
 }
 
 void tty_vhangup(struct tty_struct * tty)
@@ -454,7 +459,7 @@ void tty_vhangup(struct tty_struct * tty)
 #ifdef TTY_DEBUG_HANGUP
        printk("%s vhangup...\n", tty_name(tty));
 #endif
-       do_tty_hangup(tty, &hung_up_tty_fops);
+       do_tty_hangup((void *) tty);
 }
 
 int tty_hung_up_p(struct file * filp)
@@ -1490,15 +1495,26 @@ static int tiocsetd(struct tty_struct *tty, int *arg)
 {
        int retval, ldisc;
 
-       retval = tty_check_change(tty);
-       if (retval)
-               return retval;
        retval = get_user(ldisc, arg);
        if (retval)
                return retval;
        return tty_set_ldisc(tty, ldisc);
 }
 
+static int send_break(struct tty_struct *tty, int duration)
+{
+       current->state = TASK_INTERRUPTIBLE;
+       current->timeout = jiffies + duration;
+
+       tty->driver.break_ctl(tty, -1);
+       if (!signal_pending(current))
+               schedule();
+       tty->driver.break_ctl(tty, 0);
+       if (signal_pending(current))
+               return -EINTR;
+       return 0;
+}
+
 /*
  * Split this up, as gcc can choke on it otherwise..
  */
@@ -1506,6 +1522,7 @@ static int tty_ioctl(struct inode * inode, struct file * file,
                     unsigned int cmd, unsigned long arg)
 {
        struct tty_struct *tty, *real_tty;
+       int retval;
        
        tty = (struct tty_struct *)file->private_data;
        if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
@@ -1516,6 +1533,46 @@ static int tty_ioctl(struct inode * inode, struct file * file,
            tty->driver.subtype == PTY_TYPE_MASTER)
                real_tty = tty->link;
 
+       /*
+        * Break handling by driver
+        */
+       if (!tty->driver.break_ctl) {
+               switch(cmd) {
+               case TIOCSBRK:
+               case TIOCCBRK:
+                       return tty->driver.ioctl(tty, file, cmd, arg);
+                       
+               /* These two ioctl's always return success; even if */
+               /* the driver doesn't support them. */
+               case TCSBRK:
+               case TCSBRKP:                   
+                       retval = tty->driver.ioctl(tty, file, cmd, arg);
+                       if (retval == -ENOIOCTLCMD)
+                               retval = 0;
+                       return retval;
+               }
+       }
+
+       /*
+        * Factor out some common prep work
+        */
+       switch (cmd) {
+       case TIOCSETD:
+       case TIOCSBRK:
+       case TIOCCBRK:
+       case TCSBRK:
+       case TCSBRKP:                   
+               retval = tty_check_change(tty);
+               if (retval)
+                       return retval;
+               if (cmd != TIOCCBRK) {
+                       tty_wait_until_sent(tty, 0);
+                       if (signal_pending(current))
+                               return -EINTR;
+               }
+               break;
+       }
+
        switch (cmd) {
                case TIOCSTI:
                        return tiocsti(tty, (char *)arg);
@@ -1556,6 +1613,28 @@ static int tty_ioctl(struct inode * inode, struct file * file,
 #endif
                case TIOCTTYGSTRUCT:
                        return tiocttygstruct(tty, (struct tty_struct *) arg);
+
+               /*
+                * Break handling
+                */
+               case TIOCSBRK:  /* Turn break on, unconditionally */
+                       tty->driver.break_ctl(tty, -1);
+                       return 0;
+                       
+               case TIOCCBRK:  /* Turn break off, unconditionally */
+                       tty->driver.break_ctl(tty, 0);
+                       return 0;
+               case TCSBRK:   /* SVID version: non-zero arg --> no break */
+                       /*
+                        * XXX is the above comment correct, or the
+                        * code below correct?  Is this ioctl used at
+                        * all by anyone?
+                        */
+                       if (!arg)
+                               return send_break(tty, HZ/4);
+                       return 0;
+               case TCSBRKP:   /* support for POSIX tcsendbreak() */   
+                       return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
        }
        if (tty->driver.ioctl) {
                int retval = (tty->driver.ioctl)(tty, file, cmd, arg);
@@ -1630,13 +1709,14 @@ static void flush_to_ldisc(void *private_)
        unsigned char   *cp;
        char            *fp;
        int             count;
+       unsigned long flags;
 
        if (tty->flip.buf_num) {
                cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
                fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
                tty->flip.buf_num = 0;
 
-               cli();
+               save_flags(flags); cli();
                tty->flip.char_buf_ptr = tty->flip.char_buf;
                tty->flip.flag_buf_ptr = tty->flip.flag_buf;
        } else {
@@ -1644,21 +1724,56 @@ static void flush_to_ldisc(void *private_)
                fp = tty->flip.flag_buf;
                tty->flip.buf_num = 1;
 
-               cli();
+               save_flags(flags); cli();
                tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
                tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
        }
        count = tty->flip.count;
        tty->flip.count = 0;
-       sti();
+       restore_flags(flags);
        
-#if 0
-       if (count > tty->max_flip_cnt)
-               tty->max_flip_cnt = count;
-#endif
        tty->ldisc.receive_buf(tty, cp, fp, count);
 }
 
+/*
+ * Routine which returns the baud rate of the tty
+ */
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+       9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
+
+int tty_get_baud_rate(struct tty_struct *tty)
+{
+       unsigned int cflag, i;
+
+       cflag = tty->termios->c_cflag;
+
+       i = cflag & CBAUD;
+       if (i & CBAUDEX) {
+               i &= ~CBAUDEX;
+               if (i < 1 || i > 4) 
+                       tty->termios->c_cflag &= ~CBAUDEX;
+               else
+                       i += 15;
+       }
+       if (i==15 && tty->alt_speed)
+               return(tty->alt_speed);
+       
+       return baud_table[i];
+}
+
+void tty_flip_buffer_push(struct tty_struct *tty)
+{
+       if (tty->low_latency)
+               flush_to_ldisc((void *) tty);
+       else
+               queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
 /*
  * This subroutine initializes a tty structure.
  */
@@ -1673,6 +1788,8 @@ static void initialize_tty_struct(struct tty_struct *tty)
        tty->flip.tqueue.routine = flush_to_ldisc;
        tty->flip.tqueue.data = tty;
        tty->flip.pty_sem = MUTEX;
+       tty->tq_hangup.routine = do_tty_hangup;
+       tty->tq_hangup.data = tty;
 }
 
 /*
index 630b2ed4217d8dd4da65a9241300fcad0612b547..21c09384ec8d89b68dc5bfc9180361e5d9804382 100644 (file)
@@ -509,18 +509,15 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
                                tty->packet = 0;
                        return 0;
                }
-               /* These two ioctl's always return success; even if */
-               /* the driver doesn't support them. */
-               case TCSBRK: case TCSBRKP:
-                       retval = tty_check_change(tty);
+               case TIOCGSOFTCAR:
+                       return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
+               case TIOCSSOFTCAR:
+                       retval = get_user(arg, (unsigned int *) arg);
                        if (retval)
                                return retval;
-                       tty_wait_until_sent(tty, 0);
-                       if (signal_pending(current))
-                               return -EINTR;
-                       if (!tty->driver.ioctl)
-                               return 0;
-                       tty->driver.ioctl(tty, file, cmd, arg);
+                       tty->termios->c_cflag =
+                               ((tty->termios->c_cflag & ~CLOCAL) |
+                                (arg ? CLOCAL : 0));
                        return 0;
                default:
                        return -ENOIOCTLCMD;
index 79bf0a14ce415facb5c7764a99f1dcebde189901..0a4cdecd1be1d4ffda2ec021a2f50155c6d9561c 100644 (file)
@@ -254,8 +254,8 @@ struct pci_dev_info dev_info[] = {
        DEVICE( VIA,            VIA_82C585,     "VT 82C585 Apollo VP1/VPX"),
        DEVICE( VIA,            VIA_82C586_0,   "VT 82C586 Apollo ISA"),
        DEVICE( VIA,            VIA_82C595,     "VT 82C595 Apollo VP2"),
-       DEVICE( VIA,            VIA_82C416,     "VT 82C416MV"),
        DEVICE( VIA,            VIA_82C926,     "VT 82C926 Amazon"),
+       DEVICE( VIA,            VIA_82C416,     "VT 82C416MV"),
        DEVICE( VIA,            VIA_82C595_97,  "VT 82C595 Apollo VP2/97"),
        DEVICE( VIA,            VIA_82C586_2,   "VT 82C586 Apollo USB"),
        DEVICE( VIA,            VIA_82C586_3,   "VT 82C586B Apollo ACPI"),
index 53ad122031fc850df02cc5184198ce7dabe753c9..3c2f083bd5642428dd071445396e3233133f77e7 100644 (file)
@@ -275,7 +275,7 @@ int ppa_proc_info(char *buffer, char **start, off_t offset,
        len = length;
     return len;
 }
-/* end of ppa.c */
+
 static int device_check(int host_no);
 
 #if PPA_DEBUG > 0
@@ -1000,8 +1000,10 @@ static void ppa_interrupt(void *data)
     }
 #endif
 
-    ppa_disconnect(cmd->host->unique_id);
-    ppa_pb_release(cmd->host->unique_id);
+    if (cmd->SCp.phase > 1)
+       ppa_disconnect(cmd->host->unique_id);
+    if (cmd->SCp.phase > 0)
+       ppa_pb_release(cmd->host->unique_id);
     tmp->cur_cmd = 0;
     cmd->scsi_done(cmd);
     return;
index 349bc5a97c60954dbf14b70d08de78a33b47a24c..ea86dd17ed592657f83e323e3951e7adbb6ffae9 100644 (file)
@@ -10,7 +10,7 @@
 #ifndef _PPA_H
 #define _PPA_H
 
-#define   PPA_VERSION   "1.39"
+#define   PPA_VERSION   "1.39a"
 
 /* Use the following to enable certain chipset support
  * Default is PEDANTIC = 3
  * used with lp or other parport devices on the same parallel port.
  *             1997 by Andrea Arcangeli <arcangeli@mbox.queen.it>
  *                                                     [1.39]
+ *
+ * Little fix in ppa engine to ensure that ppa don' t release parport
+ * or disconnect in wrong cases.
+ *             1997 by Andrea Arcangeli <arcangeli@mbox.queen.it>
+ *                                                     [1.39a]
  */
 /* ------ END OF USER CONFIGURABLE PARAMETERS ----- */
 
index b2c96cb377fefbab97c078294396dff2b6977117..1c8ab164edca4e10215f04eafb92f4159c0b6525 100644 (file)
@@ -196,7 +196,7 @@ repeat:
                         */
                        if (wait && buffer_req(bh) && !buffer_locked(bh) &&
                            !buffer_dirty(bh) && !buffer_uptodate(bh)) {
-                               err = 1;
+                               err = -EIO;
                                continue;
                        }
 
@@ -288,7 +288,22 @@ asmlinkage int sys_sync(void)
  
 int file_fsync(struct file *filp, struct dentry *dentry)
 {
-       return fsync_dev(dentry->d_inode->i_dev);
+       struct inode * inode = dentry->d_inode;
+       struct super_block * sb;
+       kdev_t dev;
+
+       /* sync the inode to buffers */
+       write_inode_now(inode);
+
+       /* sync the superblock to buffers */
+       sb = inode->i_sb;
+       wait_on_super(sb);
+       if (sb->s_op && sb->s_op->write_super)
+               sb->s_op->write_super(sb);
+
+       /* .. finally sync the buffers to disk */
+       dev = inode->i_dev;
+       return sync_buffers(dev, 1);
 }
 
 asmlinkage int sys_fsync(unsigned int fd)
index 048b589bfbe5927dddee9fa46908640fb7dfe000..b5b531d8cb79a545a0572c753c2155f1442cfc0b 100644 (file)
@@ -96,10 +96,8 @@ int ext2_sync_file(struct file * file, struct dentry *dentry)
                 */
                goto skip;
 
-       if (inode->i_size > EXT2_NDIR_BLOCKS*blocksize) {
-               err = fsync_dev(inode->i_dev);
-               goto skip;
-       }
+       if (inode->i_size > EXT2_NDIR_BLOCKS*blocksize)
+               return file_fsync(file, dentry);
 
        for (wait=0; wait<=1; wait++)
        {
index e2b6ea60ede0a038fdca934cd304c998590a3162..667492fcdcfe09be3a42ede0b0deaff6fdc65c8c 100644 (file)
@@ -1122,20 +1122,21 @@ static secno bplus_lookup(struct inode *inode, struct bplus_header *b,
 
 static int hpfs_lookup(struct inode *dir, struct dentry *dentry)
 {
-       struct quad_buffer_head qbh;
+       const char *name = dentry->d_name.name;
+       int len = dentry->d_name.len;
        struct hpfs_dirent *de;
        struct inode *inode;
        ino_t ino;
-       const char *name = dentry->d_name.name;
-       int len = dentry->d_name.len;
        int retval;
+       struct quad_buffer_head qbh;
 
        /* In case of madness */
 
+       retval = -ENOTDIR;
        if (dir == 0)
-               return -ENOENT;
+               goto out;
        if (!S_ISDIR(dir->i_mode))
-               return -ENOENT;
+               goto out;
 
        /*
         * Read in the directory entry. "." is there under the name ^A^A .
@@ -1155,9 +1156,11 @@ static int hpfs_lookup(struct inode *dir, struct dentry *dentry)
         * This is not really a bailout, just means file not found.
         */
 
-       inode = NULL;
-       if (!de)
-               goto add_dentry;
+       if (!de) {
+               d_add(dentry, NULL);
+               retval = 0;
+               goto out;
+       }
 
        /*
         * Get inode number, what we're after.
@@ -1199,15 +1202,13 @@ static int hpfs_lookup(struct inode *dir, struct dentry *dentry)
                }
        }
 
-       /*
-        * Add the dentry, negative or otherwise.
-        */
-      add_dentry:
        d_add(dentry, inode);
        retval = 0;
 
       free4:
        brelse4(&qbh);
+
+      out:
        return retval;
 }
 
index 10c53b5134877811d051aa1ab65e7819ffb79093..c0ebedf912120e66715fe3fe18d9337e20a6221a 100644 (file)
@@ -42,18 +42,22 @@ static unsigned long nfsd_next_expire = 0;
 
 static int add_to_fhcache(struct dentry *, int);
 static int nfsd_d_validate(struct dentry *);
+struct dentry * lookup_inode(dev_t, ino_t, ino_t);
 
 static LIST_HEAD(fixup_head);
 static LIST_HEAD(path_inuse);
+static int nfsd_nr_fixups = 0;
 static int nfsd_nr_paths = 0;
 #define NFSD_MAX_PATHS 500
+#define NFSD_MAX_FIXUPAGE 60*HZ
 
 struct nfsd_fixup {
        struct list_head lru;
-       struct dentry *dentry;
-       unsigned long reftime;
+       ino_t   dir;
        ino_t   ino;
        dev_t   dev;
+       struct dentry *dentry;
+       unsigned long reftime;
 };
 
 struct nfsd_path {
@@ -65,6 +69,65 @@ struct nfsd_path {
        char    name[1];
 };
 
+static struct nfsd_fixup * find_cached_lookup(dev_t dev, ino_t dir, ino_t ino)
+{
+       struct list_head *tmp = fixup_head.next;
+
+       for (; tmp != &fixup_head; tmp = tmp->next) {
+               struct nfsd_fixup *fp;
+
+               fp = list_entry(tmp, struct nfsd_fixup, lru);
+               if (fp->ino != ino)
+                       continue;
+               if (fp->dir != dir)
+                       continue;
+               if (fp->dev != dev)
+                       continue;
+               list_del(tmp);
+               list_add(tmp, &fixup_head);
+               fp->reftime = jiffies;
+               return fp;
+       }
+       return NULL;
+}
+
+/*
+ * Save the dentry pointer from a successful lookup.
+ */
+static void add_to_lookup_cache(struct dentry *dentry, struct knfs_fh *fh)
+{
+       struct nfsd_fixup *fp;
+
+       fp = find_cached_lookup(fh->fh_dev, fh->fh_dirino, fh->fh_ino);
+       if (fp) {
+               fp->dentry = dentry;
+               return;
+       }
+
+       /*
+        * Add a new entry. The small race here is unimportant:
+        * if another task adds the same lookup, both entries
+        * will be consistent.
+        */
+       fp = kmalloc(sizeof(struct nfsd_fixup), GFP_KERNEL);
+       if (fp) {
+               fp->dir = fh->fh_dirino;
+               fp->ino = fh->fh_ino;
+               fp->dev = fh->fh_dev;
+               fp->dentry = dentry;
+               fp->reftime = jiffies;
+               list_add(&fp->lru, &fixup_head);
+               nfsd_nr_fixups++;
+       }
+}
+
+static void free_fixup_entry(struct nfsd_fixup *fp)
+{
+       list_del(&fp->lru);
+       kfree(fp);
+       nfsd_nr_fixups--;
+}
+
 /*
  * Copy a dentry's path into the specified buffer.
  */
@@ -210,98 +273,203 @@ static void free_path_entry(struct nfsd_path *pe)
 
 struct nfsd_getdents_callback {
        struct nfsd_dirent *dirent;
-       int found;
-       int checked;
+       ino_t dirino;           /* parent inode number */
+       int found;              /* dirent inode matched? */
+       int sequence;           /* sequence counter */
 };
 
 struct nfsd_dirent {
-       ino_t ino;
+       ino_t ino;              /* preset to desired entry */
        int len;
        char name[256];
 };
 
 /*
- * A custom filldir function to search for the specified inode.
+ * A rather strange filldir function to capture the inode number
+ * for the second entry (the parent inode) and the name matching
+ * the specified inode number.
  */
-static int filldir_ino(void * __buf, const char * name, int len, 
+static int filldir_one(void * __buf, const char * name, int len, 
                        off_t pos, ino_t ino)
 {
        struct nfsd_getdents_callback *buf = __buf;
        struct nfsd_dirent *dirent = buf->dirent;
        int result = 0;
 
-       buf->checked++;
-       if (ino == dirent->ino) {
-               buf->found = 1;
+       buf->sequence++;
+#ifdef NFSD_DEBUG_VERBOSE
+printk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);
+#endif
+       if (buf->sequence == 2) {
+               buf->dirino = ino;
+               goto out;
+       }
+       if (dirent->ino == ino) {
                dirent->len = len;
                memcpy(dirent->name, name, len);
                dirent->name[len] = 0;
+               buf->found = 1;
                result = -1;
        }
+out:
        return result;
 }
 
 /*
- * Search a directory for the specified inode number.
+ * Read a directory and return the parent inode number and the name
+ * of the specified entry. The dirent must be initialized with the
+ * inode number of the desired entry.
  */
-static int search_dir(struct dentry *parent, ino_t ino,
-                       struct nfsd_dirent *dirent)
+static int get_parent_ino(struct dentry *dentry, struct nfsd_dirent *dirent)
 {
-       struct inode *inode = parent->d_inode;
+       struct inode *dir = dentry->d_inode;
        int error;
        struct file file;
        struct nfsd_getdents_callback buffer;
 
        error = -ENOTDIR;
-       if (!inode || !S_ISDIR(inode->i_mode))
+       if (!dir || !S_ISDIR(dir->i_mode))
                goto out;
        error = -EINVAL;
-       if (!inode->i_op || !inode->i_op->default_file_ops)
+       if (!dir->i_op || !dir->i_op->default_file_ops)
                goto out;
-
        /*
         * Open the directory ...
         */
-       error = init_private_file(&file, parent, FMODE_READ);
+       error = init_private_file(&file, dentry, FMODE_READ);
        if (error)
                goto out;
        error = -EINVAL;
        if (!file.f_op->readdir)
                goto out_close;
 
-       /*
-        * Initialize the callback buffer.
-        */
-       dirent->ino = ino;
        buffer.dirent = dirent;
+       buffer.dirino = 0;
        buffer.found = 0;
-
+       buffer.sequence = 0;
        while (1) {
-               buffer.checked = 0;
-               error = file.f_op->readdir(&file, &buffer, filldir_ino);
+               int old_seq = buffer.sequence;
+               down(&dir->i_sem);
+               error = file.f_op->readdir(&file, &buffer, filldir_one);
+               up(&dir->i_sem);
                if (error < 0)
                        break;
 
                error = 0;
-               if (buffer.found) {
-#ifdef NFSD_DEBUG_VERBOSE
-printk("search_dir: found %s\n", dirent->name);
-#endif
+               if (buffer.found)
                        break;
-               }
                error = -ENOENT;
-               if (!buffer.checked)
+               if (old_seq == buffer.sequence)
                        break;
        }
+       dirent->ino = buffer.dirino;
 
 out_close:
        if (file.f_op->release)
-               file.f_op->release(inode, &file);
+               file.f_op->release(dir, &file);
 out:
        return error;
+}
+
+/*
+ * Look up a dentry given inode and parent inode numbers.
+ *
+ * This relies on the ability of a unix-like filesystem to return
+ * the parent inode of a directory as the ".." (second) entry.
+ *
+ * This could be further optimized if we had an efficient way of
+ * searching for a dentry given the inode: as we walk up the tree,
+ * it's likely that a dentry exists before we reach the root.
+ */
+struct dentry * lookup_inode(dev_t dev, ino_t dirino, ino_t ino)
+{
+       struct super_block *sb;
+       struct dentry *root, *dentry, *result;
+       struct inode *dir;
+       char *name;
+       unsigned long page;
+       int error;
+       struct nfsd_dirent dirent;
 
+       result = ERR_PTR(-ENOMEM);
+       page = __get_free_page(GFP_KERNEL);
+       if (!page)
+               goto out;
+
+       /*
+        * Get the root dentry for the device.
+        */
+       result = ERR_PTR(-ENOENT);
+       sb = get_super(dev);
+       if (!sb)
+               goto out_page;
+       root = dget(sb->s_root);
+
+       name = (char *) page + PAGE_SIZE;
+       *(--name) = 0;
+
+       /*
+        * Walk up the tree building the name string as we go.
+        * When we reach the root (ino == 2), get the dentry
+        * relative to the root dentry.
+        */
+       while (1) {
+               if (ino == 2) {
+                       if (*name == '/')
+                               name++;
+                       /*
+                        * Note: this dput()s the root dentry.
+                        */
+                       result = lookup_dentry(name, root, 0);
+                       goto out_page;
+               }
+
+               result = ERR_PTR(-ENOENT);
+               dir = iget(sb, dirino);
+               if (!dir)
+                       goto out_root;
+               dentry = d_alloc_root(dir, NULL);
+               if (!dentry)
+                       goto out_iput;
+
+               /*
+                * Get the name for this inode and the next parent inode.
+                */
+               dirent.ino = ino;
+               error = get_parent_ino(dentry, &dirent);
+               result = ERR_PTR(error);
+               dput(dentry);
+               if (error)
+                       goto out_root;
+               /*
+                * Prepend the name to the buffer.
+                */
+               name -= dirent.len;
+               memcpy(name, dirent.name, dirent.len);
+               *(--name) = '/';
+
+               /*
+                * Make sure we can't get caught in a loop ...
+                */
+               result = ERR_PTR(-EINVAL);
+               if (dirino == dirent.ino && dirino != 2) {
+printk("lookup_inode: loop detected, dirino=%ld, path=%s\n", dirino, name);    
+                       goto out_root;
+               }
+               ino = dirino;
+               dirino = dirent.ino;
+       }
+
+out_iput:
+       iput(dir);
+out_root:
+       dput(root);
+out_page:
+       free_page(page);
+out:
+       return result;
 }
-       
+
 /*
  * Find an entry in the cache matching the given dentry pointer.
  */
@@ -338,7 +506,8 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,empty->ino);
 #endif
        empty->dentry = NULL;   /* no dentry */
        /*
-        * Add the parent to the dir cache before releasing the dentry.
+        * Add the parent to the dir cache before releasing the dentry,
+        * and check whether to save a copy of the dentry's path.
         */
        if (dentry != dentry->d_parent) {
                struct dentry *parent = dget(dentry->d_parent);
@@ -346,12 +515,12 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,empty->ino);
                        nfsd_nr_verified++;
                else
                        dput(parent);
-       }
-       /*
-        * If we're expiring a directory, copy its path.
-        */
-       if (cache == NFSD_DIR_CACHE) {
-               add_to_path_cache(dentry);
+               /*
+                * If we're expiring a directory, copy its path.
+                */
+               if (cache == NFSD_DIR_CACHE) {
+                       add_to_path_cache(dentry);
+               }
        }
        dput(dentry);
        nfsd_nr_put++;
@@ -387,6 +556,7 @@ out:
  */
 static void expire_old(int cache, int age)
 {
+       struct list_head *tmp;
        struct fh_entry *fhe;
        int i;
 
@@ -402,6 +572,17 @@ printk("expire_old: expiring %s older than %d\n",
                        expire_fhe(fhe, cache);
        }
 
+       /*
+        * Remove old entries from the patch-up cache.
+        */
+       while ((tmp = fixup_head.prev) != &fixup_head) {
+               struct nfsd_fixup *fp;
+               fp = list_entry(tmp, struct nfsd_fixup, lru);
+               if ((jiffies - fp->reftime) < NFSD_MAX_FIXUPAGE)
+                       break;
+               free_fixup_entry(fp);
+       }
+
        /*
         * Trim the path cache ...
         */
@@ -484,6 +665,31 @@ static struct dentry *find_dentry_by_ino(dev_t dev, ino_t ino)
 #ifdef NFSD_DEBUG_VERBOSE
 printk("find_dentry_by_ino: looking for inode %ld\n", ino);
 #endif
+       /*
+        * Special case: inode number 2 is the root inode,
+        * so we can use the root dentry for the device.
+        */
+       if (ino == 2) {
+               struct super_block *sb = get_super(dev);
+               if (sb) {
+#ifdef NFSD_PARANOIA
+printk("find_dentry_by_ino: getting root dentry for %s\n", kdevname(dev));
+#endif
+                       if (sb->s_root) {
+                               dentry = dget(sb->s_root);
+                               goto out;
+                       } else {
+#ifdef NFSD_PARANOIA
+                               printk("find_dentry_by_ino: %s has no root??\n",
+                                       kdevname(dev));
+#endif
+                       }
+               }
+       }
+
+       /*
+        * Search the dentry cache ...
+        */
        fhe = find_fhe_by_ino(dev, ino);
        if (fhe) {
                dentry = dget(fhe->dentry);
@@ -545,13 +751,9 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
 #endif
                        goto out;
                }
-               if (inode->i_ino != fh->fh_ino || inode->i_dev != fh->fh_dev) {
-#ifdef NFSD_PARANOIA
-printk("find_dentry_in_fhcache: %s/%s mismatch, ino=%ld, fh_ino=%ld\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, fh->fh_ino);
-#endif
+               if (inode->i_ino != fh->fh_ino || inode->i_dev != fh->fh_dev)
                        goto out;
-               }
+
                fhe->dentry = NULL;
                fhe->ino = 0;
                fhe->dev = 0;
@@ -583,13 +785,17 @@ static struct dentry *lookup_by_inode(struct dentry *parent, ino_t ino)
        /*
         * Search the directory for the inode number.
         */
-       error = search_dir(parent, ino, &dirent);
+       dirent.ino = ino;
+       error = get_parent_ino(parent, &dirent);
        if (error) {
 #ifdef NFSD_PARANOIA
 printk("lookup_by_inode: ino %ld not found in %s\n", ino, parent->d_name.name);
 #endif
                goto no_entry;
        }
+#ifdef NFSD_PARANOIA
+printk("lookup_by_inode: found %s\n", dirent.name);
+#endif
 
        dentry = lookup_dentry(dirent.name, dget(parent), 0);
        if (!IS_ERR(dentry)) {
@@ -611,6 +817,20 @@ no_entry:
        dentry = NULL;
 out:
        return dentry;
+
+}
+
+/*
+ * Search the fix-up list for a dentry from a prior lookup.
+ */
+static struct dentry *nfsd_cached_lookup(struct knfs_fh *fh)
+{
+       struct nfsd_fixup *fp;
+
+       fp = find_cached_lookup(fh->fh_dev, fh->fh_dirino, fh->fh_ino);
+       if (fp)
+               return fp->dentry;
+       return NULL;
 }
 
 /*
@@ -633,62 +853,74 @@ static struct dentry *
 find_fh_dentry(struct knfs_fh *fh)
 {
        struct dentry *dentry, *parent;
+       int looked_up = 0, retry = 0;
 
        /*
         * Stage 1: Look for the dentry in the short-term fhcache.
         */
        dentry = find_dentry_in_fhcache(fh);
-       if (dentry) {
-#ifdef NFSD_DEBUG_VERBOSE
-printk("find_fh_dentry: found %s/%s, d_count=%d, ino=%ld\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count,fh->fh_ino);
-#endif
+       if (dentry)
                goto out;
-       }
 
        /*
         * Stage 2: Attempt to validate the dentry in the filehandle.
         */
        dentry = fh->fh_dcookie;
+recheck:
        if (nfsd_d_validate(dentry)) {
                struct inode * dir = dentry->d_parent->d_inode;
 
                if (dir->i_ino == fh->fh_dirino && dir->i_dev == fh->fh_dev) {
                        struct inode * inode = dentry->d_inode;
-
                        /*
                         * NFS filehandles must always have an inode,
                         * so we won't accept a negative dentry.
                         */
-                       if (!inode) {
-#ifdef NFSD_PARANOIA
-printk("find_fh_dentry: %s/%s negative, can't match %ld\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, fh->fh_ino);
-#endif
-                       } else if (inode->i_ino == fh->fh_ino &&
-                                  inode->i_dev == fh->fh_dev) {
+                       if (inode && inode->i_ino == fh->fh_ino) {
                                dget(dentry);
 #ifdef NFSD_DEBUG_VERBOSE
 printk("find_fh_dentry: validated %s/%s, ino=%ld\n",
 dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino);
+if (retry)
+printk("find_fh_dentry: retried validation successful\n");
 #endif
                                goto out;
-                       } else {
-#ifdef NFSD_PARANOIA
-printk("find_fh_dentry: %s/%s mismatch, ino=%ld, fh_ino=%ld\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, fh->fh_ino);
-#endif
                        }
-               } else {
+               }
+       }
+
+       /*
+        * Before proceeding to a lookup, check whether we cached a
+        * prior lookup. If so, try to validate that dentry ...
+        */
+       if (!retry && (dentry = nfsd_cached_lookup(fh)) != NULL) {
+               retry = 1;
+               goto recheck;
+       }
+
+       /*
+        * Stage 3: Look up the dentry based on the inode and parent inode
+        * numbers. This should work for all unix-like filesystems ...
+        */
+       looked_up = 1;
+       dentry = lookup_inode(fh->fh_dev, fh->fh_dirino, fh->fh_ino);
+       if (!IS_ERR(dentry)) {
+               struct inode * inode = dentry->d_inode;
+#ifdef NFSD_DEBUG_VERBOSE
+printk("find_fh_dentry: looked up %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+               if (inode && inode->i_ino == fh->fh_ino)
+                       goto out;
 #ifdef NFSD_PARANOIA
-printk("find_fh_dentry: %s/%s mismatch, parent ino=%ld, dirino=%ld\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, dir->i_ino, fh->fh_dirino);
+printk("find_fh_dentry: %s/%s lookup mismatch!\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
 #endif
-               }
+               dput(dentry);
        }
 
        /*
-        * Stage 3: Look for the parent dentry in the fhcache ...
+        * Stage 4: Look for the parent dentry in the fhcache ...
         */
        parent = find_dentry_by_ino(fh->fh_dev, fh->fh_dirino);
        if (parent) {
@@ -702,7 +934,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dir->i_ino, fh->fh_dirino);
        }
 
        /*
-        * Stage 4: Search the whole volume.
+        * Stage 5: Search the whole volume.
         */
 #ifdef NFSD_PARANOIA
 printk("find_fh_dentry: %s, %ld/%ld not found -- need full search!\n",
@@ -711,6 +943,10 @@ kdevname(fh->fh_dev), fh->fh_dirino, fh->fh_ino);
        dentry = NULL;
        
 out:
+       if (looked_up && dentry) {
+               add_to_lookup_cache(dentry, fh);
+       }
+
        /*
         * Perform any needed housekeeping ...
         * N.B. move this into one of the daemons ...
@@ -903,7 +1139,7 @@ static int nfsd_d_validate(struct dentry *dentry)
        unsigned long dent_addr = (unsigned long) dentry;
        unsigned long min_addr = PAGE_OFFSET;
        unsigned long max_addr = min_addr + (num_physpages << PAGE_SHIFT);
-       unsigned long align_mask = 0x1F;
+       unsigned long align_mask = 0x0F;
        unsigned int len;
        int valid = 0;
 
@@ -912,14 +1148,17 @@ static int nfsd_d_validate(struct dentry *dentry)
        if (dent_addr > max_addr - sizeof(struct dentry))
                goto bad_addr;
        if ((dent_addr & ~align_mask) != dent_addr)
-               goto bad_addr;
+               goto bad_align;
        /*
         * Looks safe enough to dereference ...
         */
        len = dentry->d_name.len;
        if (len > NFS_MAXNAMLEN)
                goto bad_length;
-
+       /*
+        * Note: d_validate doesn't dereference the parent pointer ...
+        * just combines it with the name hash to find the hash chain.
+        */
        valid = d_validate(dentry, dentry->d_parent, dentry->d_name.hash, len);
 
 out:
@@ -928,6 +1167,9 @@ out:
 bad_addr:
        printk("nfsd_d_validate: invalid address %lx\n", dent_addr);
        goto out;
+bad_align:
+       printk("nfsd_d_validate: unaligned address %lx\n", dent_addr);
+       goto out;
 bad_length:
        printk("nfsd_d_validate: invalid length %d\n", len);
        goto out;
@@ -955,9 +1197,22 @@ void nfsd_fh_free(void)
                fhe = &dirstable[0];
        }
 
+       /*
+        * N.B. write a destructor for these lists ...
+        */
+       i = 0;
+       while ((tmp = fixup_head.next) != &fixup_head) {
+               struct nfsd_fixup *fp;
+               fp = list_entry(tmp, struct nfsd_fixup, lru);
+               free_fixup_entry(fp);
+               i++;
+       }
+       printk("nfsd_fh_free: %d fixups freed\n", i);
+
        i = 0;
        while ((tmp = path_inuse.next) != &path_inuse) {
-               struct nfsd_path *pe = list_entry(tmp, struct nfsd_path, lru);
+               struct nfsd_path *pe;
+               pe = list_entry(tmp, struct nfsd_path, lru);
                free_path_entry(pe);
                i++;
        }
@@ -973,6 +1228,8 @@ nfsd_fh_init(void)
        memset(filetable, 0, NFSD_MAXFH*sizeof(struct fh_entry));
        memset(dirstable, 0, NFSD_MAXFH*sizeof(struct fh_entry));
        INIT_LIST_HEAD(&path_inuse);
+       INIT_LIST_HEAD(&fixup_head);
 
        printk("nfsd_init: initialized fhcache, entries=%lu\n", NFSD_MAXFH);
 }
+
index e33d38cda43c00539bf24e0853e83738c6c847eb..292493dacb18049afb06bb9f5775f624b0a6647f 100644 (file)
@@ -265,7 +265,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, cachep->pages);
         * Fill the cache, starting at position 2.
         */
 retry:
-       inode->u.smbfs_i.cache_valid = 1;
+       inode->u.smbfs_i.cache_valid |= SMB_F_CACHEVALID;
        result = smb_proc_readdir(dentry, 2, cachep);
        if (result < 0)
        {
@@ -279,7 +279,7 @@ printk("smb_refill_dircache: readdir failed, result=%d\n", result);
         * Check whether the cache was invalidated while
         * we were doing the scan ...
         */
-       if (!inode->u.smbfs_i.cache_valid)
+       if (!(inode->u.smbfs_i.cache_valid & SMB_F_CACHEVALID))
        {
 #ifdef SMBFS_PARANOIA
 printk("smb_refill_dircache: cache invalidated, retrying\n");
@@ -310,6 +310,7 @@ smb_invalid_dir_cache(struct inode * dir)
         * 'valid' flag in case a scan is in progress.
         */
        invalidate_inode_pages(dir);
-       dir->u.smbfs_i.cache_valid = 0;
+       dir->u.smbfs_i.cache_valid &= ~SMB_F_CACHEVALID;
+       dir->u.smbfs_i.oldmtime = 0;
 }
 
index 6b3ce2907c3d4f8f03e71a47fa824a6be94ecbcc..921aa86e10c1b01a46796da47326699b5b614071 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
+
 #include <linux/smb_fs.h>
 #include <linux/smbno.h>
 
@@ -62,7 +63,9 @@ struct inode_operations smb_dir_inode_operations =
        NULL,                   /* bmap */
        NULL,                   /* truncate */
        NULL,                   /* permission */
-       NULL                    /* smap */
+       NULL,                   /* smap */
+       NULL,                   /* updatepage */
+       smb_revalidate_inode,   /* revalidate */
 };
 
 static ssize_t
@@ -72,42 +75,25 @@ smb_dir_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
 }
 
 /*
- * Compute the hash for a qstr.
- * N.B. Move to include/linux/dcache.h?
+ * Check whether a dentry already exists for the given name,
+ * and return the inode number if it has an inode.  This is
+ * needed to keep getcwd() working.
  */
-static unsigned int 
-hash_it(const char * name, unsigned int len)
+static ino_t
+find_inode_number(struct dentry *dir, struct qstr *name)
 {
-       unsigned long hash = init_name_hash();
-       while (len--)
-               hash = partial_name_hash(*name++, hash);
-       return end_name_hash(hash);
-}
+       struct dentry * dentry;
+       ino_t ino = 0;
 
-/*
- * If a dentry already exists, we have to give the cache entry
- * the correct inode number.  This is needed for getcwd().
- */
-static void
-smb_find_ino(struct dentry *dentry, struct cache_dirent *entry)
-{
-       struct dentry * new_dentry;
-       struct qstr qname;
-
-       /* N.B. Make cache_dirent name a qstr! */
-       qname.name = entry->name;
-       qname.len  = entry->len;
-       qname.hash = hash_it(qname.name, qname.len);
-       new_dentry = d_lookup(dentry, &qname);
-       if (new_dentry)
+       name->hash = full_name_hash(name->name, name->len);
+       dentry = d_lookup(dir, name);
+       if (dentry)
        {
-               struct inode * inode = new_dentry->d_inode;
-               if (inode)
-                       entry->ino = inode->i_ino;
-               dput(new_dentry);
+               if (dentry->d_inode)
+                       ino = dentry->d_inode->i_ino;
+               dput(dentry);
        }
-       if (!entry->ino)
-               entry->ino = smb_invent_inos(1);
+       return ino;
 }
 
 static int 
@@ -168,8 +154,15 @@ dentry->d_parent->d_name.name, dentry->d_name.name, (int) filp->f_pos);
                /*
                 * Check whether to look up the inode number.
                 */
-               if (!entry->ino)
-                       smb_find_ino(dentry, entry);
+               if (!entry->ino) {
+                       struct qstr qname;
+                       /* N.B. Make cache_dirent name a qstr! */
+                       qname.name = entry->name;
+                       qname.len  = entry->len;
+                       entry->ino = find_inode_number(dentry, &qname);
+                       if (!entry->ino)
+                               entry->ino = smb_invent_inos(1);
+               }
 
                if (filldir(dirent, entry->name, entry->len, 
                                    filp->f_pos, entry->ino) < 0)
@@ -324,9 +317,9 @@ dentry->d_parent->d_name.name, dentry->d_name.name, error);
                goto add_entry;
        if (!error)
        {
+               error = -EACCES;
                finfo.f_ino = smb_invent_inos(1);
                inode = smb_iget(dir->i_sb, &finfo);
-               error = -EACCES;
                if (inode)
                {
                        /* cache the dentry pointer */
@@ -346,38 +339,58 @@ out:
  * This code is common to all routines creating a new inode.
  */
 static int
-smb_instantiate(struct dentry *dentry)
+smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id)
 {
+       struct smb_sb_info *server = server_from_dentry(dentry);
+       struct inode *inode;
        struct smb_fattr fattr;
        int error;
 
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_instantiate: file %s/%s, fileid=%u\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, fileid);
+#endif
        error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr);
-       if (!error)
+       if (error)
+               goto out_close;
+
+       smb_renew_times(dentry);
+       fattr.f_ino = smb_invent_inos(1);
+       inode = smb_iget(dentry->d_sb, &fattr);
+       if (!inode)
+               goto out_no_inode;
+
+       if (have_id)
        {
-               struct inode *inode;
-               error = -EACCES;
-               fattr.f_ino = smb_invent_inos(1);
-               inode = smb_iget(dentry->d_sb, &fattr);
-               if (inode)
-               {
-                       /* cache the dentry pointer */
-                       inode->u.smbfs_i.dentry = dentry;
-                       d_instantiate(dentry, inode);
-                       smb_renew_times(dentry);
-                       error = 0;
-               }
+               inode->u.smbfs_i.fileid = fileid;
+               inode->u.smbfs_i.access = SMB_O_RDWR;
+               inode->u.smbfs_i.open = server->generation;
        }
-#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_instantiate: file %s/%s, error=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, error);
-#endif
+       /* cache the dentry pointer */
+       inode->u.smbfs_i.dentry = dentry;
+       d_instantiate(dentry, inode);
+out:
        return error;
+
+out_no_inode:
+       error = -EACCES;
+out_close:
+       if (have_id)
+       {
+#ifdef SMBFS_PARANOIA
+printk("smb_instantiate: %s/%s failed, error=%d, closing %u\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, error, fileid);
+#endif
+               smb_close_fileid(dentry, fileid);
+       }
+       goto out;
 }
 
-/* N.B. Should the mode argument be put into the fattr? */
+/* N.B. How should the mode argument be used? */
 static int
 smb_create(struct inode *dir, struct dentry *dentry, int mode)
 {
+       __u16 fileid;
        int error;
 
 #ifdef SMBFS_DEBUG_VERBOSE
@@ -388,22 +401,23 @@ dentry->d_parent->d_name.name, dentry->d_name.name, mode);
        if (dentry->d_name.len > SMB_MAXNAMELEN)
                goto out;
 
-       /* FIXME: In the CIFS create call we get the file in open
-         * state. Currently we close it directly again, although this
-        * is not necessary anymore. */
-
        smb_invalid_dir_cache(dir);
        error = smb_proc_create(dentry->d_parent, &(dentry->d_name), 
-                               0, CURRENT_TIME);
-       if (!error)
+                               0, CURRENT_TIME, &fileid);
+       if (!error) {
+               error = smb_instantiate(dentry, fileid, 1);
+       } else
        {
-               error = smb_instantiate(dentry);
+#ifdef SMBFS_PARANOIA
+printk("smb_create: %s/%s failed, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, error);
+#endif
        }
 out:
        return error;
 }
 
-/* N.B. Should the mode argument be put into the fattr? */
+/* N.B. How should the mode argument be used? */
 static int
 smb_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
@@ -417,7 +431,7 @@ smb_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        error = smb_proc_mkdir(dentry->d_parent, &(dentry->d_name));
        if (!error)
        {
-               error = smb_instantiate(dentry);
+               error = smb_instantiate(dentry, 0, 0);
        }
 out:
        return error;
@@ -436,8 +450,17 @@ smb_rmdir(struct inode *dir, struct dentry *dentry)
         * Since the dentry is holding an inode, the file
         * is in use, so we have to close it first.
         */
-       if (dentry->d_inode)
-               smb_close(dentry->d_inode);
+       smb_close(dentry->d_inode);
+
+       /*
+        * Prune any child dentries so this dentry can become negative.
+        */
+       if (dentry->d_count > 1) {
+               shrink_dcache_parent(dentry);
+               error = -EBUSY;
+               if (dentry->d_count > 1)
+                       goto out;
+       }
 
        smb_invalid_dir_cache(dir);
        error = smb_proc_rmdir(dentry->d_parent, &(dentry->d_name));
@@ -463,8 +486,7 @@ smb_unlink(struct inode *dir, struct dentry *dentry)
         * Since the dentry is holding an inode, the file
         * is in use, so we have to close it first.
         */
-       if (dentry->d_inode)
-               smb_close(dentry->d_inode);
+       smb_close(dentry->d_inode);
 
        smb_invalid_dir_cache(dir);
        error = smb_proc_unlink(dentry->d_parent, &(dentry->d_name));
index cb0a2e89ee337c0beca5fb838711be9b24810129..2454fdf8ef913796e6c77e7bb6636021401f418b 100644 (file)
 #include <linux/fcntl.h>
 #include <linux/stat.h>
 #include <linux/mm.h>
-#include <linux/smb_fs.h>
 #include <linux/malloc.h>
 #include <linux/pagemap.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
 
+#include <linux/smbno.h>
+#include <linux/smb_fs.h>
+
 #define SMBFS_PARANOIA 1
 /* #define SMBFS_DEBUG_VERBOSE 1 */
 /* #define pr_debug printk */
 
-extern int smb_get_rsize(struct smb_sb_info *);
-extern int smb_get_wsize(struct smb_sb_info *);
-
 static inline int
 min(int a, int b)
 {
@@ -75,9 +74,15 @@ smb_readpage_sync(struct inode *inode, struct page *page)
 printk("smb_readpage_sync: file %s/%s, count=%d@%ld, rsize=%d\n",
 dentry->d_parent->d_name.name, dentry->d_name.name, count, offset, rsize);
 #endif
-       result = smb_open(dentry, O_RDONLY);
+       result = smb_open(dentry, SMB_O_RDONLY);
        if (result < 0)
+       {
+#ifdef SMBFS_PARANOIA
+printk("smb_readpage_sync: %s/%s open failed, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, result);
+#endif
                goto io_error;
+       }
 
        do {
                if (count < rsize)
@@ -90,6 +95,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name, count, offset, rsize);
                count -= result;
                offset += result;
                buffer += result;
+               inode->i_atime = CURRENT_TIME;
                if (result < rsize)
                        break;
        } while (count);
@@ -130,7 +136,7 @@ smb_writepage_sync(struct inode *inode, struct page *page,
 {
        u8 *buffer = (u8 *) page_address(page) + offset;
        int wsize = smb_get_wsize(SMB_SERVER(inode));
-       int result, refresh = 0, written = 0;
+       int result, written = 0;
 
        offset += page->offset;
 #ifdef SMBFS_DEBUG_VERBOSE
@@ -145,30 +151,33 @@ printk("smb_writepage_sync: file %s/%s, count=%d@%ld, wsize=%d\n",
 
                result = smb_proc_write(inode, offset, wsize, buffer);
                if (result < 0)
-               {
-                       /* Must mark the page invalid after I/O error */
-                       clear_bit(PG_uptodate, &page->flags);
                        goto io_error;
-               }
                /* N.B. what if result < wsize?? */
 #ifdef SMBFS_PARANOIA
 if (result < wsize)
 printk("smb_writepage_sync: short write, wsize=%d, result=%d\n", wsize, result);
 #endif
-               refresh = 1;
                buffer += wsize;
                offset += wsize;
                written += wsize;
                count -= wsize;
+               /*
+                * Update the inode now rather than waiting for a refresh.
+                */
+               inode->i_mtime = inode->i_atime = CURRENT_TIME;
+               if (offset > inode->i_size)
+                       inode->i_size = offset;
+               inode->u.smbfs_i.cache_valid |= SMB_F_LOCALWRITE;
        } while (count);
 
-io_error:
-#if 0
-       if (refresh)
-               smb_refresh_inode(inode);
-#endif
+out:
        smb_unlock_page(page);
        return written ? written : result;
+
+io_error:
+       /* Must mark the page invalid after I/O error */
+       clear_bit(PG_uptodate, &page->flags);
+       goto out;
 }
 
 /*
@@ -230,19 +239,32 @@ page_addr, offset, buffer);
 static ssize_t
 smb_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
 {
+       struct dentry * dentry = file->f_dentry;
+       struct inode * inode = dentry->d_inode;
        ssize_t status;
 
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_file_read: file %s/%s, count=%lu@%lu\n",
-file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name,
-count, (unsigned long) *ppos);
+dentry->d_parent->d_name.name, dentry->d_name.name,
+(unsigned long) count, (unsigned long) *ppos);
 #endif
 
-       status = smb_revalidate_inode(file->f_dentry->d_inode);
-       if (status >= 0)
+       status = smb_revalidate_inode(inode);
+       if (status)
        {
-               status = generic_file_read(file, buf, count, ppos);
+#ifdef SMBFS_PARANOIA
+printk("smb_file_read: %s/%s validation failed, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, status);
+#endif
+               goto out;
        }
+
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_file_read: before read, size=%ld, pages=%ld, flags=%x, atime=%ld\n",
+inode->i_size, inode->i_nrpages, inode->i_flags, inode->i_atime);
+#endif
+       status = generic_file_read(file, buf, count, ppos);
+out:
        return status;
 }
 
@@ -255,14 +277,21 @@ smb_file_mmap(struct file * file, struct vm_area_struct * vma)
 
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_file_mmap: file %s/%s, address %lu - %lu\n",
-file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name,
+dentry->d_parent->d_name.name, dentry->d_name.name,
 vma->vm_start, vma->vm_end);
 #endif
+
        status = smb_revalidate_inode(inode);
-       if (status >= 0)
+       if (status)
        {
-               status = generic_file_mmap(file, vma);
+#ifdef SMBFS_PARANOIA
+printk("smb_file_mmap: %s/%s validation failed, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, status);
+#endif
+               goto out;
        }
+       status = generic_file_mmap(file, vma);
+out:
        return status;
 }
 
@@ -272,38 +301,37 @@ vma->vm_start, vma->vm_end);
 static ssize_t
 smb_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
+       struct dentry * dentry = file->f_dentry;
+       struct inode * inode = dentry->d_inode;
        ssize_t result;
 
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_file_write: file %s/%s, count=%lu@%lu\n",
-file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name,
-count, (unsigned long) *ppos);
+dentry->d_parent->d_name.name, dentry->d_name.name,
+(unsigned long) count, (unsigned long) *ppos);
 #endif
 
-#ifdef SMBFS_PARANOIA
-       /* Should be impossible now that inodes can't change mode */
-       result = -EINVAL;
-       if (!S_ISREG(file->f_dentry->d_inode->i_mode))
+       result = smb_revalidate_inode(inode);
+       if (result)
        {
-               printk("smb_file_write: write to non-file, mode %07o\n",
-                      file->f_dentry->d_inode->i_mode);
-               goto out;
-       }
+#ifdef SMBFS_PARANOIA
+printk("smb_file_write: %s/%s validation failed, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, result);
 #endif
+                       goto out;
+       }
 
-       result = smb_revalidate_inode(file->f_dentry->d_inode);
-       if (result)
-               goto out;
-
-       result = smb_open(file->f_dentry, O_WRONLY);
+       result = smb_open(dentry, SMB_O_WRONLY);
        if (result)
                goto out;
 
        if (count > 0)
        {
                result = generic_file_write(file, buf, count, ppos);
-               if (result > 0)
-                       smb_refresh_inode(file->f_dentry->d_inode);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_file_write: pos=%ld, size=%ld, mtime=%ld, atime=%ld\n",
+(long) file->f_pos, inode->i_size, inode->i_mtime, inode->i_atime);
+#endif
        }
 out:
        return result;
@@ -313,7 +341,9 @@ static int
 smb_file_open(struct inode *inode, struct file * file)
 {
 #ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_file_open: inode=%p, file=%p\n", inode, file);
+printk("smb_file_open: opening %s/%s, d_count=%d\n",
+file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name,
+file->f_dentry->d_count);
 #endif
        return 0;
 }
@@ -324,7 +354,7 @@ smb_file_release(struct inode *inode, struct file * file)
        struct dentry * dentry = file->f_dentry;
 
 #ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_file_release: closing file %s/%s, d_count=%d\n",
+printk("smb_file_release: closing %s/%s, d_count=%d\n",
 dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
 #endif
        
@@ -335,6 +365,27 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
        return 0;
 }
 
+/*
+ * Check whether the required access is compatible with
+ * an inode's permission. SMB doesn't recognize superuser
+ * privileges, so we need our own check for this.
+ */
+static int
+smb_file_permission(struct inode *inode, int mask)
+{
+       int mode = inode->i_mode;
+       int error = 0;
+
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_file_permission: mode=%x, mask=%x\n", mode, mask);
+#endif
+       /* Look at user permissions */
+       mode >>= 6;
+       if ((mode & 7 & mask) != mask)
+               error = -EACCES;
+       return error;
+}
+
 static struct file_operations smb_file_operations =
 {
        NULL,                   /* lseek - default */
@@ -371,7 +422,7 @@ struct inode_operations smb_file_inode_operations =
        smb_writepage,          /* writepage */
        NULL,                   /* bmap */
        NULL,                   /* truncate */
-       NULL,                   /* permission */
+       smb_file_permission,    /* permission */
        NULL,                   /* smap */
        smb_updatepage,         /* updatepage */
        smb_revalidate_inode,   /* revalidate */
index 9fa49bd5d4bea82dc4318f1a77504882e13a015c..b2189286330405259e01a4b42bd3b97cb5ee711f 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/malloc.h>
 #include <linux/init.h>
 #include <linux/dcache.h>
+
 #include <linux/smb_fs.h>
 #include <linux/smbno.h>
 #include <linux/smb_mount.h>
@@ -83,6 +84,26 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr)
        return result;
 }
 
+/*
+ * Copy the inode data to a smb_fattr structure.
+ */
+void
+smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr)
+{
+       memset(fattr, 0, sizeof(struct smb_fattr));
+       fattr->f_mode   = inode->i_mode;
+       fattr->f_nlink  = inode->i_nlink;
+       fattr->f_uid    = inode->i_uid;
+       fattr->f_gid    = inode->i_gid;
+       fattr->f_rdev   = inode->i_rdev;
+       fattr->f_size   = inode->i_size;
+       fattr->f_mtime  = inode->i_mtime;
+       fattr->f_ctime  = inode->i_ctime;
+       fattr->f_atime  = inode->i_atime;
+       fattr->f_blksize= inode->i_blksize;
+       fattr->f_blocks = inode->i_blocks;
+}
+
 static void
 smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr)
 {
@@ -152,6 +173,16 @@ smb_revalidate_inode(struct inode *inode)
        int error = 0;
 
        pr_debug("smb_revalidate_inode\n");
+       /*
+        * If this is a file opened with write permissions,
+        * the inode will be up-to-date.
+        */
+       if (S_ISREG(inode->i_mode) && smb_is_open(inode)) {
+               if (inode->u.smbfs_i.access == SMB_O_RDWR ||
+                   inode->u.smbfs_i.access == SMB_O_WRONLY)
+                       goto out;
+       }
+
        /*
         * Check whether we've recently refreshed the inode.
         */
@@ -245,7 +276,6 @@ inode->i_mode, fattr.f_mode);
                        fattr.f_mode = inode->i_mode; /* save mode */
                        make_bad_inode(inode);
                        inode->i_mode = fattr.f_mode; /* restore mode */
-                       inode->i_nlink = 0;
                        /*
                         * No need to worry about unhashing the dentry: the
                         * lookup validation will see that the inode is bad.
@@ -358,6 +388,7 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
        sb->s_blocksize = 1024; /* Eh...  Is this correct? */
        sb->s_blocksize_bits = 10;
        sb->s_magic = SMB_SUPER_MAGIC;
+       sb->s_flags = 0;
        sb->s_dev = dev; /* shouldn't need this ... */
        sb->s_op = &smb_sops;
 
@@ -376,15 +407,23 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
        if (!mnt)
                goto out_no_mount;
        *mnt = *data;
-       mnt->version = 0; /* dynamic flags */
+       /* ** temp ** pass config flags in file mode */
+       mnt->version = (mnt->file_mode >> 9);
 #ifdef CONFIG_SMB_WIN95
-       mnt->version |= 1;
+       mnt->version |= SMB_FIX_WIN95;
 #endif
        mnt->file_mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
        mnt->file_mode |= S_IFREG;
        mnt->dir_mode  &= (S_IRWXU | S_IRWXG | S_IRWXO);
        mnt->dir_mode  |= S_IFDIR;
        sb->u.smbfs_sb.mnt = mnt;
+       /*
+        * Display the enabled options
+        */
+       if (mnt->version & SMB_FIX_WIN95)
+               printk("SMBFS: Win 95 bug fixes enabled\n");
+       if (mnt->version & SMB_FIX_OLDATTR)
+               printk("SMBFS: Using core getattr (Win 95 speedup)\n");
 
        /*
         * Keep the super block locked while we get the root inode.
@@ -457,9 +496,6 @@ smb_notify_change(struct inode *inode, struct iattr *attr)
                goto out;
        }
 
-       /*
-        * Make sure our inode is up-to-date ...
-        */
        error = smb_revalidate_inode(inode);
        if (error)
                goto out;
@@ -506,13 +542,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name,
        {
                struct smb_fattr fattr;
 
-               fattr.attr = 0;
-               fattr.f_size = inode->i_size;
-               fattr.f_blksize = inode->i_blksize;
-               fattr.f_ctime = inode->i_ctime;
-               fattr.f_mtime = inode->i_mtime;
-               fattr.f_atime = inode->i_atime;
-
+               smb_get_inode_attr(inode, &fattr);
                if ((attr->ia_valid & ATTR_CTIME) != 0)
                        fattr.f_ctime = attr->ia_ctime;
 
@@ -531,16 +561,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name,
 
 out:
        if (refresh)
-       {
-               /*
-                * N.B. Currently we're only using the dir cache for
-                * file names, so we don't need to invalidate here.
-                */
-#if 0
-               smb_invalid_dir_cache(dentry->d_parent->d_inode);
-#endif
                smb_refresh_inode(inode);
-       }
        return error;
 }
 
index af0bc12c07c87216578f28b1edc5dd5e2abdc750..966ee1e27bcde247a514448e4156039704d28f4d 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/fcntl.h>
 #include <linux/dcache.h>
 #include <linux/dirent.h>
+
 #include <linux/smb_fs.h>
 #include <linux/smbno.h>
 #include <linux/smb_mount.h>
@@ -24,6 +25,7 @@
 #include <asm/string.h>
 
 #define SMBFS_PARANOIA 1
+/* #define SMBFS_DEBUG_TIMESTAMP 1 */
 /* #define SMBFS_DEBUG_VERBOSE 1 */
 /* #define pr_debug printk */
 
@@ -36,6 +38,9 @@
 #define SMB_DIRINFO_SIZE 43
 #define SMB_STATUS_SIZE  21
 
+static int smb_proc_setfile_trans2(struct smb_sb_info *, struct inode *,
+                               struct smb_fattr *);
+
 static inline int
 min(int a, int b)
 {
@@ -171,24 +176,25 @@ static int day_n[] =
 
 extern struct timezone sys_tz;
 
-static int
-utc2local(int time)
+static time_t
+utc2local(time_t time)
 {
        return time - sys_tz.tz_minuteswest * 60;
 }
 
-static int
-local2utc(int time)
+static time_t
+local2utc(time_t time)
 {
        return time + sys_tz.tz_minuteswest * 60;
 }
 
 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
 
-static int
-date_dos2unix(unsigned short time, unsigned short date)
+static time_t
+date_dos2unix(__u16 date, __u16 time)
 {
-       int month, year, secs;
+       int month, year;
+       time_t secs;
 
        month = ((date >> 5) & 15) - 1;
        year = date >> 9;
@@ -203,14 +209,15 @@ date_dos2unix(unsigned short time, unsigned short date)
 /* Convert linear UNIX date to a MS-DOS time/date pair. */
 
 static void
-date_unix2dos(int unix_date, __u8 * date, __u8 * time)
+date_unix2dos(int unix_date, __u16 *date, __u16 *time)
 {
        int day, year, nl_day, month;
 
        unix_date = utc2local(unix_date);
-       WSET(time, 0,
-            (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) +
-            (((unix_date / 3600) % 24) << 11));
+       *time = (unix_date % 60) / 2 +
+               (((unix_date / 60) % 60) << 5) +
+               (((unix_date / 3600) % 24) << 11);
+
        day = unix_date / 86400 - 3652;
        year = day / 365;
        if ((year + 3) / 4 + 365 * year > day)
@@ -227,12 +234,10 @@ date_unix2dos(int unix_date, __u8 * date, __u8 * time)
                        if (day_n[month] > nl_day)
                                break;
        }
-       WSET(date, 0,
-            nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9));
+       *date = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9);
 }
 
 
-
 /*****************************************************************************/
 /*                                                                           */
 /*  Support section.                                                         */
@@ -272,9 +277,27 @@ smb_valid_packet(__u8 * packet)
 static int
 smb_verify(__u8 * packet, int command, int wct, int bcc)
 {
-       return (SMB_CMD(packet) == command &&
-               SMB_WCT(packet) >= wct &&
-               (bcc == -1 || SMB_BCC(packet) >= bcc)) ? 0 : -EIO;
+       if (SMB_CMD(packet) != command)
+               goto bad_command;
+       if (SMB_WCT(packet) < wct)
+               goto bad_wct;
+       if (bcc != -1 && SMB_BCC(packet) < bcc)
+               goto bad_bcc;
+       return 0;
+
+bad_command:
+       printk("smb_verify: command=%x, SMB_CMD=%x??\n",
+               command, SMB_CMD(packet));
+       goto fail;
+bad_wct:
+       printk("smb_verify: command=%x, wct=%d, SMB_WCT=%d??\n",
+               command, wct, SMB_WCT(packet));
+       goto fail;
+bad_bcc:
+       printk("smb_verify: command=%x, bcc=%d, SMB_BCC=%d??\n",
+               command, bcc, SMB_BCC(packet));
+fail:
+       return -EIO;
 }
 
 /*
@@ -327,15 +350,18 @@ server->packet_size, server->opt.max_xmit, size);
 }
 
 static int
-smb_errno(int errcls, int error)
+smb_errno(struct smb_sb_info *server)
 {
+       int errcls = server->rcls;
+       int error  = server->err;
+       char *class = "Unknown";
+
        if (errcls == ERRDOS)
                switch (error)
                {
                case ERRbadfunc:
                        return EINVAL;
                case ERRbadfile:
-                       return ENOENT;
                case ERRbadpath:
                        return ENOENT;
                case ERRnofids:
@@ -351,7 +377,6 @@ smb_errno(int errcls, int error)
                case ERRbadmem:
                        return EFAULT;
                case ERRbadenv:
-                       return EREMOTEIO;
                case ERRbadformat:
                        return EREMOTEIO;
                case ERRbadaccess:
@@ -364,7 +389,7 @@ smb_errno(int errcls, int error)
                        return EREMOTEIO;
                case ERRdiffdevice:
                        return EXDEV;
-               case ERRnofiles:
+               case ERRnofiles:        /* Why is this mapped to 0?? */
                        return 0;
                case ERRbadshare:
                        return ETXTBSY;
@@ -372,18 +397,19 @@ smb_errno(int errcls, int error)
                        return EDEADLK;
                case ERRfilexists:
                        return EEXIST;
-               case 87:
+               case 87:                /* should this map to 0?? */
                        return 0;       /* Unknown error!! */
                case 123:               /* Invalid name?? e.g. .tmp* */
                        return ENOENT;
+               case 145:               /* Win NT 4.0: non-empty directory? */
+                       return EBUSY;
                        /* This next error seems to occur on an mv when
                         * the destination exists */
                case 183:
                        return EEXIST;
                default:
-                       printk("smb_errno: ERRDOS code %d, returning EIO\n",
-                               error);
-                       return EIO;
+                       class = "ERRDOS";
+                       goto err_unknown;
        } else if (errcls == ERRSRV)
                switch (error)
                {
@@ -396,9 +422,8 @@ smb_errno(int errcls, int error)
                case ERRaccess:
                        return EACCES;
                default:
-                       printk("smb_errno: ERRSRV code %d, returning EIO\n",
-                               error);
-                       return EIO;
+                       class = "ERRSRV";
+                       goto err_unknown;
        } else if (errcls == ERRHRD)
                switch (error)
                {
@@ -409,7 +434,6 @@ smb_errno(int errcls, int error)
                case ERRnotready:
                        return EUCLEAN;
                case ERRbadcmd:
-                       return EIO;
                case ERRdata:
                        return EIO;
                case ERRbadreq:
@@ -419,15 +443,15 @@ smb_errno(int errcls, int error)
                case ERRlock:
                        return EDEADLK;
                default:
-                       printk("smb_errno: ERRHRD code %d, returning EIO\n",
-                               error);
-                       return EIO;
+                       class = "ERRHRD";
+                       goto err_unknown;
        } else if (errcls == ERRCMD)
-               {
-               printk("smb_errno: ERRCMD code %d, returning EIO\n", error);
-               return EIO;
-               }
-       return 0;
+               class = "ERRCMD";
+
+err_unknown:
+       printk("smb_errno: class %s, code %d from command %x\n",
+               class, error, SMB_CMD(server->packet));
+       return EIO;
 }
 
 static inline void
@@ -526,31 +550,51 @@ out:
 static int
 smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc)
 {
-       int result = 0;
+       int result = -EIO;
 
        s->rcls = 0;
        s->err = 0;
 
        /* Make sure we have a connection */
-       if (s->state != CONN_VALID && !smb_retry(s))
+       if (s->state != CONN_VALID)
        {
-               result = -EIO;
-       } else if (smb_request(s) < 0)
+               if (!smb_retry(s))
+                       goto out;
+       }
+
+       if (smb_request(s) < 0)
        {
                pr_debug("smb_request failed\n");
-               result = -EIO;
-       } else if (smb_valid_packet(s->packet) != 0)
-       {
-               pr_debug("not a valid packet!\n");
-               result = -EIO;
-       } else if (s->rcls != 0)
+               goto out;
+       }
+       if (smb_valid_packet(s->packet) != 0)
        {
-               result = -smb_errno(s->rcls, s->err);
-       } else if (smb_verify(s->packet, command, wct, bcc) != 0)
+#ifdef SMBFS_PARANOIA
+printk("smb_request_ok: invalid packet!\n");
+#endif
+               goto out;
+       }
+
+       /*
+        * Check for server errors.  The current smb_errno() routine
+        * is squashing some error codes, but I don't think this is
+        * correct: after a server error the packet won't be valid.
+        */
+       if (s->rcls != 0)
        {
-               pr_debug("smb_verify failed\n");
-               result = -EIO;
+               result = -smb_errno(s);
+               if (!result)
+                       printk("smb_request_ok: rcls=%d, err=%d mapped to 0\n",
+                               s->rcls, s->err);
+               /*
+                * Exit now even if the error was squashed ...
+                * packet verify will fail anyway.
+                */
+               goto out;
        }
+       result = smb_verify(s->packet, command, wct, bcc);
+
+out:
        return result;
 }
 
@@ -693,18 +737,21 @@ smb_setup_bcc(struct smb_sb_info *server, __u8 * p)
 
 /*
  * We're called with the server locked, and we leave it that way.
- * Set the permissions to be consistent with the desired access.
  */
-
 static int
-smb_proc_open(struct smb_sb_info *server, struct dentry *dir, int wish)
+smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish)
 {
-       struct inode *ino = dir->d_inode;
+       struct inode *ino = dentry->d_inode;
        int mode, read_write = 0x42, read_only = 0x40;
        int error;
        char *p;
 
+       /*
+        * Attempt to open r/w, unless there are no write privileges.
+        */
        mode = read_write;
+       if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
+               mode = read_only;
 #if 0
        if (!(wish & (O_WRONLY | O_RDWR)))
                mode = read_only;
@@ -715,7 +762,7 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dir, int wish)
        WSET(server->packet, smb_vwv0, mode);
        WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
        *p++ = 4;
-       p = smb_encode_path(server, p, dir, NULL);
+       p = smb_encode_path(server, p, dentry, NULL);
        smb_setup_bcc(server, p);
 
        error = smb_request_ok(server, SMBopen, 7, 0);
@@ -728,8 +775,8 @@ smb_proc_open(struct smb_sb_info *server, struct dentry *dir, int wish)
                    (error == -EACCES || error == -ETXTBSY || error == -EROFS))
                {
 #ifdef SMBFS_PARANOIA
-printk("smb_proc_open: %s/%s open failed, error=%d, retrying R/O\n",
-dir->d_parent->d_name.name, dir->d_name.name, error);
+printk("smb_proc_open: %s/%s R/W failed, error=%d, retrying R/O\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, error);
 #endif
                        mode = read_only;
                        goto retry;
@@ -742,25 +789,31 @@ dir->d_parent->d_name.name, dir->d_name.name, error);
        /* smb_vwv2 has mtime */
        /* smb_vwv4 has size  */
        ino->u.smbfs_i.access = WVAL(server->packet, smb_vwv6);
-       ino->u.smbfs_i.access &= 3;
+       ino->u.smbfs_i.access &= SMB_ACCMASK;
 
        /* N.B. Suppose the open failed?? */
        ino->u.smbfs_i.open = server->generation;
 
-#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_proc_open: error=%d, access=%d\n", error, ino->u.smbfs_i.access);
+#ifdef SMBFS_PARANOIA
+if (error)
+printk("smb_proc_open: %s/%s failed, error=%d, access=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,error,ino->u.smbfs_i.access);
 #endif
        return error;
 }
 
+/*
+ * Make sure the file is open, and check that the access
+ * is compatible with the desired access.
+ */
 int
 smb_open(struct dentry *dentry, int wish)
 {
-       struct inode *i = dentry->d_inode;
+       struct inode *inode = dentry->d_inode;
        int result;
 
        result = -ENOENT;
-       if (!i)
+       if (!inode)
        {
                printk("smb_open: no inode for dentry %s/%s\n",
                        dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -772,12 +825,12 @@ smb_open(struct dentry *dentry, int wish)
         * currently open, we can be sure that the file isn't about
         * to be closed. (See smb_close_dentry() below.)
         */
-       if (!smb_is_open(i))
+       if (!smb_is_open(inode))
        {
-               struct smb_sb_info *server = SMB_SERVER(i);
+               struct smb_sb_info *server = SMB_SERVER(inode);
                smb_lock_server(server);
                result = 0;
-               if (!smb_is_open(i))
+               if (!smb_is_open(inode))
                        result = smb_proc_open(server, dentry, wish);
                smb_unlock_server(server);
                if (result)
@@ -794,14 +847,20 @@ dentry->d_parent->d_name.name, dentry->d_name.name, result);
                smb_renew_times(dentry);
        }
 
-       result = -EACCES;
-       if (((wish == O_RDONLY) && ((i->u.smbfs_i.access == O_RDONLY)
-                                    || (i->u.smbfs_i.access == O_RDWR)))
-           || ((wish == O_WRONLY) && ((i->u.smbfs_i.access == O_WRONLY)
-                                       || (i->u.smbfs_i.access == O_RDWR)))
-           || ((wish == O_RDWR) && (i->u.smbfs_i.access == O_RDWR)))
-               result = 0;
-
+       /*
+        * Check whether the access is compatible with the desired mode.
+        */
+       result = 0;
+       if (inode->u.smbfs_i.access != wish && 
+           inode->u.smbfs_i.access != SMB_O_RDWR)
+       {
+#ifdef SMBFS_PARANOIA
+printk("smb_open: %s/%s access denied, access=%x, wish=%x\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+inode->u.smbfs_i.access, wish);
+#endif
+               result = -EACCES;
+       }
 out:
        return result;
 }
@@ -818,7 +877,12 @@ smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime)
 }
 
 /*
- * Called with the server locked
+ * Called with the server locked.
+ *
+ * Win NT 4.0 has an apparent bug in that it fails to update the
+ * modify time when writing to a file. As a workaround, we update
+ * the attributes if the file has been modified locally (we want to
+ * keep modify and access times in sync ...)
  */
 static int 
 smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino)
@@ -826,6 +890,29 @@ smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino)
        int result = 0;
        if (smb_is_open(ino))
        {
+               /*
+                * Check whether to update locally-modified attributes at
+                * closing time. This is necessary to keep the modify and
+                * access times in sync.
+                *
+                * Kludge alert: If we're using trans2 getattr messages,
+                * the timestamps are accurate only to two seconds ...
+                * we must round the time to avoid cache invalidations!
+                */
+               if (ino->u.smbfs_i.access == SMB_O_RDWR ||
+                   ino->u.smbfs_i.access == SMB_O_WRONLY) {
+                       struct smb_fattr fattr;
+
+                       if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) {
+                               if (ino->i_mtime & 1)
+                                       ino->i_mtime--;
+                               if (ino->i_atime & 1)
+                                       ino->i_atime--;
+                       }
+                       smb_get_inode_attr(ino, &fattr);
+                       smb_proc_setfile_trans2(server, ino, &fattr);
+               }
+
                /*
                 * We clear the open flag in advance, in case another
                 * process observes the value while we block below.
@@ -833,6 +920,14 @@ smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino)
                ino->u.smbfs_i.open = 0;
                result = smb_proc_close(server, ino->u.smbfs_i.fileid,
                                                ino->i_mtime);
+               ino->u.smbfs_i.cache_valid &= ~SMB_F_LOCALWRITE;
+               /*
+                * Force a revalidation after closing ... some servers
+                * don't post the size until the file has been closed.
+                */
+               if (server->opt.protocol < SMB_PROTOCOL_NT1)
+                       ino->u.smbfs_i.oldmtime = 0;
+               ino->u.smbfs_i.closed = jiffies;
        }
        return result;
 }
@@ -892,6 +987,22 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
        }
 }
 
+/*
+ * This is used to close a file following a failed instantiate.
+ * Since we don't have an inode, we can't use any of the above.
+ */
+int
+smb_close_fileid(struct dentry *dentry, __u16 fileid)
+{
+       struct smb_sb_info *server = server_from_dentry(dentry);
+       int result = 0;
+
+       smb_lock_server(server);
+       result = smb_proc_close(server, fileid, CURRENT_TIME);
+       smb_unlock_server(server);
+       return result;
+}
+
 /* In smb_proc_read and smb_proc_write we do not retry, because the
    file-id would not be valid after a reconnection. */
 
@@ -972,11 +1083,11 @@ count, offset, server->packet_size);
 
 int
 smb_proc_create(struct dentry *dir, struct qstr *name,
-               __u16 attr, time_t ctime)
+               __u16 attr, time_t ctime, __u16 *fileid)
 {
        struct smb_sb_info *server;
-       int error;
        char *p;
+       int error;
 
        server = server_from_dentry(dir);
        smb_lock_server(server);
@@ -989,13 +1100,14 @@ smb_proc_create(struct dentry *dir, struct qstr *name,
        p = smb_encode_path(server, p, dir, name);
        smb_setup_bcc(server, p);
 
-       if ((error = smb_request_ok(server, SMBcreate, 1, 0)) < 0)
+       error = smb_request_ok(server, SMBcreate, 1, 0);
+       if (error < 0)
        {
                if (smb_retry(server))
                        goto retry;
                goto out;
        }
-       smb_proc_close(server, WVAL(server->packet, smb_vwv0), CURRENT_TIME);
+       *fileid = WVAL(server->packet, smb_vwv0);
        error = 0;
 
 out:
@@ -1164,14 +1276,19 @@ smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
 static void
 smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
 {
-       fattr->f_mode = server->mnt->file_mode;
        if (fattr->attr & aDIR)
        {
+               /* N.B. check for read-only directories */
                fattr->f_mode = server->mnt->dir_mode;
                fattr->f_size = 512;
+       } else
+       {
+               fattr->f_mode = server->mnt->file_mode;
+               if (fattr->attr & aRONLY)
+                       fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
        }
 
-       fattr->f_blocks = 0; /* already set to zero? */
+       fattr->f_blocks = 0;
        if ((fattr->f_blksize != 0) && (fattr->f_size != 0))
        {
                fattr->f_blocks =
@@ -1519,7 +1636,7 @@ ff_dir_handle, ff_resume_key, ff_lastname, mask);
                        WSET(param, 10, 8 + 4 + 2);     /* resume required +
                                                           close on end +
                                                           continue */
-                       if (server->mnt->version & 1)
+                       if (server->mnt->version & SMB_FIX_WIN95)
                        {
                                /* Windows 95 is not able to deliver answers
                                 * to FIND_NEXT fast enough, so sleep 0.2 sec
@@ -1557,7 +1674,7 @@ printk("smb_proc_readdir_long: error=%d, breaking\n", result);
 printk("smb_proc_readdir_long: rcls=%d, err=%d, breaking\n",
 server->rcls, server->err);
 #endif
-                       entries = -smb_errno(server->rcls, server->err);
+                       entries = -smb_errno(server);
                        break;
                }
 #ifdef SMBFS_PARANOIA
@@ -1669,15 +1786,16 @@ smb_proc_readdir(struct dentry *dir, int fpos, void *cachep)
                return smb_proc_readdir_short(server, dir, fpos, cachep);
 }
 
+/*
+ * Note: called with the server locked.
+ */
 static int
 smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir,
-                       struct qstr *name, struct smb_fattr *attr)
+                       struct qstr *name, struct smb_fattr *fattr)
 {
        int result;
        char *p;
 
-       smb_lock_server(server);
-
       retry:
        p = smb_setup_header(server, SMBgetatr, 0, 0);
        *p++ = 4;
@@ -1690,32 +1808,38 @@ smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir,
                        goto retry;
                goto out;
        }
-       attr->attr   = WVAL(server->packet, smb_vwv0);
-       attr->f_ctime = attr->f_atime = attr->f_mtime = 
-            local2utc(DVAL(server->packet, smb_vwv1));
-       attr->f_size = DVAL(server->packet, smb_vwv3);
+       fattr->attr    = WVAL(server->packet, smb_vwv0);
+       fattr->f_mtime = local2utc(DVAL(server->packet, smb_vwv1));
+       fattr->f_size  = DVAL(server->packet, smb_vwv3);
+       fattr->f_ctime = fattr->f_mtime; 
+       fattr->f_atime = fattr->f_mtime; 
+#ifdef SMBFS_DEBUG_TIMESTAMP
+printk("getattr_core: %s/%s, mtime=%ld\n",
+dir->d_name.name, name->name, fattr->f_mtime);
+#endif
        result = 0;
 
 out:
-       smb_unlock_server(server);
        return result;
 }
 
+/*
+ * Note: called with the server locked.
+ */
 static int
 smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
                        struct qstr *name, struct smb_fattr *attr)
 {
        char *p;
        int result;
-
+       __u16 date, time;
+       int off_date = 0, off_time = 2;
        unsigned char *resp_data = NULL;
        unsigned char *resp_param = NULL;
        int resp_data_len = 0;
        int resp_param_len = 0;
        char param[SMB_MAXPATHLEN + 20]; /* too big for the stack! */
 
-       smb_lock_server(server);
-
       retry:
        WSET(param, 0, 1);      /* Info level SMB_INFO_STANDARD */
        DSET(param, 2, 0);
@@ -1737,7 +1861,7 @@ smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
 printk("smb_proc_getattr_trans2: for %s: result=%d, rcls=%d, err=%d\n",
 &param[6], result, server->rcls, server->err);
 #endif
-               result = -smb_errno(server->rcls, server->err);
+               result = -smb_errno(server);
                goto out;
        }
        result = -ENOENT;
@@ -1750,18 +1874,34 @@ printk("smb_proc_getattr_trans2: not enough data for %s, len=%d\n",
                goto out;
        }
 
-       attr->f_ctime = date_dos2unix(WVAL(resp_data, 2),
-                                     WVAL(resp_data, 0));
-       attr->f_atime = date_dos2unix(WVAL(resp_data, 6),
-                                     WVAL(resp_data, 4));
-       attr->f_mtime = date_dos2unix(WVAL(resp_data, 10),
-                                     WVAL(resp_data, 8));
+       /*
+        * Kludge alert: Win 95 swaps the date and time field,
+        * contrary to the CIFS docs and Win NT practice.
+        */
+       if (server->mnt->version & SMB_FIX_WIN95) {
+               off_date = 2;
+               off_time = 0;
+       }
+       date = WVAL(resp_data, off_date);
+       time = WVAL(resp_data, off_time);
+       attr->f_ctime = date_dos2unix(date, time);
+
+       date = WVAL(resp_data, 4 + off_date);
+       time = WVAL(resp_data, 4 + off_time);
+       attr->f_atime = date_dos2unix(date, time);
+
+       date = WVAL(resp_data, 8 + off_date);
+       time = WVAL(resp_data, 8 + off_time);
+       attr->f_mtime = date_dos2unix(date, time);
+#ifdef SMBFS_DEBUG_TIMESTAMP
+printk("getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n",
+dir->d_name.name, name->name, date, time, attr->f_mtime);
+#endif
        attr->f_size = DVAL(resp_data, 12);
        attr->attr = WVAL(resp_data, 20);
        result = 0;
 
 out:
-       smb_unlock_server(server);
        return result;
 }
 
@@ -1769,28 +1909,33 @@ int
 smb_proc_getattr(struct dentry *dir, struct qstr *name,
                     struct smb_fattr *fattr)
 {
-       struct smb_sb_info *server;
+       struct smb_sb_info *server = server_from_dentry(dir);
        int result;
 
-       server = server_from_dentry(dir);
+       smb_lock_server(server);
        smb_init_dirent(server, fattr);
 
        /*
-        * Win 95 is painfully slow at returning trans2 getattr info ...
+        * Win 95 is painfully slow at returning trans2 getattr info,
+        * so we provide the SMB_FIX_OLDATTR option switch.
         */
        if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 &&
-           !(server->mnt->version & 1))
+           !(server->mnt->version & SMB_FIX_OLDATTR))
                result = smb_proc_getattr_trans2(server, dir, name, fattr);
        else
                result = smb_proc_getattr_core(server, dir, name, fattr);
 
        smb_finish_dirent(server, fattr);
 
+       smb_unlock_server(server);
        return result;
 }
 
-/* In core protocol, there is only 1 time to be set, we use
-   entry->f_mtime, to make touch work. */
+/*
+ * In the core protocol there is only one time to be set,
+ * so we use fattr->f_mtime to make `touch' work.
+ * Note: called with the server locked.
+ */
 static int
 smb_proc_setattr_core(struct smb_sb_info *server,
                      struct dentry *dir, struct smb_fattr *fattr)
@@ -1799,19 +1944,25 @@ smb_proc_setattr_core(struct smb_sb_info *server,
        char *buf;
        int result;
 
-       smb_lock_server(server);
+#ifdef SMBFS_DEBUG_TIMESTAMP
+printk("setattr_core: %s/%s, mtime=%ld\n", 
+dir->d_parent->d_name.name, dir->d_name.name, fattr->f_mtime);
+#endif
 
       retry:
        buf = server->packet;
        p = smb_setup_header(server, SMBsetatr, 8, 0);
        WSET(buf, smb_vwv0, fattr->attr);
        DSET(buf, smb_vwv1, utc2local(fattr->f_mtime));
+       WSET(buf, smb_vwv3, 0); /* reserved values */
+       WSET(buf, smb_vwv4, 0);
+       WSET(buf, smb_vwv5, 0);
+       WSET(buf, smb_vwv6, 0);
+       WSET(buf, smb_vwv7, 0);
        *p++ = 4;
        p = smb_encode_path(server, p, dir, NULL);
-       *p++ = 4;
-       *p++ = 0;
-
        smb_setup_bcc(server, p);
+
        result = smb_request_ok(server, SMBsetatr, 0, 0);
        if (result < 0)
        {
@@ -1821,14 +1972,17 @@ smb_proc_setattr_core(struct smb_sb_info *server,
        }
        result = 0;
 out:
-       smb_unlock_server(server);
        return result;
 }
 
+/*
+ * Note: called with the server locked.
+ */
 static int
 smb_proc_setattr_trans2(struct smb_sb_info *server,
                        struct dentry *dir, struct smb_fattr *fattr)
 {
+       __u16 date, time;
        char *p;
        int result;
 
@@ -1839,20 +1993,28 @@ smb_proc_setattr_trans2(struct smb_sb_info *server,
        char param[SMB_MAXPATHLEN + 20]; /* too long for the stack! */
        char data[26];
 
-       smb_lock_server(server);
-
       retry:
        WSET(param, 0, 1);      /* Info level SMB_INFO_STANDARD */
        DSET(param, 2, 0);
        p = smb_encode_path(server, param + 6, dir, NULL);
 
-       date_unix2dos(fattr->f_ctime, &(data[0]), &(data[2]));
-       date_unix2dos(fattr->f_atime, &(data[4]), &(data[6]));
-       date_unix2dos(fattr->f_mtime, &(data[8]), &(data[10]));
+       date_unix2dos(fattr->f_ctime, &date, &time);
+       WSET(data, 0, date);
+       WSET(data, 2, time);
+       date_unix2dos(fattr->f_atime, &date, &time);
+       WSET(data, 4, date);
+       WSET(data, 6, time);
+       date_unix2dos(fattr->f_mtime, &date, &time);
+       WSET(data, 8, date);
+       WSET(data, 10, time);
+#ifdef SMBFS_DEBUG_TIMESTAMP
+printk("setattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n", 
+dir->d_parent->d_name.name, dir->d_name.name, date, time, fattr->f_mtime);
+#endif
        DSET(data, 12, fattr->f_size);
        DSET(data, 16, fattr->f_blksize);
        WSET(data, 20, fattr->attr);
-       WSET(data, 22, 0);
+       DSET(data, 22, 0); /* ULONG EA size */
 
        result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
                                    26, data, p - param, param,
@@ -1866,10 +2028,64 @@ smb_proc_setattr_trans2(struct smb_sb_info *server,
        }
        result = 0;
        if (server->rcls != 0)
-               result = -smb_errno(server->rcls, server->err);
+               result = -smb_errno(server);
+
+out:
+       return result;
+}
+
+/*
+ * Set the attributes for an open file.
+ */
+static int
+smb_proc_setfile_trans2(struct smb_sb_info *server,
+                       struct inode * inode, struct smb_fattr *fattr)
+{
+       __u16 date, time;
+       unsigned char *resp_data = NULL;
+       unsigned char *resp_parm = NULL;
+       int resp_data_len = 0;
+       int resp_parm_len = 0;
+       int result;
+       char parm[6], data[26];
+
+      retry:
+       WSET(parm, 0, inode->u.smbfs_i.fileid);
+       WSET(parm, 2, 1);       /* Info level SMB_INFO_STANDARD */
+
+       date_unix2dos(fattr->f_ctime, &date, &time);
+       WSET(data, 0, date);
+       WSET(data, 2, time);
+       date_unix2dos(fattr->f_atime, &date, &time);
+       WSET(data, 4, date);
+       WSET(data, 6, time);
+#ifdef SMBFS_DEBUG_TIMESTAMP
+printk("smb_proc_setfile_trans2: date=%x, time=%x, atime=%ld\n", 
+date, time, fattr->f_atime);
+#endif
+       date_unix2dos(fattr->f_mtime, &date, &time);
+       WSET(data, 8, date);
+       WSET(data, 10, time);
+       DSET(data, 12, fattr->f_size);
+       DSET(data, 16, fattr->f_blksize);
+       WSET(data, 20, fattr->attr);
+       DSET(data, 22, 0); /* ULONG EA size */
+
+       result = smb_trans2_request(server, TRANSACT2_SETFILEINFO,
+                                   26, data, 6, parm,
+                                   &resp_data_len, &resp_data,
+                                   &resp_parm_len, &resp_parm);
+       if (result < 0)
+       {
+               if (smb_retry(server))
+                       goto retry;
+               goto out;
+       }
+       result = 0;
+       if (server->rcls != 0)
+               result = -smb_errno(server);
 
 out:
-       smb_unlock_server(server);
        return result;
 }
 
@@ -1879,11 +2095,22 @@ smb_proc_setattr(struct smb_sb_info *server, struct dentry *dir,
 {
        int result;
 
-       if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
-               result = smb_proc_setattr_trans2(server, dir, fattr);
-       else
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_setattr: setting %s/%s, open=%d\n", 
+dir->d_parent->d_name.name, dir->d_name.name, smb_is_open(dir->d_inode));
+#endif
+       smb_lock_server(server);
+       if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) {
+               struct inode *inode = dir->d_inode;
+
+               if (smb_is_open(inode))
+                       result = smb_proc_setfile_trans2(server, inode, fattr);
+               else
+                       result = smb_proc_setattr_trans2(server, dir, fattr);
+       } else
                result = smb_proc_setattr_core(server, dir, fattr);
 
+       smb_unlock_server(server);
        return result;
 }
 
index cf95cc3293ed095e2511654cac8db60f65217302..f14836199c1696eac5143ce159054d82cf901754 100644 (file)
@@ -420,8 +420,8 @@ printk("smb_receive: receive error: %d\n", result);
 #endif
                goto out;
        }
-       server->rcls = *(packet+9);
-       server->err = WVAL(packet, 11);
+       server->rcls = *(packet + smb_rcls);
+       server->err  = WVAL(packet, smb_err);
 
 #ifdef SMBFS_DEBUG_VERBOSE
 if (server->rcls != 0)
index a5dfa8463fb256246497f331c7e958512bb85019..8da1df471d7ba55c0c8dcce1fd9379cb26a540e3 100644 (file)
@@ -654,6 +654,7 @@ extern struct file_operations rdwr_pipe_fops;
 extern struct file_system_type *get_fs_type(const char *name);
 
 extern int fs_may_remount_ro(struct super_block *);
+extern int fs_may_mount(kdev_t dev);
 
 extern struct file *inuse_filps;
 extern struct super_block super_blocks[NR_SUPER];
diff --git a/include/linux/ftape-header-segment.h b/include/linux/ftape-header-segment.h
new file mode 100644 (file)
index 0000000..4732218
--- /dev/null
@@ -0,0 +1,122 @@
+#ifndef _FTAPE_HEADER_SEGMENT_H
+#define _FTAPE_HEADER_SEGMENT_H
+
+/*
+ * Copyright (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/include/linux/ftape-header-segment.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:28 $
+ *
+ *      This file defines some offsets into the header segment of a
+ *      floppy tape cartridge.  For use with the QIC-40/80/3010/3020
+ *      floppy-tape driver "ftape" for Linux.
+ */
+
+#define FT_SIGNATURE   0  /* must be 0xaa55aa55 */
+#define FT_FMT_CODE    4
+#define FT_REV_LEVEL   5  /* only for QIC-80 since. Rev. L (== 0x0c)         */
+#define FT_HSEG_1      6  /* first header segment, except for format code  6 */
+#define FT_HSEG_2      8  /* second header segment, except for format code 6 */
+#define FT_FRST_SEG   10  /* first data segment, except for format code 6    */
+#define FT_LAST_SEG   12  /* last data segment, except for format code 6     */
+#define FT_FMT_DATE   14  /* date and time of most recent format, see below  */
+#define FT_WR_DATE    18  /* date and time of most recent write or format    */
+#define FT_SPT        24  /* segments per track                              */
+#define FT_TPC        26  /* tracks per cartridge                            */
+#define FT_FHM        27  /* floppy drive head (maximum of it)               */
+#define FT_FTM        28  /* floppy track max.                               */
+#define FT_FSM        29  /* floppy sector max. (128)                        */
+#define FT_LABEL      30  /* floppy tape label                               */
+#define FT_LABEL_DATE 74  /* date and time the tape label was written        */
+#define FT_LABEL_SZ   (FT_LABEL_DATE - FT_LABEL)
+#define FT_CMAP_START 78  /* starting segment of compression map             */
+#define FT_FMT_ERROR 128  /* must be set to 0xff if remainder gets lost during
+                          * tape format
+                          */
+#define FT_SEG_CNT   130  /* number of seg. written, formatted or verified
+                          * through lifetime of tape (why not read?)
+                          */
+#define FT_INIT_DATE 138  /* date and time of initial tape format    */
+#define FT_FMT_CNT   142  /* number of times tape has been formatted */
+#define FT_FSL_CNT   144  /* number of segments in failed sector log */
+#define FT_MK_CODE   146  /* id string of tape manufacturer          */
+#define FT_LOT_CODE  190  /* tape manufacturer lot code              */
+#define FT_6_HSEG_1  234  /* first header segment for format code  6 */
+#define FT_6_HSEG_2  238  /* second header segment for format code 6 */
+#define FT_6_FRST_SEG 242 /* first data segment for format code 6    */
+#define FT_6_LAST_SEG 246 /* last data segment for format code 6     */
+
+#define FT_FSL        256
+#define FT_HEADER_END 256 /* space beyond this point:
+                          * format codes 2, 3 and 5: 
+                          * -  failed sector log until byte 2047
+                          * -  bad sector map in the reamining part of segment
+                          * format codes 4 and 6:
+                          * -  bad sector map  starts hear
+                          */
+
+
+/*  value to be stored at the FT_SIGNATURE offset 
+ */
+#define FT_HSEG_MAGIC 0xaa55aa55
+#define FT_D2G_MAGIC  0x82288228 /* Ditto 2GB */
+
+/* data and time encoding: */
+#define FT_YEAR_SHIFT 25
+#define FT_YEAR_MASK  0xfe000000
+#define FT_YEAR_0     1970
+#define FT_YEAR_MAX   127
+#define FT_YEAR(year) ((((year)-FT_YEAR_0)<<FT_YEAR_SHIFT)&FT_YEAR_MASK)
+
+#define FT_TIME_SHIFT   0
+#define FT_TIME_MASK    0x01FFFFFF
+#define FT_TIME_MAX     0x01ea6dff /* last second of a year */
+#define FT_TIME(mo,d,h,m,s) \
+       ((((s)+60*((m)+60*((h)+24*((d)+31*(mo))))) & FT_TIME_MASK))
+
+#define FT_TIME_STAMP(y,mo,d,h,m,s) (FT_YEAR(y) | FT_TIME(mo,d,h,m,s))
+
+/* values for the format code field */
+typedef enum {
+       fmt_normal = 2, /*  QIC-80 post Rev. B 205Ft or 307Ft tape    */
+       fmt_1100ft = 3, /*  QIC-80 post Rev. B 1100Ft tape            */
+       fmt_var    = 4, /*  QIC-80 post Rev. B variabel length format */
+       fmt_425ft  = 5, /*  QIC-80 post Rev. B 425Ft tape             */
+       fmt_big    = 6  /*  QIC-3010/3020 variable length tape with more 
+                        *  than 2^16 segments per tape
+                        */
+} ft_format_type;
+
+/* definitions for the failed sector log */
+#define FT_FSL_SIZE        (2 * FT_SECTOR_SIZE - FT_HEADER_END)
+#define FT_FSL_MAX_ENTRIES (FT_FSL_SIZE/sizeof(__u32))
+
+typedef struct ft_fsl_entry {
+       __u16 segment;
+       __u16 date;
+} __attribute__ ((packed)) ft_fsl_entry;
+
+
+/*  date encoding for the failed sector log 
+ *  month: 1..12, day: 1..31, year: 1970..2097
+ */
+#define FT_FSL_TIME_STAMP(y,m,d) \
+       (((((y) - FT_YEAR_0)<<9)&0xfe00) | (((m)<<5)&0x01e0) | ((d)&0x001f))
+
+#endif /* _FTAPE_HEADER_SEGMENT_H */
diff --git a/include/linux/ftape-vendors.h b/include/linux/ftape-vendors.h
new file mode 100644 (file)
index 0000000..ec1a81f
--- /dev/null
@@ -0,0 +1,137 @@
+#ifndef _FTAPE_VENDORS_H
+#define _FTAPE_VENDORS_H
+
+/*
+ *      Copyright (C) 1993-1996 Bas Laarhoven,
+ *                (C) 1996-1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/include/linux/ftape-vendors.h,v $
+ * $Revision: 1.6 $
+ * $Date: 1997/10/09 15:38:11 $
+ *
+ *      This file contains the supported drive types with their
+ *      QIC-117 spec. vendor code and drive dependent configuration
+ *      information.
+ */
+
+typedef enum {
+       unknown_wake_up = 0,
+       no_wake_up,
+       wake_up_colorado,
+       wake_up_mountain,
+       wake_up_insight,
+} wake_up_types;
+
+typedef struct {
+       wake_up_types wake_up;  /* see wake_up_types */
+       char *name;             /* Text describing the drive */
+} wakeup_method;
+
+/*  Note: order of entries in WAKEUP_METHODS must be so that a variable
+ *        of type wake_up_types can be used as an index in the array.
+ */
+#define WAKEUP_METHODS { \
+  { unknown_wake_up,    "Unknown" }, \
+  { no_wake_up,         "None" }, \
+  { wake_up_colorado,   "Colorado" }, \
+  { wake_up_mountain,   "Mountain" }, \
+  { wake_up_insight,    "Motor-on" }, \
+}
+
+typedef struct {
+       unsigned int vendor_id; /* vendor id from drive */
+       int speed;              /* maximum tape transport speed (ips) */
+       wake_up_types wake_up;  /* see wake_up_types */
+       char *name;             /* Text describing the drive */
+} vendor_struct;
+
+#define UNKNOWN_VENDOR (-1)
+
+#define QIC117_VENDORS {                                                   \
+/* see _vendor_struct */                                                   \
+  { 0x00000,  82, wake_up_colorado,  "Colorado DJ-10 (old)" },             \
+  { 0x00047,  90, wake_up_colorado,  "Colorado DJ-10/DJ-20" },             \
+  { 0x011c2,  84, wake_up_colorado,  "Colorado 700" },                     \
+  { 0x011c3,  90, wake_up_colorado,  "Colorado 1400" },                            \
+  { 0x011c4,  84, wake_up_colorado,  "Colorado DJ-10/DJ-20 (new)" },       \
+  { 0x011c5,  84, wake_up_colorado,  "HP Colorado T1000" },                \
+  { 0x011c6,  90, wake_up_colorado,  "HP Colorado T3000" },                \
+  { 0x00005,  45, wake_up_mountain,  "Archive 5580i" },                            \
+  { 0x10005,  50, wake_up_insight,   "Insight 80Mb, Irwin 80SX" },         \
+  { 0x00140,  74, wake_up_mountain,  "Archive S.Hornet [Identity/Escom]" }, \
+  { 0x00146,  72, wake_up_mountain,  "Archive 31250Q [Escom]" },           \
+  { 0x0014a, 100, wake_up_mountain,  "Archive XL9250i [Conner/Escom]" },    \
+  { 0x0014c,  98, wake_up_mountain,  "Conner C250MQT" },                   \
+  { 0x0014e,  80, wake_up_mountain,  "Conner C250MQ" },                            \
+  { 0x00150,  80, wake_up_mountain,  "Conner TSM420R/TST800R" },           \
+  { 0x00152,  80, wake_up_mountain,  "Conner TSM850R" },                   \
+  { 0x00156,  80, wake_up_mountain,  "Conner TSM850R/1700R/TST3200R" },            \
+  { 0x00180,   0, wake_up_mountain,  "Summit SE 150" },                            \
+  { 0x00181,  85, wake_up_mountain,  "Summit SE 250, Mountain FS8000" },    \
+  { 0x001c1,  82, no_wake_up,        "Wangtek 3040F" },                            \
+  { 0x001c8,  64, no_wake_up,        "Wangtek 3080F" },                            \
+  { 0x001c8,  64, wake_up_colorado,  "Wangtek 3080F" },                            \
+  { 0x001ca,  67, no_wake_up,        "Wangtek 3080F (new)" },              \
+  { 0x001cc,  77, wake_up_colorado,  "Wangtek 3200 / Teac 700" },          \
+  { 0x001cd,  75, wake_up_colorado,  "Reveal TB1400" },                            \
+  { 0x00380,  85, wake_up_colorado,  "Exabyte Eagle-96" },                 \
+  { 0x00381,  85, wake_up_colorado,  "Exabyte Eagle TR-3" },               \
+  { 0x00382,  85, wake_up_colorado,  "Exabyte Eagle TR-3" },               \
+  { 0x003ce,  77, wake_up_colorado,  "Teac 800" },                         \
+  { 0x003cf,   0, wake_up_colorado,  "Teac FT3010TR" },                            \
+  { 0x08880,  64, no_wake_up,        "Iomega 250, Ditto 800" },                    \
+  { 0x08880,  64, wake_up_colorado,  "Iomega 250, Ditto 800" },                    \
+  { 0x08880,  64, wake_up_insight,   "Iomega 250, Ditto 800" },                    \
+  { 0x08881,  80, wake_up_colorado,  "Iomega 700" },                       \
+  { 0x08882,  80, wake_up_colorado,  "Iomega 3200" },                      \
+  { 0x08883,  80, wake_up_colorado,  "Iomega DITTO 2GB" },                 \
+  { 0x00021,  70, no_wake_up,        "AIWA CT-803" },                      \
+  { 0x004c0,  80, no_wake_up,        "AIWA TD-S1600" },                            \
+  { 0x00021,   0, wake_up_mountain,  "COREtape QIC80" },                   \
+  { 0x00441,   0, wake_up_mountain,  "ComByte DoublePlay" },               \
+  { 0x00481, 127, wake_up_mountain,  "PERTEC MyTape 800" },                \
+  { 0x00483, 130, wake_up_mountain,  "PERTEC MyTape 3200" },               \
+  { UNKNOWN_VENDOR, 0, no_wake_up, "unknown" }                             \
+}
+
+#define QIC117_MAKE_CODES {                    \
+  { 0, "Unassigned" },                         \
+  { 1, "Alloy Computer Products" },            \
+  { 2, "3M" },                                 \
+  { 3, "Tandberg Data" },                      \
+  { 4, "Colorado" },                           \
+  { 5, "Archive/Conner" },                     \
+  { 6, "Mountain/Summit Memory Systems" },     \
+  { 7, "Wangtek/Rexon/Tecmar" },               \
+  { 8, "Sony" },                               \
+  { 9, "Cipher Data Products" },               \
+  { 10, "Irwin Magnetic Systems" },            \
+  { 11, "Braemar" },                           \
+  { 12, "Verbatim" },                          \
+  { 13, "Core International" },                        \
+  { 14, "Exabyte" },                           \
+  { 15, "Teac" },                              \
+  { 16, "Gigatek" },                           \
+  { 17, "ComByte" },                           \
+  { 18, "PERTEC Memories" },                   \
+  { 19, "Aiwa" },                              \
+  { 71, "Colorado" },                          \
+  { 546, "Iomega Inc" },                       \
+}
+
+#endif /* _FTAPE_VENDORS_H */
index 0ce7a1c4b0f5acebadae34dc2dc94d085022387e..36ade3437e3a8c9516a15fd2f24f541603672afc 100644 (file)
 #define _FTAPE_H
 
 /*
- * Copyright (C) 1994-1995 Bas Laarhoven.
+ * Copyright (C) 1994-1996 Bas Laarhoven,
+ *           (C) 1996-1997 Claus-Justus Heine.
 
-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 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.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
 
-You should have received a copy of the GNU General Public License
-along with this program; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 
  *
- $Source: /home/bas/distr/ftape-2.03b/RCS/ftape.h,v $
- $Author: bas $
- *
- $Revision: 1.18 $
- $Date: 1995/05/06 16:11:53 $
- $State: Beta $
+ * $Source: /homes/cvs/ftape-stacked/include/linux/ftape.h,v $
+ * $Revision: 1.17.6.4 $
+ * $Date: 1997/11/25 01:52:54 $
  *
  *      This file contains global definitions, typedefs and macro's
- *      for the QIC-40/80 floppy-tape driver for Linux.
+ *      for the QIC-40/80/3010/3020 floppy-tape driver for Linux.
  */
 
+#define FTAPE_VERSION "ftape v3.04d 25/11/97"
+
+/* this makes the Kernel version numbers readable */
+#define KERNEL_VER(major,minor,sublvl) (((major)<<16)+((minor)<<8)+(sublvl))
+
+#ifdef __KERNEL__
 #include <linux/sched.h>
 #include <linux/mm.h>
+#endif
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/config.h>
+#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) 
+typedef daddr_t __kernel_daddr_t; /* needed for mtio.h */
+#endif
+#include <linux/mtio.h>
+
+#define FT_SECTOR(x)           (x+1)   /* sector offset into real sector */
+#define FT_SECTOR_SIZE         1024
+#define FT_SECTORS_PER_SEGMENT   32
+#define FT_ECC_SECTORS            3
+#define FT_SEGMENT_SIZE                ((FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS) * FT_SECTOR_SIZE)
+#define FT_BUFF_SIZE    (FT_SECTORS_PER_SEGMENT * FT_SECTOR_SIZE)
+
+/*
+ *   bits of the minor device number that define drive selection
+ *   methods. Could be used one day to access multiple tape
+ *   drives on the same controller.
+ */
+#define FTAPE_SEL_A     0
+#define FTAPE_SEL_B     1
+#define FTAPE_SEL_C     2
+#define FTAPE_SEL_D     3
+#define FTAPE_SEL_MASK     3
+#define FTAPE_SEL(unit) ((unit) & FTAPE_SEL_MASK)
+#define FTAPE_NO_REWIND 4      /* mask for minor nr */
 
-#define SECTOR(x)       (x+1)         /* sector offset into real sector */
-#define SECTOR_SIZE     (1024)
-#define SECTORS_PER_SEGMENT (32)
-#define BUFF_SIZE       (SECTORS_PER_SEGMENT * SECTOR_SIZE)
-#define FTAPE_UNIT      (ftape_unit & 3)
-#define RQM_DELAY       (12)
-#define MILLISECOND     (1)
-#define SECOND          (1000)
-#define FOREVER         (-1)
+/* the following two may be reported when MTIOCGET is requested ... */
+typedef union {
+       struct {
+               __u8 error;
+               __u8 command;
+       } error;
+       long space;
+} ft_drive_error;
+typedef union {
+       struct {
+               __u8 drive_status;
+               __u8 drive_config;
+               __u8 tape_status;
+       } status;
+       long space;
+} ft_drive_status;
+
+#ifdef __KERNEL__
+
+#define FT_RQM_DELAY    12
+#define FT_MILLISECOND  1
+#define FT_SECOND       1000
+#define FT_FOREVER      -1
 #ifndef HZ
-# error "HZ undefined."
+#error "HZ undefined."
 #endif
-#define MSPT            (SECOND / HZ) /* milliseconds per tick */
+#define FT_USPT         (1000000/HZ) /* microseconds per tick */
 
 /* This defines the number of retries that the driver will allow
  * before giving up (and letting a higher level handle the error).
  */
 #ifdef TESTING
-# define SOFT_RETRIES 1          /* number of low level retries */
-# define RETRIES_ON_ECC_ERROR 3  /* ecc error when correcting segment */
+#define FT_SOFT_RETRIES 1         /* number of low level retries */
+#define FT_RETRIES_ON_ECC_ERROR 3  /* ecc error when correcting segment */
 #else
-# define SOFT_RETRIES 6          /* number of low level retries (triple) */
-# define RETRIES_ON_ECC_ERROR 3  /* ecc error when correcting segment */
+#define FT_SOFT_RETRIES 6         /* number of low level retries (triple) */
+#define FT_RETRIES_ON_ECC_ERROR 3  /* ecc error when correcting segment */
+#endif
+
+#ifndef THE_FTAPE_MAINTAINER
+#define THE_FTAPE_MAINTAINER "the ftape maintainer"
+#endif
+
+/* Initialize missing configuration parameters.
+ */
+#ifndef CONFIG_FT_NR_BUFFERS
+# define CONFIG_FT_NR_BUFFERS 3
+#endif
+#ifndef CONFIG_FT_FDC_THR
+# define CONFIG_FT_FDC_THR 8
 #endif
+#ifndef CONFIG_FT_FDC_MAX_RATE
+# define CONFIG_FT_FDC_MAX_RATE 2000
+#endif
+#ifndef CONFIG_FT_FDC_BASE
+# define CONFIG_FT_FDC_BASE 0
+#endif
+#ifndef CONFIG_FT_FDC_IRQ
+# define CONFIG_FT_FDC_IRQ  0
+#endif
+#ifndef CONFIG_FT_FDC_DMA
+# define CONFIG_FT_FDC_DMA  0
+#endif
+
+/* Turn some booleans into numbers.
+ */
+#ifdef CONFIG_FT_PROBE_FC10
+# undef CONFIG_FT_PROBE_FC10
+# define CONFIG_FT_PROBE_FC10 1
+#else
+# define CONFIG_FT_PROBE_FC10 0
+#endif
+#ifdef CONFIG_FT_MACH2
+# undef CONFIG_FT_MACH2
+# define CONFIG_FT_MACH2 1
+#else
+# define CONFIG_FT_MACH2 0
+#endif
+
+/* Insert default settings
+ */
+#if CONFIG_FT_PROBE_FC10 == 1
+# if CONFIG_FT_FDC_BASE == 0
+#  undef  CONFIG_FT_FDC_BASE
+#  define CONFIG_FT_FDC_BASE 0x180
+# endif
+# if CONFIG_FT_FDC_IRQ == 0
+#  undef  CONFIG_FT_FDC_IRQ
+#  define CONFIG_FT_FDC_IRQ 9
+# endif
+# if CONFIG_FT_FDC_DMA == 0
+#  undef  CONFIG_FT_FDC_DMA
+#  define CONFIG_FT_FDC_DMA 3
+# endif
+#elif CONIFG_FT_MACH2 == 1    /* CONFIG_FT_PROBE_FC10 == 1 */
+# if CONFIG_FT_FDC_BASE == 0
+#  undef  CONFIG_FT_FDC_BASE
+#  define CONFIG_FT_FDC_BASE 0x1E0
+# endif
+# if CONFIG_FT_FDC_IRQ == 0
+#  undef  CONFIG_FT_FDC_IRQ
+#  define CONFIG_FT_FDC_IRQ 6
+# endif
+# if CONFIG_FT_FDC_DMA == 0
+#  undef  CONFIG_FT_FDC_DMA
+#  define CONFIG_FT_FDC_DMA 2
+# endif
+#elif CONFIG_FT_ALT_FDC == 1  /* CONFIG_FT_MACH2 */
+# if CONFIG_FT_FDC_BASE == 0
+#  undef  CONFIG_FT_FDC_BASE
+#  define CONFIG_FT_FDC_BASE 0x370
+# endif
+# if CONFIG_FT_FDC_IRQ == 0
+#  undef  CONFIG_FT_FDC_IRQ
+#  define CONFIG_FT_FDC_IRQ 6
+# endif
+# if CONFIG_FT_FDC_DMA == 0
+#  undef  CONFIG_FT_FDC_DMA
+#  define CONFIG_FT_FDC_DMA 2
+# endif
+#else                          /* CONFIG_FT_ALT_FDC */
+# if CONFIG_FT_FDC_BASE == 0
+#  undef  CONFIG_FT_FDC_BASE
+#  define CONFIG_FT_FDC_BASE 0x3f0
+# endif
+# if CONFIG_FT_FDC_IRQ == 0
+#  undef  CONFIG_FT_FDC_IRQ
+#  define CONFIG_FT_FDC_IRQ 6
+# endif
+# if CONFIG_FT_FDC_DMA == 0
+#  undef  CONFIG_FT_FDC_DMA
+#  define CONFIG_FT_FDC_DMA 2
+# endif
+#endif                         /* standard FDC */
+
 /*      some useful macro's
  */
 #define ABS(a)          ((a) < 0 ? -(a) : (a))
-#define NR_ITEMS(x)     (sizeof(x)/ sizeof(*x))
-
-typedef unsigned char byte;
+#define NR_ITEMS(x)     (int)(sizeof(x)/ sizeof(*x))
 
 extern int ftape_init(void);
 
-#endif
+#endif  /* __KERNEL__ */
 
+#endif
index 9381d30a3d7fd0ba9195f6c30cb98c971ddfab2e..c794ed898ac506d4fa33cc3637e60611ce9206e2 100644 (file)
@@ -1,11 +1,16 @@
 /* 
  * linux/mtio.h header file for Linux. Written by H. Bergman
+ *
+ * Modified for special ioctls provided by zftape in September 1997
+ * by C.-J. Heine.
  */
 
 #ifndef _LINUX_MTIO_H
 #define _LINUX_MTIO_H
 
+#include <linux/types.h>
 #include <linux/ioctl.h>
+#include <linux/qic117.h>
 
 /*
  * Structures and definitions for mag tape io control commands
@@ -104,7 +109,7 @@ struct      mtget {
 #define MT_ISSCSI2             0x72    /* Generic ANSI SCSI-2 tape unit */
 
 /* QIC-40/80/3010/3020 ftape supported drives.
- * 20bit vendor ID + 0x800000 (see vendors.h in ftape distribution)
+ * 20bit vendor ID + 0x800000 (see ftape-vendors.h)
  */
 #define MT_ISFTAPE_UNKNOWN     0x800000 /* obsolete */
 #define MT_ISFTAPE_FLAG        0x800000
@@ -170,6 +175,109 @@ struct mtconfiginfo {
        char            reserved[10];
 };
 
+/*  structure for MTIOCVOLINFO, query information about the volume
+ *  currently positioned at (zftape)
+ */
+struct mtvolinfo {
+       unsigned int mt_volno;   /* vol-number */
+       unsigned int mt_blksz;   /* blocksize used when recording */
+       unsigned int mt_rawsize; /* raw tape space consumed, in kb */
+       unsigned int mt_size;    /* volume size after decompression, in kb */
+       unsigned int mt_cmpr:1;  /* this volume has been compressed */
+};
+
+/* raw access to a floppy drive, read and write an arbitrary segment.
+ * For ftape/zftape to support formatting etc.
+ */
+#define MT_FT_RD_SINGLE  0
+#define MT_FT_RD_AHEAD   1
+#define MT_FT_WR_ASYNC   0 /* start tape only when all buffers are full     */
+#define MT_FT_WR_MULTI   1 /* start tape, continue until buffers are empty  */
+#define MT_FT_WR_SINGLE  2 /* write a single segment and stop afterwards    */
+#define MT_FT_WR_DELETE  3 /* write deleted data marks, one segment at time */
+
+struct mtftseg
+{            
+       unsigned mt_segno;   /* the segment to read or write */
+       unsigned mt_mode;    /* modes for read/write (sync/async etc.) */
+       int      mt_result;  /* result of r/w request, not of the ioctl */
+       void    *mt_data;    /* User space buffer: must be 29kb */
+};
+
+/* get tape capacity (ftape/zftape)
+ */
+struct mttapesize {
+       unsigned long mt_capacity; /* entire, uncompressed capacity 
+                                   * of a cartridge
+                                   */
+       unsigned long mt_used;     /* what has been used so far, raw 
+                                   * uncompressed amount
+                                   */
+};
+
+/*  possible values of the ftfmt_op field
+ */
+#define FTFMT_SET_PARMS                1 /* set software parms */
+#define FTFMT_GET_PARMS                2 /* get software parms */
+#define FTFMT_FORMAT_TRACK     3 /* start formatting a tape track   */
+#define FTFMT_STATUS           4 /* monitor formatting a tape track */
+#define FTFMT_VERIFY           5 /* verify the given segment        */
+
+struct ftfmtparms {
+       unsigned char  ft_qicstd;   /* QIC-40/QIC-80/QIC-3010/QIC-3020 */
+       unsigned char  ft_fmtcode;  /* Refer to the QIC specs */
+       unsigned char  ft_fhm;      /* floppy head max */
+       unsigned char  ft_ftm;      /* floppy track max */
+       unsigned short ft_spt;      /* segments per track */
+       unsigned short ft_tpc;      /* tracks per cartridge */
+};
+
+struct ftfmttrack {
+       unsigned int  ft_track;   /* track to format */
+       unsigned char ft_gap3;    /* size of gap3, for FORMAT_TRK */
+};
+
+struct ftfmtstatus {
+       unsigned int  ft_segment;  /* segment currently being formatted */
+};
+
+struct ftfmtverify {
+       unsigned int  ft_segment;   /* segment to verify */
+       unsigned long ft_bsm;       /* bsm as result of VERIFY cmd */
+};
+
+struct mtftformat {
+       unsigned int fmt_op;      /* operation to perform */
+       union fmt_arg {
+               struct ftfmtparms  fmt_parms;  /* format parameters */
+               struct ftfmttrack  fmt_track;  /* ctrl while formatting */
+               struct ftfmtstatus fmt_status;
+               struct ftfmtverify fmt_verify; /* for verifying */ 
+       } fmt_arg;
+};
+
+struct mtftcmd {
+       unsigned int ft_wait_before; /* timeout to wait for drive to get ready 
+                                     * before command is sent. Milliseconds
+                                     */
+       qic117_cmd_t ft_cmd;         /* command to send */
+       unsigned char ft_parm_cnt;   /* zero: no parm is sent. */
+       unsigned char ft_parms[3];   /* parameter(s) to send to
+                                     * the drive. The parms are nibbles
+                                     * driver sends cmd + 2 step pulses */
+       unsigned int ft_result_bits; /* if non zero, number of bits
+                                     * returned by the tape drive
+                                     */
+       unsigned int ft_result;      /* the result returned by the tape drive*/
+       unsigned int ft_wait_after;  /* timeout to wait for drive to get ready
+                                     * after command is sent. 0: don't wait */
+       int ft_status;               /* status returned by ready wait
+                                     * undefined if timeout was 0.
+                                     */
+       int ft_error;                /* error code if error status was set by 
+                                     * command
+                                     */
+};
 
 /* mag tape io control commands */
 #define        MTIOCTOP        _IOW('m', 1, struct mtop)       /* do a mag tape op */
@@ -182,6 +290,16 @@ struct mtconfiginfo {
 #define        MTIOCGETCONFIG  _IOR('m', 4, struct mtconfiginfo) /* get tape config */
 #define        MTIOCSETCONFIG  _IOW('m', 5, struct mtconfiginfo) /* set tape config */
 
+/* the next six are used by the floppy ftape drivers and its frontends
+ * sorry, but MTIOCTOP commands are write only.
+ */
+#define        MTIOCRDFTSEG    _IOWR('m', 6, struct mtftseg)  /* read a segment */
+#define        MTIOCWRFTSEG    _IOWR('m', 7, struct mtftseg)   /* write a segment */
+#define MTIOCVOLINFO   _IOR('m',  8, struct mtvolinfo) /* info about volume */
+#define MTIOCGETSIZE    _IOR('m',  9, struct mttapesize)/* get cartridge size*/
+#define MTIOCFTFORMAT   _IOWR('m', 10, struct mtftformat) /* format ftape */
+#define MTIOCFTCMD     _IOWR('m', 11, struct mtftcmd) /* send QIC-117 cmd */
+
 /* Generic Mag Tape (device independent) status macros for examining
  * mt_gstat -- HP-UX compatible.
  * There is room for more generic status bits here, but I don't
index a78aa8a69ce8301be2b6840c2711a391a5cf9bf5..c0da45b961861ba35f280e4cd8165cd3a53a0028 100644 (file)
 #define PCI_DEVICE_ID_VIA_82C576       0x0576
 #define PCI_DEVICE_ID_VIA_82C585       0x0585
 #define PCI_DEVICE_ID_VIA_82C586_0     0x0586
-#define PCI_DEVICE_ID_VIA_82C926       0x0926
 #define PCI_DEVICE_ID_VIA_82C595       0x0595
+#define PCI_DEVICE_ID_VIA_82C926       0x0926
 #define PCI_DEVICE_ID_VIA_82C416       0x1571
 #define PCI_DEVICE_ID_VIA_82C595_97    0x1595
 #define PCI_DEVICE_ID_VIA_82C586_2     0x3038
diff --git a/include/linux/qic117.h b/include/linux/qic117.h
new file mode 100644 (file)
index 0000000..07b537e
--- /dev/null
@@ -0,0 +1,290 @@
+#ifndef _QIC117_H
+#define _QIC117_H
+
+/*
+ *      Copyright (C) 1993-1996 Bas Laarhoven,
+ *                (C) 1997      Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/include/linux/qic117.h,v $
+ * $Revision: 1.2 $
+ * $Date: 1997/10/05 19:19:32 $
+ *
+ *      This file contains QIC-117 spec. related definitions for the
+ *      QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
+ *
+ *      These data were taken from the Quarter-Inch Cartridge
+ *      Drive Standards, Inc. document titled:
+ *      `Common Command Set Interface Specification for Flexible
+ *       Disk Controller Based Minicartridge Tape Drives'
+ *       document QIC-117 Revision J, 28 Aug 96.
+ *      For more information, contact:
+ *       Quarter-Inch Cartridge Drive Standards, Inc.
+ *       311 East Carrillo Street
+ *       Santa Barbara, California 93101
+ *       Telephone (805) 963-3853
+ *       Fax       (805) 962-1541
+ *       WWW       http://www.qic.org
+ *
+ *      Current QIC standard revisions (of interest) are:
+ *       QIC-40-MC,   Rev. M,  2 Sep 92.
+ *       QIC-80-MC,   Rev. N, 20 Mar 96.
+ *       QIC-80-MC,   Rev. K, 15 Dec 94.
+ *       QIC-113,     Rev. G, 15 Jun 95.
+ *       QIC-117,     Rev. J, 28 Aug 96.
+ *       QIC-122,     Rev. B,  6 Mar 91.
+ *       QIC-130,     Rev. C,  2 Sep 92.
+ *       QIC-3010-MC, Rev. F, 14 Jun 95.
+ *       QIC-3020-MC, Rev. G, 31 Aug 95.
+ *       QIC-CRF3,    Rev. B, 15 Jun 95.
+ * */
+
+/*
+ *      QIC-117 common command set rev. J.
+ *      These commands are sent to the tape unit
+ *      as number of pulses over the step line.
+ */
+
+typedef enum {
+       QIC_NO_COMMAND                  = 0,
+       QIC_RESET                       = 1,
+       QIC_REPORT_NEXT_BIT             = 2,
+       QIC_PAUSE                       = 3,
+       QIC_MICRO_STEP_PAUSE            = 4,
+       QIC_ALTERNATE_TIMEOUT           = 5,
+       QIC_REPORT_DRIVE_STATUS         = 6,
+       QIC_REPORT_ERROR_CODE           = 7,
+       QIC_REPORT_DRIVE_CONFIGURATION  = 8,
+       QIC_REPORT_ROM_VERSION          = 9,
+       QIC_LOGICAL_FORWARD             = 10,
+       QIC_PHYSICAL_REVERSE            = 11,
+       QIC_PHYSICAL_FORWARD            = 12,
+       QIC_SEEK_HEAD_TO_TRACK          = 13,
+       QIC_SEEK_LOAD_POINT             = 14,
+       QIC_ENTER_FORMAT_MODE           = 15,
+       QIC_WRITE_REFERENCE_BURST       = 16,
+       QIC_ENTER_VERIFY_MODE           = 17,
+       QIC_STOP_TAPE                   = 18,
+/* commands 19-20: reserved */
+       QIC_MICRO_STEP_HEAD_UP          = 21,
+       QIC_MICRO_STEP_HEAD_DOWN        = 22,
+       QIC_SOFT_SELECT                 = 23,
+       QIC_SOFT_DESELECT               = 24,
+       QIC_SKIP_REVERSE                = 25,
+       QIC_SKIP_FORWARD                = 26,
+       QIC_SELECT_RATE                 = 27,
+/* command 27, in ccs2: Select Rate or Format */
+       QIC_ENTER_DIAGNOSTIC_1          = 28,
+       QIC_ENTER_DIAGNOSTIC_2          = 29,
+       QIC_ENTER_PRIMARY_MODE          = 30,
+/* command 31: vendor unique */
+       QIC_REPORT_VENDOR_ID            = 32,
+       QIC_REPORT_TAPE_STATUS          = 33,
+       QIC_SKIP_EXTENDED_REVERSE       = 34,
+       QIC_SKIP_EXTENDED_FORWARD       = 35,
+       QIC_CALIBRATE_TAPE_LENGTH       = 36,
+       QIC_REPORT_FORMAT_SEGMENTS      = 37,
+       QIC_SET_FORMAT_SEGMENTS         = 38,
+/* commands 39-45: reserved */
+       QIC_PHANTOM_SELECT              = 46,
+       QIC_PHANTOM_DESELECT            = 47
+} qic117_cmd_t;
+
+typedef enum {
+       discretional = 0, required, ccs1, ccs2
+} qic_compatibility;
+
+typedef enum {
+       unused, mode, motion, report
+} command_types;
+
+struct qic117_command_table {
+       char *name;
+       __u8 mask;
+       __u8 state;
+       __u8 cmd_type;
+       __u8 non_intr;
+       __u8 level;
+};
+
+#define QIC117_COMMANDS {\
+/* command                           mask  state cmd_type           */\
+/* |    name                         |     |     |       non_intr   */\
+/* |    |                            |     |     |       |  level   */\
+/* 0*/ {NULL,                        0x00, 0x00, mode,   0, discretional},\
+/* 1*/ {"soft reset",                0x00, 0x00, motion, 1, required},\
+/* 2*/ {"report next bit",           0x00, 0x00, report, 0, required},\
+/* 3*/ {"pause",                     0x36, 0x24, motion, 1, required},\
+/* 4*/ {"micro step pause",          0x36, 0x24, motion, 1, required},\
+/* 5*/ {"alternate command timeout", 0x00, 0x00, mode,   0, required},\
+/* 6*/ {"report drive status",       0x00, 0x00, report, 0, required},\
+/* 7*/ {"report error code",         0x01, 0x01, report, 0, required},\
+/* 8*/ {"report drive configuration",0x00, 0x00, report, 0, required},\
+/* 9*/ {"report rom version",        0x00, 0x00, report, 0, required},\
+/*10*/ {"logical forward",           0x37, 0x25, motion, 0, required},\
+/*11*/ {"physical reverse",          0x17, 0x05, motion, 0, required},\
+/*12*/ {"physical forward",          0x17, 0x05, motion, 0, required},\
+/*13*/ {"seek head to track",        0x37, 0x25, motion, 0, required},\
+/*14*/ {"seek load point",           0x17, 0x05, motion, 1, required},\
+/*15*/ {"enter format mode",         0x1f, 0x05, mode,   0, required},\
+/*16*/ {"write reference burst",     0x1f, 0x05, motion, 1, required},\
+/*17*/ {"enter verify mode",         0x37, 0x25, mode,   0, required},\
+/*18*/ {"stop tape",                 0x00, 0x00, motion, 1, required},\
+/*19*/ {"reserved (19)",             0x00, 0x00, unused, 0, discretional},\
+/*20*/ {"reserved (20)",             0x00, 0x00, unused, 0, discretional},\
+/*21*/ {"micro step head up",        0x02, 0x00, motion, 0, required},\
+/*22*/ {"micro step head down",      0x02, 0x00, motion, 0, required},\
+/*23*/ {"soft select",               0x00, 0x00, mode,   0, discretional},\
+/*24*/ {"soft deselect",             0x00, 0x00, mode,   0, discretional},\
+/*25*/ {"skip segments reverse",     0x36, 0x24, motion, 1, required},\
+/*26*/ {"skip segments forward",     0x36, 0x24, motion, 1, required},\
+/*27*/ {"select rate or format",     0x03, 0x01, mode,   0, required /* [ccs2] */},\
+/*28*/ {"enter diag mode 1",         0x00, 0x00, mode,   0, discretional},\
+/*29*/ {"enter diag mode 2",         0x00, 0x00, mode,   0, discretional},\
+/*30*/ {"enter primary mode",        0x00, 0x00, mode,   0, required},\
+/*31*/ {"vendor unique (31)",        0x00, 0x00, unused, 0, discretional},\
+/*32*/ {"report vendor id",          0x00, 0x00, report, 0, required},\
+/*33*/ {"report tape status",        0x04, 0x04, report, 0, ccs1},\
+/*34*/ {"skip extended reverse",     0x36, 0x24, motion, 1, ccs1},\
+/*35*/ {"skip extended forward",     0x36, 0x24, motion, 1, ccs1},\
+/*36*/ {"calibrate tape length",     0x17, 0x05, motion, 1, ccs2},\
+/*37*/ {"report format segments",    0x17, 0x05, report, 0, ccs2},\
+/*38*/ {"set format segments",       0x17, 0x05, mode,   0, ccs2},\
+/*39*/ {"reserved (39)",             0x00, 0x00, unused, 0, discretional},\
+/*40*/ {"vendor unique (40)",        0x00, 0x00, unused, 0, discretional},\
+/*41*/ {"vendor unique (41)",        0x00, 0x00, unused, 0, discretional},\
+/*42*/ {"vendor unique (42)",        0x00, 0x00, unused, 0, discretional},\
+/*43*/ {"vendor unique (43)",        0x00, 0x00, unused, 0, discretional},\
+/*44*/ {"vendor unique (44)",        0x00, 0x00, unused, 0, discretional},\
+/*45*/ {"vendor unique (45)",        0x00, 0x00, unused, 0, discretional},\
+/*46*/ {"phantom select",            0x00, 0x00, mode,   0, discretional},\
+/*47*/ {"phantom deselect",          0x00, 0x00, mode,   0, discretional},\
+}
+
+/*
+ *      Status bits returned by QIC_REPORT_DRIVE_STATUS
+ */
+
+#define QIC_STATUS_READY       0x01    /* Drive is ready or idle. */
+#define QIC_STATUS_ERROR       0x02    /* Error detected, must read
+                                          error code to clear this */
+#define QIC_STATUS_CARTRIDGE_PRESENT 0x04      /* Tape is present */
+#define QIC_STATUS_WRITE_PROTECT 0x08  /* Tape is write protected */
+#define QIC_STATUS_NEW_CARTRIDGE 0x10  /* New cartridge inserted, must
+                                          read error status to clear. */
+#define QIC_STATUS_REFERENCED  0x20    /* Cartridge appears to have been
+                                          formatted. */
+#define QIC_STATUS_AT_BOT      0x40    /* Cartridge is at physical
+                                          beginning of tape. */
+#define QIC_STATUS_AT_EOT      0x80    /* Cartridge is at physical end
+                                          of tape. */
+/*
+ *      Status bits returned by QIC_REPORT_DRIVE_CONFIGURATION
+ */
+
+#define QIC_CONFIG_RATE_MASK   0x18
+#define QIC_CONFIG_RATE_SHIFT  3
+#define QIC_CONFIG_RATE_250    0
+#define QIC_CONFIG_RATE_500    2
+#define QIC_CONFIG_RATE_1000   3
+#define QIC_CONFIG_RATE_2000   1
+#define QIC_CONFIG_RATE_4000    0       /* since QIC-117 Rev. J */
+
+#define QIC_CONFIG_LONG                0x40    /* Extra Length Tape Detected */
+#define QIC_CONFIG_80          0x80    /* QIC-80 detected. */
+
+/*
+ *      Status bits returned by QIC_REPORT_TAPE_STATUS
+ */
+
+#define QIC_TAPE_STD_MASK       0x0f
+#define QIC_TAPE_QIC40         0x01
+#define QIC_TAPE_QIC80         0x02
+#define QIC_TAPE_QIC3020       0x03
+#define QIC_TAPE_QIC3010       0x04
+
+#define QIC_TAPE_LEN_MASK      0x70
+#define QIC_TAPE_205FT         0x10
+#define QIC_TAPE_307FT         0x20
+#define QIC_TAPE_VARIABLE      0x30
+#define QIC_TAPE_1100FT                0x40
+#define QIC_TAPE_FLEX          0x60
+
+#define QIC_TAPE_WIDE          0x80
+
+/* Define a value (in feet) slightly higher than 
+ * the possible maximum tape length.
+ */
+#define QIC_TOP_TAPE_LEN       1500
+
+/*
+ *      Errors: List of error codes, and their severity.
+ */
+
+typedef struct {
+       char *message;          /* Text describing the error. */
+       unsigned int fatal:1;   /* Non-zero if the error is fatal. */
+} ftape_error;
+
+#define QIC117_ERRORS {\
+  /* 0*/ { "No error", 0, },\
+  /* 1*/ { "Command Received while Drive Not Ready", 0, },\
+  /* 2*/ { "Cartridge Not Present or Removed", 1, },\
+  /* 3*/ { "Motor Speed Error (not within 1%)", 1, },\
+  /* 4*/ { "Motor Speed Fault (jammed, or gross speed error", 1, },\
+  /* 5*/ { "Cartridge Write Protected", 1, },\
+  /* 6*/ { "Undefined or Reserved Command Code", 1, },\
+  /* 7*/ { "Illegal Track Address Specified for Seek", 1, },\
+  /* 8*/ { "Illegal Command in Report Subcontext", 0, },\
+  /* 9*/ { "Illegal Entry into a Diagnostic Mode", 1, },\
+  /*10*/ { "Broken Tape Detected (based on hole sensor)", 1, },\
+  /*11*/ { "Warning--Read Gain Setting Error", 1, },\
+  /*12*/ { "Command Received While Error Status Pending (obs)", 1, },\
+  /*13*/ { "Command Received While New Cartridge Pending", 1, },\
+  /*14*/ { "Command Illegal or Undefined in Primary Mode", 1, },\
+  /*15*/ { "Command Illegal or Undefined in Format Mode", 1, },\
+  /*16*/ { "Command Illegal or Undefined in Verify Mode", 1, },\
+  /*17*/ { "Logical Forward Not at Logical BOT or no Format Segments in Format Mode", 1, },\
+  /*18*/ { "Logical EOT Before All Segments generated", 1, },\
+  /*19*/ { "Command Illegal When Cartridge Not Referenced", 1, },\
+  /*20*/ { "Self-Diagnostic Failed (cannot be cleared)", 1, },\
+  /*21*/ { "Warning EEPROM Not Initialized, Defaults Set", 1, },\
+  /*22*/ { "EEPROM Corrupted or Hardware Failure", 1, },\
+  /*23*/ { "Motion Time-out Error", 1, },\
+  /*24*/ { "Data Segment Too Long -- Logical Forward or Pause", 1, },\
+  /*25*/ { "Transmit Overrun (obs)", 1, },\
+  /*26*/ { "Power On Reset Occurred", 0, },\
+  /*27*/ { "Software Reset Occurred", 0, },\
+  /*28*/ { "Diagnostic Mode 1 Error", 1, },\
+  /*29*/ { "Diagnostic Mode 2 Error", 1, },\
+  /*30*/ { "Command Received During Non-Interruptible Process", 1, },\
+  /*31*/ { "Rate or Format Selection Error", 1, },\
+  /*32*/ { "Illegal Command While in High Speed Mode", 1, },\
+  /*33*/ { "Illegal Seek Segment Value", 1, },\
+  /*34*/ { "Invalid Media", 1, },\
+  /*35*/ { "Head Positioning Failure", 1, },\
+  /*36*/ { "Write Reference Burst Failure", 1, },\
+  /*37*/ { "Prom Code Missing", 1, },\
+  /*38*/ { "Invalid Format", 1, },\
+  /*39*/ { "EOT/BOT System Failure", 1, },\
+  /*40*/ { "Prom A Checksum Error", 1, },\
+  /*41*/ { "Drive Wakeup Reset Occurred", 1, },\
+  /*42*/ { "Prom B Checksum Error", 1, },\
+  /*43*/ { "Illegal Entry into Format Mode", 1, },\
+}
+
+#endif                         /* _QIC117_H */
index 5081a0640e84c95524bd8c9db6afad5b82910c1d..32448833107778264bbb010bd1395dd8177e9816 100644 (file)
@@ -132,113 +132,6 @@ struct serial_icounter_struct {
 
 
 #ifdef __KERNEL__
-/*
- * This is our internal structure for each serial port's state.
- * 
- * Many fields are paralleled by the structure used by the serial_struct
- * structure.
- *
- * For definitions of the flags field, see tty.h
- */
-
-#include <linux/termios.h>
-#include <linux/tqueue.h>
-
-/*
- * Counters of the input lines (CTS, DSR, RI, CD) interrupts
- */
-struct async_icount {
-       __u32   cts, dsr, rng, dcd, tx, rx;
-       __u32   frame, parity, overrun, brk;
-       __u32   buf_overrun;
-};
-
-struct serial_state {
-       int     magic;
-       int     baud_base;
-       int     port;
-       int     irq;
-       int     flags;
-       int     hub6;
-       int     type;
-       int     line;
-       int     xmit_fifo_size;
-       int     custom_divisor;
-       int     count;
-       unsigned short  close_delay;
-       unsigned short  closing_wait; /* time to wait before closing */
-       struct async_icount     icount; 
-       struct termios          normal_termios;
-       struct termios          callout_termios;
-       struct async_struct *info;
-};
-
-struct async_struct {
-       int                     magic;
-       int                     port;
-       int                     hub6;
-       int                     flags;
-       int                     xmit_fifo_size;
-       struct serial_state     *state;
-       struct tty_struct       *tty;
-       int                     read_status_mask;
-       int                     ignore_status_mask;
-       int                     timeout;
-       int                     quot;
-       int                     x_char; /* xon/xoff character */
-       int                     close_delay;
-       unsigned short          closing_wait;
-       unsigned short          closing_wait2;
-       int                     IER;    /* Interrupt Enable Register */
-       int                     MCR;    /* Modem control register */
-       unsigned long           event;
-       unsigned long           last_active;
-       int                     line;
-       int                     blocked_open; /* # of blocked opens */
-       long                    session; /* Session of opening process */
-       long                    pgrp; /* pgrp of opening process */
-       unsigned char           *xmit_buf;
-       int                     xmit_head;
-       int                     xmit_tail;
-       int                     xmit_cnt;
-       struct tq_struct        tqueue;
-       struct tq_struct        tqueue_hangup;
-       struct wait_queue       *open_wait;
-       struct wait_queue       *close_wait;
-       struct wait_queue       *delta_msr_wait;
-       struct async_struct     *next_port; /* For the linked list */
-       struct async_struct     *prev_port;
-};
-
-#define SERIAL_MAGIC 0x5301
-#define SSTATE_MAGIC 0x5302
-
-/*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#define SERIAL_XMIT_SIZE 4096
-
-/*
- * Events are used to schedule things to happen at timer-interrupt
- * time, instead of at rs interrupt time.
- */
-#define RS_EVENT_WRITE_WAKEUP  0
-
-/*
- * Multiport serial configuration structure --- internal structure
- */
-struct rs_multiport_struct {
-       int             port1;
-       unsigned char   mask1, match1;
-       int             port2;
-       unsigned char   mask2, match2;
-       int             port3;
-       unsigned char   mask3, match3;
-       int             port4;
-       unsigned char   mask4, match4;
-       int             port_monitor;
-};
-
 /* Export to allow PCMCIA to use this - Dave Hinds */
 extern int register_serial(struct serial_struct *req);
 extern void unregister_serial(int line);
diff --git a/include/linux/serialP.h b/include/linux/serialP.h
new file mode 100644 (file)
index 0000000..6bf0746
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Private header file for the (dumb) serial driver
+ *
+ * Copyright (C) 1997 by Theodore Ts'o.
+ * 
+ * Redistribution of this file is permitted under the terms of the GNU 
+ * Public License (GPL)
+ */
+
+#ifndef _LINUX_SERIALP_H
+#define _LINUX_SERIALP_H
+
+/*
+ * This is our internal structure for each serial port's state.
+ * 
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+
+/*
+ * Counters of the input lines (CTS, DSR, RI, CD) interrupts
+ */
+struct async_icount {
+       __u32   cts, dsr, rng, dcd, tx, rx;
+       __u32   frame, parity, overrun, brk;
+       __u32   buf_overrun;
+};
+
+struct serial_state {
+       int     magic;
+       int     baud_base;
+       int     port;
+       int     irq;
+       int     flags;
+       int     hub6;
+       int     type;
+       int     line;
+       int     xmit_fifo_size;
+       int     custom_divisor;
+       int     count;
+       unsigned short  close_delay;
+       unsigned short  closing_wait; /* time to wait before closing */
+       struct async_icount     icount; 
+       struct termios          normal_termios;
+       struct termios          callout_termios;
+       struct async_struct *info;
+};
+
+struct async_struct {
+       int                     magic;
+       int                     port;
+       int                     hub6;
+       int                     flags;
+       int                     xmit_fifo_size;
+       struct serial_state     *state;
+       struct tty_struct       *tty;
+       int                     read_status_mask;
+       int                     ignore_status_mask;
+       int                     timeout;
+       int                     quot;
+       int                     x_char; /* xon/xoff character */
+       int                     close_delay;
+       unsigned short          closing_wait;
+       unsigned short          closing_wait2;
+       int                     IER;    /* Interrupt Enable Register */
+       int                     MCR;    /* Modem control register */
+       unsigned long           event;
+       unsigned long           last_active;
+       int                     line;
+       int                     blocked_open; /* # of blocked opens */
+       long                    session; /* Session of opening process */
+       long                    pgrp; /* pgrp of opening process */
+       unsigned char           *xmit_buf;
+       int                     xmit_head;
+       int                     xmit_tail;
+       int                     xmit_cnt;
+       struct tq_struct        tqueue;
+       struct wait_queue       *open_wait;
+       struct wait_queue       *close_wait;
+       struct wait_queue       *delta_msr_wait;
+       struct async_struct     *next_port; /* For the linked list */
+       struct async_struct     *prev_port;
+};
+
+#define SERIAL_MAGIC 0x5301
+#define SSTATE_MAGIC 0x5302
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP  0
+
+/*
+ * Multiport serial configuration structure --- internal structure
+ */
+struct rs_multiport_struct {
+       int             port1;
+       unsigned char   mask1, match1;
+       int             port2;
+       unsigned char   mask2, match2;
+       int             port3;
+       unsigned char   mask3, match3;
+       int             port4;
+       unsigned char   mask4, match4;
+       int             port_monitor;
+};
+
+#endif /* _LINUX_SERIAL_H */
index 9298bcbbca2d20a8d7b569b1146f5e7a7f4b1c24..d34e9d77d936ccca3b237223960c7babdd054181 100644 (file)
@@ -65,6 +65,18 @@ smb_vfree(void *obj)
 
 #endif /* DEBUG_SMB_MALLOC */
 
+/*
+ * Flags for the in-memory inode
+ */
+#define SMB_F_CACHEVALID       0x01    /* directory cache valid */
+#define SMB_F_LOCALWRITE       0x02    /* file modified locally */
+
+/*
+ * Bug fix flags
+ */
+#define SMB_FIX_WIN95  0x0001  /* Win 95 server */
+#define SMB_FIX_OLDATTR        0x0002  /* Use core getattr (Win 95 speedup) */
+
 /* linux/fs/smbfs/mmap.c */
 int smb_mmap(struct file *, struct vm_area_struct *);
 
@@ -80,35 +92,29 @@ int smb_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
 
 /* linux/fs/smbfs/inode.c */
 struct super_block *smb_read_super(struct super_block *, void *, int);
-extern int init_smb_fs(void);
+void smb_get_inode_attr(struct inode *, struct smb_fattr *);
 void smb_invalidate_inodes(struct smb_sb_info *);
 int  smb_revalidate_inode(struct inode *);
 int  smb_refresh_inode(struct inode *);
 int  smb_notify_change(struct inode *, struct iattr *);
-void smb_invalidate_connection(struct smb_sb_info *);
-int  smb_conn_is_valid(struct smb_sb_info *);
 unsigned long smb_invent_inos(unsigned long);
 struct inode *smb_iget(struct super_block *, struct smb_fattr *);
 
 /* linux/fs/smbfs/proc.c */
-__u32 smb_len(unsigned char *packet);
-__u8 *smb_encode_smb_length(__u8 *p, __u32 len);
-__u8 *smb_setup_header(struct smb_sb_info *server, __u8 command,
-                      __u16 wct, __u16 bcc);
-int smb_offerconn(struct smb_sb_info *server);
-int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt);
+__u32 smb_len(unsigned char *);
+__u8 *smb_encode_smb_length(__u8 *, __u32);
+__u8 *smb_setup_header(struct smb_sb_info *, __u8, __u16, __u16);
+int smb_get_rsize(struct smb_sb_info *);
+int smb_get_wsize(struct smb_sb_info *);
+int smb_offerconn(struct smb_sb_info *);
+int smb_newconn(struct smb_sb_info *, struct smb_conn_opt *);
 int smb_close(struct inode *);
 void smb_close_dentry(struct dentry *);
+int smb_close_fileid(struct dentry *, __u16);
 int smb_open(struct dentry *, int);
-static inline int
-smb_is_open(struct inode *i)
-{
-       return (i->u.smbfs_i.open == SMB_SERVER(i)->generation);
-}
-
 int smb_proc_read(struct inode *, off_t, int, char *);
 int smb_proc_write(struct inode *, off_t, int, const char *);
-int smb_proc_create(struct dentry *, struct qstr *, __u16, time_t);
+int smb_proc_create(struct dentry *, struct qstr *, __u16, time_t, __u16 *);
 int smb_proc_mv(struct dentry *, struct qstr *, struct dentry *, struct qstr *);
 int smb_proc_mkdir(struct dentry *, struct qstr *);
 int smb_proc_rmdir(struct dentry *, struct qstr *);
@@ -121,7 +127,13 @@ int smb_proc_reconnect(struct smb_sb_info *);
 int smb_proc_connect(struct smb_sb_info *);
 int smb_proc_disconnect(struct smb_sb_info *);
 int smb_proc_trunc(struct smb_sb_info *, __u16, __u32);
-void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *);
+void smb_init_root_dirent(struct smb_sb_info *, struct smb_fattr *);
+
+static inline int
+smb_is_open(struct inode *i)
+{
+       return (i->u.smbfs_i.open == SMB_SERVER(i)->generation);
+}
 
 /* linux/fs/smbfs/sock.c */
 int smb_round_length(int);
index 954434b3df8ed0e21836614557f4ab2059be1f42..71e57ea6a5c08762648c45c5e70d34e999a425cd 100644 (file)
@@ -28,6 +28,7 @@ struct smb_inode_info {
        __u16 access;           /* Access bits. */
        __u16 cache_valid;      /* dircache valid? */
        unsigned long oldmtime; /* last time refreshed */
+       unsigned long closed;   /* timestamp when closed */
        void * dentry;          /* The dentry we were opened with */
 };
 
index a1443646b8b471a8fb483d7ae9282df764196a7e..0f25e0265393d955ef4bf17e7e98bdad752df1cf 100644 (file)
@@ -2,12 +2,12 @@
 #define _SMBNO_H_
 
 /* these define the attribute byte as seen by DOS */
-#define aRONLY (1L<<0)
-#define aHIDDEN (1L<<1)
-#define aSYSTEM (1L<<2)
-#define aVOLID (1L<<3)
-#define aDIR (1L<<4)
-#define aARCH (1L<<5)
+#define aRONLY (1L<<0)
+#define aHIDDEN        (1L<<1)
+#define aSYSTEM        (1L<<2)
+#define aVOLID (1L<<3)
+#define aDIR   (1L<<4)
+#define aARCH  (1L<<5)
 
 /* error classes */
 #define SUCCESS 0  /* The request was successful. */
 #define ERRsharebufexc 36       /* share buffer exceeded */
 #define ERRdiskfull 39
 
+/*
+ * Access modes when opening a file
+ */
+#define SMB_ACCMASK    0x0003
+#define SMB_O_RDONLY   0x0000
+#define SMB_O_WRONLY   0x0001
+#define SMB_O_RDWR     0x0002
+
 /* offsets into message for common items */
 #define smb_com 8
 #define smb_rcls 9
index 61b24941ea860008de6f9399c53ed0aba0807320..b4344e5ad633dcf2dd8b06aee769a3115b542e2f 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/tqueue.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_ldisc.h>
+#include <linux/serialP.h>
 
 #include <asm/system.h>
 
@@ -223,14 +224,17 @@ struct tty_struct {
        int count;
        struct winsize winsize;
        unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;
+       unsigned char low_latency:1;
        unsigned char ctrl_status;
 
        struct tty_struct *link;
        struct fasync_struct *fasync;
        struct tty_flip_buffer flip;
        int max_flip_cnt;
+       int alt_speed;          /* For magic substitution of 38400 bps */
        struct wait_queue *write_wait;
        struct wait_queue *read_wait;
+       struct tq_struct tq_hangup;
        void *disc_data;
        void *driver_data;
 
@@ -329,6 +333,8 @@ extern void tty_unhangup(struct file *filp);
 extern int tty_hung_up_p(struct file * filp);
 extern void do_SAK(struct tty_struct *tty);
 extern void disassociate_ctty(int priv);
+extern void tty_flip_buffer_push(struct tty_struct *tty);
+extern int tty_get_baud_rate(struct tty_struct *tty);
 
 /* n_tty.c */
 extern struct tty_ldisc tty_ldisc_N_TTY;
index a2313af6af95b4ad02cdd7a249dd95357137c81a..65853d7011c07e6a2fc1b6ee1c020db62202da8a 100644 (file)
  *     This routine notifies the tty driver that it should hangup the
  *     tty device.
  *
+ * void (*break_ctl)(struct tty_stuct *tty, int state);
+ *
+ *     This optional routine requests the tty driver to turn on or
+ *     off BREAK status on the RS-232 port.  If state is -1,
+ *     then the BREAK status should be turned on; if state is 0, then
+ *     BREAK should be turned off.
+ *
+ *     If this routine is implemented, the high-level tty driver will
+ *     handle the following ioctls: TCSBRK, TCSBRKP, TIOCSBRK,
+ *     TIOCCBRK.  Otherwise, these ioctls will be passed down to the
+ *     driver to handle.
+ *
  * void (*wait_until_sent)(struct tty_struct *tty, int timeout);
  * 
  *     This routine waits until the device has written out all of the
@@ -148,6 +160,7 @@ struct tty_driver {
        void (*stop)(struct tty_struct *tty);
        void (*start)(struct tty_struct *tty);
        void (*hangup)(struct tty_struct *tty);
+       void (*break_ctl)(struct tty_struct *tty, int state);
        void (*flush_buffer)(struct tty_struct *tty);
        void (*set_ldisc)(struct tty_struct *tty);
        void (*wait_until_sent)(struct tty_struct *tty, int timeout);
diff --git a/include/linux/zftape.h b/include/linux/zftape.h
new file mode 100644 (file)
index 0000000..ec5d2ff
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef _ZFTAPE_H
+#define _ZFTAPE_H
+
+/*
+ * Copyright (C) 1996, 1997 Claus-Justus Heine.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.  If not, write to
+ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ *
+ * $Source: /homes/cvs/ftape-stacked/include/linux/zftape.h,v $
+ * $Revision: 1.12 $
+ * $Date: 1997/10/21 11:02:37 $
+ *
+ *      Special ioctl and other global info for the zftape VFS
+ *      interface for the QIC-40/80/3010/3020 floppy-tape driver for
+ *      Linux.
+ */
+
+#define ZFTAPE_VERSION  "zftape for " FTAPE_VERSION
+
+#include <linux/ftape.h>
+
+#define ZFTAPE_LABEL       "Ftape - The Linux Floppy Tape Project!"
+
+/* Bits of the minor device number that control the operation mode */
+#define ZFT_Q80_MODE           (1 << 3)
+#define ZFT_ZIP_MODE           (1 << 4)
+#define ZFT_RAW_MODE           (1 << 5)
+#define ZFT_MINOR_OP_MASK      (ZFT_Q80_MODE   |       \
+                                ZFT_ZIP_MODE   |       \
+                                ZFT_RAW_MODE)
+#define ZFT_MINOR_MASK         (FTAPE_SEL_MASK         |       \
+                                ZFT_MINOR_OP_MASK      |       \
+                                FTAPE_NO_REWIND)
+
+#ifdef ZFT_OBSOLETE
+struct mtblksz {
+       unsigned int mt_blksz;
+};
+#define MTIOC_ZFTAPE_GETBLKSZ _IOR('m', 104, struct mtblksz)
+#endif
+
+#ifdef __KERNEL__
+
+extern int zft_init(void);
+
+extern inline __s64 zft_div_blksz(__s64 value, __u32 blk_sz)
+{
+       if (blk_sz == 1) {
+               return value;
+       } else {
+               return (__s64)(((__u32)(value >> 10) + (blk_sz >> 10) - 1) 
+                              / (blk_sz >> 10));
+       } 
+}
+
+extern inline __s64 zft_mul_blksz(__s64 value, __u32 blk_sz)
+{
+       if (blk_sz == 1) {
+               return value;
+       } else {
+               /*  if blk_sz != 1, then it is a multiple of 1024. In
+                *  this case, `value' will also fit into 32 bits.
+                * 
+                *  Actually, this limits the capacity to 42
+                *  bits. This is (2^32)*1024, roughly a thousand
+                *  times 2GB, or 3 Terabytes. Hopefully this is enough
+                */
+               return(__s64)(((__u32)(value)*(blk_sz>>10))<<10);
+       }
+}
+
+#endif
+
+#endif
index 9aaf4e308241f5c0f6649a48e0863e764db1ea1c..2def730da5d0fa69a5315797096e68a107a54233 100644 (file)
@@ -242,6 +242,9 @@ extern void plip_setup(char *str, int *ints);
 #ifdef CONFIG_HFMODEM
 extern void hfmodem_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_FTAPE
+extern void ftape_setup(char *str, int *ints);
+#endif
 
 #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
 extern void ipc_init(void);
@@ -559,6 +562,9 @@ struct {
 #ifdef CONFIG_PMAC_CONSOLE
        { "console=", pmac_cons_setup },
        { "vmode=", pmac_vmode_setup },
+#endif
+#ifdef CONFIG_FTAPE
+       { "ftape=", ftape_setup},
 #endif
        { 0, 0 }
 };
index bf9aa5d64247dfc2250f6b05176fb74ec99f654a..2805ca581001a3a2dc8f716ae5f4e7c1e80bd90f 100644 (file)
@@ -242,6 +242,8 @@ EXPORT_SYMBOL(tty_hangup);
 EXPORT_SYMBOL(tty_wait_until_sent);
 EXPORT_SYMBOL(tty_check_change);
 EXPORT_SYMBOL(tty_hung_up_p);
+EXPORT_SYMBOL(tty_flip_buffer_push);
+EXPORT_SYMBOL(tty_get_baud_rate);
 EXPORT_SYMBOL(do_SAK);
 EXPORT_SYMBOL(console_print);
 
index a6eb1b2a36455a5db5bbc51b9f5b7bed42ae55b5..0efb65febccc0199a725e3cb7463a72751dd6989 100644 (file)
@@ -1220,9 +1220,7 @@ int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
 static int msync_interval(struct vm_area_struct * vma,
        unsigned long start, unsigned long end, int flags)
 {
-       if (!vma->vm_dentry)
-               return 0;
-       if (vma->vm_ops->sync) {
+       if (vma->vm_dentry && vma->vm_ops && vma->vm_ops->sync) {
                int error;
                error = vma->vm_ops->sync(vma, start, end-start, flags);
                if (!error && (flags & MS_SYNC)) {
index 40cee309e5871866243c8d3bb4d2ceb2e82e9499..d2128c1802ddb4686765402fc9ee7f385eaebde9 100644 (file)
@@ -81,11 +81,11 @@ static unsigned int netlink_poll(struct file *file, poll_table * wait)
  *     Write a message to the kernel side of a communication link
  */
  
-static long netlink_write(struct inode * inode, struct file * file,
-                         const char * buf, unsigned long count)
+static ssize_t netlink_write(struct file * file, const char * buf,
+                       size_t count,loff_t *ppos)
 {
        int err; 
-       unsigned int minor = MINOR(inode->i_rdev);
+       unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
        struct sk_buff *skb;
        skb=alloc_skb(count, GFP_KERNEL);
        err = copy_from_user(skb_put(skb,count),buf, count);
@@ -96,11 +96,11 @@ static long netlink_write(struct inode * inode, struct file * file,
  *     Read a message from the kernel side of the communication link
  */
 
-static long netlink_read(struct inode * inode, struct file * file, char * buf,
-                        unsigned long count)
+static ssize_t  netlink_read(struct file * file, char * buf,
+                        size_t count,loff_t *ppos)
 {
        int err; 
-       unsigned int minor = MINOR(inode->i_rdev);
+       unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
        struct sk_buff *skb;
        cli();
        while((skb=skb_dequeue(&skb_queue_rd[minor]))==NULL)
index ce3cd7bd00e2a6cea3e14c0abb3086d0b4c2df8a..d49bbb59f7672c321911e164e94d19801cfab070 100644 (file)
@@ -338,8 +338,10 @@ call_reserveresult(struct rpc_task *task)
        } else {
                task->tk_action = NULL;
        }
-       if (!task->tk_rqstp)
+       if (!task->tk_rqstp) {
+               printk("RPC: task has no request, exit EIO\n");
                rpc_exit(task, -EIO);
+       }
 }
 
 /*
@@ -415,6 +417,7 @@ call_encode(struct rpc_task *task)
        /* Encode header and provided arguments */
        encode = rpcproc_encode(clnt, task->tk_proc);
        if (!(p = call_header(task))) {
+               printk("RPC: call_header failed, exit EIO\n");
                rpc_exit(task, -EIO);
        } else
        if ((status = encode(req, p, task->tk_argp)) < 0) {
@@ -749,6 +752,7 @@ garbage:
                task->tk_action = call_encode;
                return NULL;
        }
+       printk("RPC: garbage, exit EIO\n");
        rpc_exit(task, -EIO);
        return NULL;
 }
index 0372500ee9eae52dd5a1e7c1aace9643c35acce5..15703111df302adde0ef84979f9254847e7a8cee 100644 (file)
@@ -1009,18 +1009,11 @@ xprt_reserve_status(struct rpc_task *task)
        } else if (!RPCXPRT_CONGESTED(xprt)) {
                /* OK: There's room for us. Grab a free slot and bump
                 * congestion value */
-               if (!(req = xprt->free)) {
-                       /* printk("RPC: inconsistent free list!\n"); */
-                       rpc_debug = ~0;
-                       dprintk("RPC: %4d inconsistent free list "
-                                       "(cong %ld cwnd %ld)\n",
-                                       task->tk_pid, xprt->cong, xprt->cwnd);
-                       goto bummer;
-               }
-               if (req->rq_xid) {
-                       printk("RPC: used rqst slot %p on free list!\n", req);
-                       goto bummer;
-               }
+               req = xprt->free;
+               if (!req)
+                       goto bad_list;
+               if (req->rq_xid)
+                       goto bad_used;
                xprt->free     = req->rq_next;
                xprt->cong    += RPC_CWNDSCALE;
                task->tk_rqstp = req;
@@ -1035,6 +1028,13 @@ xprt_reserve_status(struct rpc_task *task)
 
        return;
 
+bad_list:
+       printk("RPC: %4d inconsistent free list (cong %ld cwnd %ld)\n",
+               task->tk_pid, xprt->cong, xprt->cwnd);
+       rpc_debug = ~0;
+       goto bummer;
+bad_used:
+       printk("RPC: used rqst slot %p on free list!\n", req);
 bummer:
        task->tk_status = -EIO;
        xprt->free = NULL;