diff -u --recursive --new-file v2.1.65/linux/CREDITS linux/CREDITS --- v2.1.65/linux/CREDITS Tue Nov 18 17:22:07 1997 +++ linux/CREDITS Sat Nov 22 09:48:07 1997 @@ -1609,11 +1609,12 @@ 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 diff -u --recursive --new-file v2.1.65/linux/Documentation/00-INDEX linux/Documentation/00-INDEX --- v2.1.65/linux/Documentation/00-INDEX Tue Sep 23 16:48:46 1997 +++ linux/Documentation/00-INDEX Tue Nov 25 14:45:26 1997 @@ -36,6 +36,8 @@ - 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 diff -u --recursive --new-file v2.1.65/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.1.65/linux/Documentation/Configure.help Mon Nov 17 18:47:20 1997 +++ linux/Documentation/Configure.help Tue Nov 25 14:45:26 1997 @@ -5009,22 +5009,278 @@ 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 @@ # 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 diff -u --recursive --new-file v2.1.65/linux/Documentation/devices.tex linux/Documentation/devices.tex --- v2.1.65/linux/Documentation/devices.tex Wed Nov 12 13:34:25 1997 +++ linux/Documentation/devices.tex Tue Nov 25 14:45:26 1997 @@ -797,14 +797,30 @@ \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 @@ \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} diff -u --recursive --new-file v2.1.65/linux/Documentation/devices.txt linux/Documentation/devices.txt --- v2.1.65/linux/Documentation/devices.txt Wed Nov 12 13:34:25 1997 +++ linux/Documentation/devices.txt Tue Nov 25 14:45:26 1997 @@ -507,14 +507,30 @@ 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 @@ /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 diff -u --recursive --new-file v2.1.65/linux/Documentation/filesystems/smbfs.txt linux/Documentation/filesystems/smbfs.txt --- v2.1.65/linux/Documentation/filesystems/smbfs.txt Wed Sep 3 20:52:41 1997 +++ linux/Documentation/filesystems/smbfs.txt Mon Nov 24 10:30:40 1997 @@ -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. +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 a special mount program, which can be found in -the smbfs package, found on +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/ + 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 -u --recursive --new-file v2.1.65/linux/Documentation/ftape.txt linux/Documentation/ftape.txt --- v2.1.65/linux/Documentation/ftape.txt Wed Dec 31 16:00:00 1969 +++ linux/Documentation/ftape.txt Tue Nov 25 14:45:26 1997 @@ -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 diff -u --recursive --new-file v2.1.65/linux/MAINTAINERS linux/MAINTAINERS --- v2.1.65/linux/MAINTAINERS Wed Nov 12 13:34:25 1997 +++ linux/MAINTAINERS Tue Nov 25 14:45:26 1997 @@ -239,6 +239,7 @@ 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 diff -u --recursive --new-file v2.1.65/linux/Makefile linux/Makefile --- v2.1.65/linux/Makefile Tue Nov 18 17:22:07 1997 +++ linux/Makefile Tue Nov 18 17:22:31 1997 @@ -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/) diff -u --recursive --new-file v2.1.65/linux/arch/alpha/lib/strrchr.S linux/arch/alpha/lib/strrchr.S --- v2.1.65/linux/arch/alpha/lib/strrchr.S Fri Oct 18 01:53:20 1996 +++ linux/arch/alpha/lib/strrchr.S Wed Nov 19 14:07:33 1997 @@ -21,7 +21,7 @@ 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 @@ $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 @@ 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 diff -u --recursive --new-file v2.1.65/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v2.1.65/linux/arch/i386/defconfig Wed Nov 12 13:34:25 1997 +++ linux/arch/i386/defconfig Tue Nov 25 14:59:16 1997 @@ -211,7 +211,7 @@ 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,13 +245,17 @@ # 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 # CONFIG_NVRAM is not set # CONFIG_JOYSTICK is not set # CONFIG_MISC_RADIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set # # Sound diff -u --recursive --new-file v2.1.65/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S --- v2.1.65/linux/arch/i386/kernel/entry.S Tue Nov 18 17:22:07 1997 +++ linux/arch/i386/kernel/entry.S Tue Nov 18 17:16:54 1997 @@ -349,10 +349,6 @@ 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) diff -u --recursive --new-file v2.1.65/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c --- v2.1.65/linux/arch/i386/kernel/traps.c Tue Nov 18 17:22:07 1997 +++ linux/arch/i386/kernel/traps.c Tue Nov 18 17:14:46 1997 @@ -103,7 +103,6 @@ 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 @@ __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 @@ * 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)); - } diff -u --recursive --new-file v2.1.65/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c --- v2.1.65/linux/arch/i386/mm/fault.c Tue Nov 18 17:22:07 1997 +++ linux/arch/i386/mm/fault.c Tue Nov 18 17:33:37 1997 @@ -74,6 +74,9 @@ 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 @@ * 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 @@ 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 @@ 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); -} - - diff -u --recursive --new-file v2.1.65/linux/drivers/block/floppy.c linux/drivers/block/floppy.c --- v2.1.65/linux/drivers/block/floppy.c Tue Nov 18 17:22:07 1997 +++ linux/drivers/block/floppy.c Tue Nov 25 14:45:26 1997 @@ -4016,11 +4016,6 @@ 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 @@ static int floppy_grab_irq_and_dma(void) { - int i; unsigned long flags; INT_OFF; @@ -4052,16 +4046,6 @@ } 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 @@ 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 void floppy_release_irq_and_dma(void) { + int old_fdc; #ifdef FLOPPY_SANITY_CHECK #ifndef __sparc__ int drive; @@ -4136,6 +4151,13 @@ 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; } @@ -4223,12 +4245,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"); diff -u --recursive --new-file v2.1.65/linux/drivers/block/md.c linux/drivers/block/md.c --- v2.1.65/linux/drivers/block/md.c Wed Nov 12 13:34:25 1997 +++ linux/drivers/block/md.c Wed Nov 19 11:00:02 1997 @@ -541,6 +541,9 @@ 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; diff -u --recursive --new-file v2.1.65/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog --- v2.1.65/linux/drivers/char/ChangeLog Thu Jun 26 12:33:38 1997 +++ linux/drivers/char/ChangeLog Mon Nov 24 08:45:44 1997 @@ -1,3 +1,47 @@ +Mon Nov 24 10:37:49 1997 Theodore Ts'o + + * 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 + + * 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 * serial.c (begin_break, end_break, rs_ioctl): Applied patch diff -u --recursive --new-file v2.1.65/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v2.1.65/linux/drivers/char/Config.in Wed Nov 12 13:34:25 1997 +++ linux/drivers/char/Config.in Tue Nov 25 14:45:26 1997 @@ -76,11 +76,6 @@ 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 @@ 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 diff -u --recursive --new-file v2.1.65/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.1.65/linux/drivers/char/Makefile Wed Nov 12 13:34:25 1997 +++ linux/drivers/char/Makefile Tue Nov 25 14:45:26 1997 @@ -297,8 +297,11 @@ 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 diff -u --recursive --new-file v2.1.65/linux/drivers/char/esp.c linux/drivers/char/esp.c --- v2.1.65/linux/drivers/char/esp.c Wed Sep 24 20:05:46 1997 +++ linux/drivers/char/esp.c Mon Nov 24 08:45:44 1997 @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -140,11 +141,8 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } /* - * 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 @@ { 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 @@ } 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 @@ 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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/Config.in linux/drivers/char/ftape/Config.in --- v2.1.65/linux/drivers/char/ftape/Config.in Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/Config.in Tue Nov 25 14:45:26 1997 @@ -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 diff -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/Makefile linux/drivers/char/ftape/Makefile --- v2.1.65/linux/drivers/char/ftape/Makefile Mon Apr 22 01:46:18 1996 +++ linux/drivers/char/ftape/Makefile Tue Nov 25 14:45:26 1997 @@ -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. +# +# $Source: /homes/cvs/ftape-stacked/ftape/Makefile,v $ +# $Revision: 1.4 $ +# $Date: 1997/10/05 19:17:56 $ # -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. +# Makefile for the QIC-40/80/3010/3020 floppy-tape driver for +# Linux. # -# 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! # -# For a Mountain MACH-2 controller, try -#FDC_OPT = -DMACH2 -DFDC_BASE=0x1E0 -DFDC_IRQ=6 -DFDC_DMA=2 +# This isn't used inside the kernel, only for my private development +# version # -# 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 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 diff -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/README.PCI linux/drivers/char/ftape/README.PCI --- v2.1.65/linux/drivers/char/ftape/README.PCI Wed Mar 6 05:07:18 1996 +++ linux/drivers/char/ftape/README.PCI Tue Nov 25 14:45:26 1997 @@ -30,7 +30,7 @@ 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 @@ 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 diff -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/RELEASE-NOTES linux/drivers/char/ftape/RELEASE-NOTES --- v2.1.65/linux/drivers/char/ftape/RELEASE-NOTES Fri May 31 03:54:08 1996 +++ linux/drivers/char/ftape/RELEASE-NOTES Tue Nov 25 14:45:26 1997 @@ -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, ==== + +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 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 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 +has resigned from maintaining ftape, and I, +Claus-Justus Heine , +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 + +- 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 @@ 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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/calibr.c linux/drivers/char/ftape/calibr.c --- v2.1.65/linux/drivers/char/ftape/calibr.c Thu Mar 14 01:51:41 1996 +++ linux/drivers/char/ftape/calibr.c Wed Dec 31 16:00:00 1969 @@ -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 -#include -#include -#include -#include - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/calibr.h linux/drivers/char/ftape/calibr.h --- v2.1.65/linux/drivers/char/ftape/calibr.h Mon Sep 30 00:40:01 1996 +++ linux/drivers/char/ftape/calibr.h Wed Dec 31 16:00:00 1969 @@ -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 - -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/compressor/Makefile linux/drivers/char/ftape/compressor/Makefile --- v2.1.65/linux/drivers/char/ftape/compressor/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/compressor/Makefile Tue Nov 25 14:45:27 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/compressor/lzrw3.c linux/drivers/char/ftape/compressor/lzrw3.c --- v2.1.65/linux/drivers/char/ftape/compressor/lzrw3.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/compressor/lzrw3.c Tue Nov 25 14:45:27 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/compressor/lzrw3.h linux/drivers/char/ftape/compressor/lzrw3.h --- v2.1.65/linux/drivers/char/ftape/compressor/lzrw3.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/compressor/lzrw3.h Tue Nov 25 14:45:27 1997 @@ -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 +#include + +/******************************************************************************/ +/* */ +/* 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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/compressor/zftape-compress.c linux/drivers/char/ftape/compressor/zftape-compress.c --- v2.1.65/linux/drivers/char/ftape/compressor/zftape-compress.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/compressor/zftape-compress.c Tue Nov 25 14:45:27 1997 @@ -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 +#include +#include +#include + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/compressor/zftape-compress.h linux/drivers/char/ftape/compressor/zftape-compress.h --- v2.1.65/linux/drivers/char/ftape/compressor/zftape-compress.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/compressor/zftape-compress.h Tue Nov 25 14:45:27 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ecc.c linux/drivers/char/ftape/ecc.c --- v2.1.65/linux/drivers/char/ftape/ecc.c Thu Mar 14 01:51:41 1996 +++ linux/drivers/char/ftape/ecc.c Wed Dec 31 16:00:00 1969 @@ -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 -#include -#include - -#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., L00 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; - } -} - - - -/* - * 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) */ - - - -/* - * 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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ecc.h linux/drivers/char/ftape/ecc.h --- v2.1.65/linux/drivers/char/ftape/ecc.h Wed Mar 6 05:07:19 1996 +++ linux/drivers/char/ftape/ecc.h Wed Dec 31 16:00:00 1969 @@ -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< -#include - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/fc-10.h linux/drivers/char/ftape/fc-10.h --- v2.1.65/linux/drivers/char/ftape/fc-10.h Wed Mar 6 05:07:19 1996 +++ linux/drivers/char/ftape/fc-10.h Wed Dec 31 16:00:00 1969 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/fdc-io.c linux/drivers/char/ftape/fdc-io.c --- v2.1.65/linux/drivers/char/ftape/fdc-io.c Wed Sep 24 20:05:46 1997 +++ linux/drivers/char/ftape/fdc-io.c Wed Dec 31 16:00:00 1969 @@ -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 -#include -#include -#include -#include -#include -#include -#include - -#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(®[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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/fdc-io.h linux/drivers/char/ftape/fdc-io.h --- v2.1.65/linux/drivers/char/ftape/fdc-io.h Mon Sep 30 00:39:58 1996 +++ linux/drivers/char/ftape/fdc-io.h Wed Dec 31 16:00:00 1969 @@ -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 - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/fdc-isr.c linux/drivers/char/ftape/fdc-isr.c --- v2.1.65/linux/drivers/char/ftape/fdc-isr.c Sat Aug 31 23:14:45 1996 +++ linux/drivers/char/ftape/fdc-isr.c Wed Dec 31 16:00:00 1969 @@ -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 -#include -#include - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/fdc-isr.h linux/drivers/char/ftape/fdc-isr.h --- v2.1.65/linux/drivers/char/ftape/fdc-isr.h Wed Mar 6 05:07:19 1996 +++ linux/drivers/char/ftape/fdc-isr.h Wed Dec 31 16:00:00 1969 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-bsm.c linux/drivers/char/ftape/ftape-bsm.c --- v2.1.65/linux/drivers/char/ftape/ftape-bsm.c Thu Mar 14 01:53:44 1996 +++ linux/drivers/char/ftape/ftape-bsm.c Wed Dec 31 16:00:00 1969 @@ -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 -#include - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-bsm.h linux/drivers/char/ftape/ftape-bsm.h --- v2.1.65/linux/drivers/char/ftape/ftape-bsm.h Wed Mar 6 05:07:19 1996 +++ linux/drivers/char/ftape/ftape-bsm.h Wed Dec 31 16:00:00 1969 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-ctl.c linux/drivers/char/ftape/ftape-ctl.c --- v2.1.65/linux/drivers/char/ftape/ftape-ctl.c Tue Oct 29 15:40:36 1996 +++ linux/drivers/char/ftape/ftape-ctl.c Wed Dec 31 16:00:00 1969 @@ -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 -#include -#include -#include - -#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 "); - 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 "); - 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 "); - 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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-ctl.h linux/drivers/char/ftape/ftape-ctl.h --- v2.1.65/linux/drivers/char/ftape/ftape-ctl.h Mon Sep 30 00:39:58 1996 +++ linux/drivers/char/ftape/ftape-ctl.h Wed Dec 31 16:00:00 1969 @@ -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 -#include - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-eof.c linux/drivers/char/ftape/ftape-eof.c --- v2.1.65/linux/drivers/char/ftape/ftape-eof.c Thu Mar 14 01:53:44 1996 +++ linux/drivers/char/ftape/ftape-eof.c Wed Dec 31 16:00:00 1969 @@ -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 -#include -#include - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-eof.h linux/drivers/char/ftape/ftape-eof.h --- v2.1.65/linux/drivers/char/ftape/ftape-eof.h Wed Mar 6 05:07:19 1996 +++ linux/drivers/char/ftape/ftape-eof.h Wed Dec 31 16:00:00 1969 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-io.c linux/drivers/char/ftape/ftape-io.c --- v2.1.65/linux/drivers/char/ftape/ftape-io.c Wed Sep 24 20:05:46 1997 +++ linux/drivers/char/ftape/ftape-io.c Wed Dec 31 16:00:00 1969 @@ -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 -#include -#include -#include -#include -#include -#include -#include - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-io.h linux/drivers/char/ftape/ftape-io.h --- v2.1.65/linux/drivers/char/ftape/ftape-io.h Mon Sep 30 00:39:58 1996 +++ linux/drivers/char/ftape/ftape-io.h Wed Dec 31 16:00:00 1969 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-read.c linux/drivers/char/ftape/ftape-read.c --- v2.1.65/linux/drivers/char/ftape/ftape-read.c Tue Oct 29 15:40:36 1996 +++ linux/drivers/char/ftape/ftape-read.c Wed Dec 31 16:00:00 1969 @@ -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 -#include -#include -#include -#include - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-read.h linux/drivers/char/ftape/ftape-read.h --- v2.1.65/linux/drivers/char/ftape/ftape-read.h Wed Mar 6 05:07:20 1996 +++ linux/drivers/char/ftape/ftape-read.h Wed Dec 31 16:00:00 1969 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-rw.c linux/drivers/char/ftape/ftape-rw.c --- v2.1.65/linux/drivers/char/ftape/ftape-rw.c Thu Apr 11 23:49:35 1996 +++ linux/drivers/char/ftape/ftape-rw.c Wed Dec 31 16:00:00 1969 @@ -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 -#include -#include - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-rw.h linux/drivers/char/ftape/ftape-rw.h --- v2.1.65/linux/drivers/char/ftape/ftape-rw.h Mon Sep 30 00:39:58 1996 +++ linux/drivers/char/ftape/ftape-rw.h Wed Dec 31 16:00:00 1969 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-write.c linux/drivers/char/ftape/ftape-write.c --- v2.1.65/linux/drivers/char/ftape/ftape-write.c Tue Oct 29 15:40:36 1996 +++ linux/drivers/char/ftape/ftape-write.c Wed Dec 31 16:00:00 1969 @@ -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 -#include -#include -#include -#include - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/ftape-write.h linux/drivers/char/ftape/ftape-write.h --- v2.1.65/linux/drivers/char/ftape/ftape-write.h Wed Mar 6 05:07:20 1996 +++ linux/drivers/char/ftape/ftape-write.h Wed Dec 31 16:00:00 1969 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/kernel-interface.c linux/drivers/char/ftape/kernel-interface.c --- v2.1.65/linux/drivers/char/ftape/kernel-interface.c Tue May 13 22:41:07 1997 +++ linux/drivers/char/ftape/kernel-interface.c Wed Dec 31 16:00:00 1969 @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/kernel-interface.h linux/drivers/char/ftape/kernel-interface.h --- v2.1.65/linux/drivers/char/ftape/kernel-interface.h Mon Sep 30 00:39:58 1996 +++ linux/drivers/char/ftape/kernel-interface.h Wed Dec 31 16:00:00 1969 @@ -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 -#include - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/Makefile linux/drivers/char/ftape/lowlevel/Makefile --- v2.1.65/linux/drivers/char/ftape/lowlevel/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/Makefile Tue Nov 25 14:45:27 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/fc-10.c linux/drivers/char/ftape/lowlevel/fc-10.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/fc-10.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/fc-10.c Tue Nov 25 14:45:27 1997 @@ -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 +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/fc-10.h linux/drivers/char/ftape/lowlevel/fc-10.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/fc-10.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/fc-10.h Tue Nov 25 14:45:27 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/fdc-io.c linux/drivers/char/ftape/lowlevel/fdc-io.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/fdc-io.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/fdc-io.c Tue Nov 25 14:45:27 1997 @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#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(®[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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/fdc-io.h linux/drivers/char/ftape/lowlevel/fdc-io.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/fdc-io.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/fdc-io.h Tue Nov 25 14:45:27 1997 @@ -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 + +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/fdc-isr.c linux/drivers/char/ftape/lowlevel/fdc-isr.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/fdc-isr.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/fdc-isr.c Tue Nov 25 14:45:27 1997 @@ -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 +#include + +#define volatile /* */ + +#include +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/fdc-isr.h linux/drivers/char/ftape/lowlevel/fdc-isr.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/fdc-isr.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/fdc-isr.h Tue Nov 25 14:45:27 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-bsm.c linux/drivers/char/ftape/lowlevel/ftape-bsm.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-bsm.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-bsm.c Tue Nov 25 14:45:27 1997 @@ -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 + +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-bsm.h linux/drivers/char/ftape/lowlevel/ftape-bsm.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-bsm.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-bsm.h Tue Nov 25 14:45:27 1997 @@ -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 +#include + +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-buffer.c linux/drivers/char/ftape/lowlevel/ftape-buffer.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-buffer.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-buffer.c Tue Nov 25 14:45:27 1997 @@ -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 +#include +#include +#include +#include +#include + +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-buffer.h linux/drivers/char/ftape/lowlevel/ftape-buffer.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-buffer.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-buffer.h Tue Nov 25 14:45:27 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-calibr.c linux/drivers/char/ftape/lowlevel/ftape-calibr.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-calibr.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-calibr.c Tue Nov 25 14:45:27 1997 @@ -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 +#include +#include +#include +#include +#if defined(__alpha__) +# include +#elif defined(__i386__) +# include +#endif +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-calibr.h linux/drivers/char/ftape/lowlevel/ftape-calibr.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-calibr.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-calibr.h Tue Nov 25 14:45:27 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-ctl.c linux/drivers/char/ftape/lowlevel/ftape-ctl.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-ctl.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-ctl.c Tue Nov 25 14:45:27 1997 @@ -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 +#include +#include +#include +#include + +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#endif +#include + +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-ctl.h linux/drivers/char/ftape/lowlevel/ftape-ctl.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-ctl.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-ctl.h Tue Nov 25 14:45:27 1997 @@ -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 +#include +#include + +#include "../lowlevel/ftape-rw.h" +#include + +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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-ecc.c linux/drivers/char/ftape/lowlevel/ftape-ecc.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-ecc.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-ecc.c Tue Nov 25 14:45:27 1997 @@ -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 + +#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., L00 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; + } +} + + + +/* 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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-ecc.h linux/drivers/char/ftape/lowlevel/ftape-ecc.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-ecc.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-ecc.h Tue Nov 25 14:45:27 1997 @@ -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< +#include + +#include +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-format.h linux/drivers/char/ftape/lowlevel/ftape-format.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-format.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-format.h Tue Nov 25 14:45:27 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-init.c linux/drivers/char/ftape/lowlevel/ftape-init.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-init.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-init.c Tue Nov 25 14:45:27 1997 @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16) +#include +#else +#define __initdata +#define __initfunc(__arg) __arg +#endif +#include +#ifdef CONFIG_ZFTAPE +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-init.h linux/drivers/char/ftape/lowlevel/ftape-init.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-init.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-init.h Tue Nov 25 14:45:27 1997 @@ -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 +#include + +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-io.c linux/drivers/char/ftape/lowlevel/ftape-io.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-io.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-io.c Tue Nov 25 14:45:27 1997 @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-io.h linux/drivers/char/ftape/lowlevel/ftape-io.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-io.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-io.h Tue Nov 25 14:45:27 1997 @@ -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 +#include + +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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-proc.c linux/drivers/char/ftape/lowlevel/ftape-proc.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-proc.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-proc.c Tue Nov 25 14:45:27 1997 @@ -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 + +#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 + +#include +#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 +#else +#define __initdata +#define __initfunc(__arg) __arg +#endif +#include + + +#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 /* 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 + * entry for each WAN device + */ + +/* + * Generic /proc/net/ftape/ 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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-proc.h linux/drivers/char/ftape/lowlevel/ftape-proc.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-proc.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-proc.h Tue Nov 25 14:45:27 1997 @@ -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 + +extern struct proc_dir_entry proc_ftape; + +extern int ftape_proc_init(void); +extern void ftape_proc_destroy(void); + +#endif diff -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-read.c linux/drivers/char/ftape/lowlevel/ftape-read.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-read.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-read.c Tue Nov 25 14:45:27 1997 @@ -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 +#include +#include +#include + +#include +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-read.h linux/drivers/char/ftape/lowlevel/ftape-read.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-read.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-read.h Tue Nov 25 14:45:27 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-rw.c linux/drivers/char/ftape/lowlevel/ftape-rw.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-rw.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-rw.c Tue Nov 25 14:45:27 1997 @@ -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 +#include + +#include +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-rw.h linux/drivers/char/ftape/lowlevel/ftape-rw.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-rw.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-rw.h Tue Nov 25 14:45:27 1997 @@ -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 + +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-setup.c linux/drivers/char/ftape/lowlevel/ftape-setup.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-setup.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-setup.c Tue Nov 25 14:45:27 1997 @@ -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 +#include +#include +#include +#include + +#include +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,16) +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-tracing.c linux/drivers/char/ftape/lowlevel/ftape-tracing.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-tracing.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-tracing.c Tue Nov 25 14:45:27 1997 @@ -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 +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-tracing.h linux/drivers/char/ftape/lowlevel/ftape-tracing.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-tracing.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-tracing.h Tue Nov 25 14:45:27 1997 @@ -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 +#include + +/* + * 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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-write.c linux/drivers/char/ftape/lowlevel/ftape-write.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-write.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-write.c Tue Nov 25 14:45:27 1997 @@ -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 +#include +#include +#include + +#include +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-write.h linux/drivers/char/ftape/lowlevel/ftape-write.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape-write.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape-write.h Tue Nov 25 14:45:27 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape_syms.c linux/drivers/char/ftape/lowlevel/ftape_syms.c --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape_syms.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape_syms.c Tue Nov 25 14:45:27 1997 @@ -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 +#define __NO_VERSION__ +#include + +#include +#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 +#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 +}; +#endif diff -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/lowlevel/ftape_syms.h linux/drivers/char/ftape/lowlevel/ftape_syms.h --- v2.1.65/linux/drivers/char/ftape/lowlevel/ftape_syms.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/lowlevel/ftape_syms.h Tue Nov 25 14:45:27 1997 @@ -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 +/* ftape-vfs.c defined global vars. + */ + +extern struct symbol_table ftape_symbol_table; +#endif + +#endif diff -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/qic117.h linux/drivers/char/ftape/qic117.h --- v2.1.65/linux/drivers/char/ftape/qic117.h Thu Apr 11 23:49:35 1996 +++ linux/drivers/char/ftape/qic117.h Wed Dec 31 16:00:00 1969 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/tracing.c linux/drivers/char/ftape/tracing.c --- v2.1.65/linux/drivers/char/ftape/tracing.c Thu Mar 14 01:53:45 1996 +++ linux/drivers/char/ftape/tracing.c Wed Dec 31 16:00:00 1969 @@ -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 - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/tracing.h linux/drivers/char/ftape/tracing.h --- v2.1.65/linux/drivers/char/ftape/tracing.h Mon Sep 30 00:39:58 1996 +++ linux/drivers/char/ftape/tracing.h Wed Dec 31 16:00:00 1969 @@ -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 - -#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/vendors.h linux/drivers/char/ftape/vendors.h --- v2.1.65/linux/drivers/char/ftape/vendors.h Fri Mar 15 02:04:09 1996 +++ linux/drivers/char/ftape/vendors.h Wed Dec 31 16:00:00 1969 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/Makefile linux/drivers/char/ftape/zftape/Makefile --- v2.1.65/linux/drivers/char/ftape/zftape/Makefile Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/Makefile Tue Nov 25 14:45:28 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-buffers.c linux/drivers/char/ftape/zftape/zftape-buffers.c --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-buffers.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-buffers.c Tue Nov 25 14:45:28 1997 @@ -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 +#include +#include +#include + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,0) +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-buffers.h linux/drivers/char/ftape/zftape/zftape-buffers.h --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-buffers.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-buffers.h Tue Nov 25 14:45:28 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-ctl.c linux/drivers/char/ftape/zftape/zftape-ctl.c --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-ctl.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-ctl.c Tue Nov 25 14:45:28 1997 @@ -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 +#include +#include +#define __NO_VERSION__ +#include +#ifdef CONFIG_KERNELD +#include +#endif +#include + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-ctl.h linux/drivers/char/ftape/zftape/zftape-ctl.h --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-ctl.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-ctl.h Tue Nov 25 14:45:28 1997 @@ -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 +#include +#include + +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-eof.c linux/drivers/char/ftape/zftape/zftape-eof.c --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-eof.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-eof.c Tue Nov 25 14:45:28 1997 @@ -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 +#include + +#include + +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-eof.h linux/drivers/char/ftape/zftape/zftape-eof.h --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-eof.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-eof.h Tue Nov 25 14:45:28 1997 @@ -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 +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-init.c linux/drivers/char/ftape/zftape/zftape-init.c --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-init.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-init.c Tue Nov 25 14:45:28 1997 @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KERNELD +#include +#endif +#include +#include + +#include +#if LINUX_VERSION_CODE >=KERNEL_VER(2,1,16) +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-init.h linux/drivers/char/ftape/zftape/zftape-init.h --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-init.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-init.h Tue Nov 25 14:45:28 1997 @@ -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 + +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-read.c linux/drivers/char/ftape/zftape/zftape-read.c --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-read.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-read.c Tue Nov 25 14:45:28 1997 @@ -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 +#include +#include +#ifdef CONFIG_KERNELD +#include +#endif + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-read.h linux/drivers/char/ftape/zftape/zftape-read.h --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-read.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-read.h Tue Nov 25 14:45:28 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-rw.c linux/drivers/char/ftape/zftape/zftape-rw.c --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-rw.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-rw.c Tue Nov 25 14:45:28 1997 @@ -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 +#include +#include + +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-rw.h linux/drivers/char/ftape/zftape/zftape-rw.h --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-rw.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-rw.h Tue Nov 25 14:45:28 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-vtbl.c linux/drivers/char/ftape/zftape/zftape-vtbl.c --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-vtbl.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-vtbl.c Tue Nov 25 14:45:28 1997 @@ -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 +#include +#include +#include + +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-vtbl.h linux/drivers/char/ftape/zftape/zftape-vtbl.h --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-vtbl.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-vtbl.h Tue Nov 25 14:45:28 1997 @@ -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 + +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-write.c linux/drivers/char/ftape/zftape/zftape-write.c --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-write.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-write.c Tue Nov 25 14:45:28 1997 @@ -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 +#include +#include +#ifdef CONFIG_KERNELD +#include +#endif + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VER(2,1,6) +#include +#else +#include +#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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape-write.h linux/drivers/char/ftape/zftape/zftape-write.h --- v2.1.65/linux/drivers/char/ftape/zftape/zftape-write.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape-write.h Tue Nov 25 14:45:28 1997 @@ -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 -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape_syms.c linux/drivers/char/ftape/zftape/zftape_syms.c --- v2.1.65/linux/drivers/char/ftape/zftape/zftape_syms.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape_syms.c Tue Nov 25 14:45:28 1997 @@ -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 +#define __NO_VERSION__ +#include + +#include + +#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 +#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 +}; +#endif diff -u --recursive --new-file v2.1.65/linux/drivers/char/ftape/zftape/zftape_syms.h linux/drivers/char/ftape/zftape/zftape_syms.h --- v2.1.65/linux/drivers/char/ftape/zftape/zftape_syms.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/ftape/zftape/zftape_syms.h Tue Nov 25 14:45:28 1997 @@ -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 +/* ftape-vfs.c defined global vars. + */ + +extern struct symbol_table zft_symbol_table; +#endif + +#endif diff -u --recursive --new-file v2.1.65/linux/drivers/char/rocket.c linux/drivers/char/rocket.c --- v2.1.65/linux/drivers/char/rocket.c Wed Sep 24 20:05:47 1997 +++ linux/drivers/char/rocket.c Mon Nov 24 08:45:44 1997 @@ -622,10 +622,12 @@ 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 @@ } /* baud rate */ +#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */ i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; @@ -690,6 +693,11 @@ 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 @@ /* * 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 @@ 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 @@ (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 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 @@ ((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 @@ 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; diff -u --recursive --new-file v2.1.65/linux/drivers/char/serial.c linux/drivers/char/serial.c --- v2.1.65/linux/drivers/char/serial.c Wed Sep 24 20:05:47 1997 +++ linux/drivers/char/serial.c Mon Nov 24 08:45:44 1997 @@ -22,6 +22,9 @@ * 1/97: Extended dumb serial ports are a config option now. * Saves 4k. Michael A. Griffith * + * 8/97: Fix bug in rs_set_termios with RTS + * Stanislav V. Voronyi + * * This module exports the following rs232 io functions: * * int rs_init(void); @@ -319,13 +322,6 @@ 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 @@ 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 @@ 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) { @@ -908,28 +904,6 @@ } /* - * 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 * (irq=0). This doesn't work very well for 16450's, but gives barely @@ -1062,7 +1036,6 @@ unsigned short ICP; #endif - page = get_free_page(GFP_KERNEL); if (!page) return -ENOMEM; @@ -1365,9 +1338,9 @@ 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 @@ #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 @@ 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 @@ return 0; } - /* - * 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 async_struct * info, int duration) +static void rs_break(struct tty_struct *tty, int break_state) { - 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. - */ -static void begin_break(struct async_struct * info) -{ - 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 @@ { 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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; diff -u --recursive --new-file v2.1.65/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v2.1.65/linux/drivers/char/tty_io.c Sat Oct 25 02:44:15 1997 +++ linux/drivers/char/tty_io.c Mon Nov 24 08:45:44 1997 @@ -366,14 +366,18 @@ 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 @@ 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 @@ * 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 @@ 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 @@ #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 @@ #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 @@ { 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 @@ 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 @@ 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 @@ #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 @@ 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,22 +1724,57 @@ 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. */ static void initialize_tty_struct(struct tty_struct *tty) @@ -1673,6 +1788,8 @@ 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; } /* diff -u --recursive --new-file v2.1.65/linux/drivers/char/tty_ioctl.c linux/drivers/char/tty_ioctl.c --- v2.1.65/linux/drivers/char/tty_ioctl.c Wed Sep 24 20:05:47 1997 +++ linux/drivers/char/tty_ioctl.c Mon Nov 24 08:45:44 1997 @@ -509,18 +509,15 @@ 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; diff -u --recursive --new-file v2.1.65/linux/drivers/pci/pci.c linux/drivers/pci/pci.c --- v2.1.65/linux/drivers/pci/pci.c Mon Nov 17 18:47:21 1997 +++ linux/drivers/pci/pci.c Wed Nov 19 22:45:53 1997 @@ -254,8 +254,8 @@ 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"), diff -u --recursive --new-file v2.1.65/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c --- v2.1.65/linux/drivers/scsi/ppa.c Mon Nov 17 18:47:21 1997 +++ linux/drivers/scsi/ppa.c Sat Nov 22 10:28:31 1997 @@ -275,7 +275,7 @@ len = length; return len; } -/* end of ppa.c */ + static int device_check(int host_no); #if PPA_DEBUG > 0 @@ -1000,8 +1000,10 @@ } #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; diff -u --recursive --new-file v2.1.65/linux/drivers/scsi/ppa.h linux/drivers/scsi/ppa.h --- v2.1.65/linux/drivers/scsi/ppa.h Tue Nov 18 17:22:07 1997 +++ linux/drivers/scsi/ppa.h Tue Nov 25 15:56:47 1997 @@ -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 @@ -54,6 +54,11 @@ * used with lp or other parport devices on the same parallel port. * 1997 by Andrea Arcangeli * [1.39] + * + * Little fix in ppa engine to ensure that ppa don' t release parport + * or disconnect in wrong cases. + * 1997 by Andrea Arcangeli + * [1.39a] */ /* ------ END OF USER CONFIGURABLE PARAMETERS ----- */ diff -u --recursive --new-file v2.1.65/linux/fs/buffer.c linux/fs/buffer.c --- v2.1.65/linux/fs/buffer.c Wed Nov 12 13:34:27 1997 +++ linux/fs/buffer.c Mon Nov 24 13:02:51 1997 @@ -196,7 +196,7 @@ */ if (wait && buffer_req(bh) && !buffer_locked(bh) && !buffer_dirty(bh) && !buffer_uptodate(bh)) { - err = 1; + err = -EIO; continue; } @@ -288,7 +288,22 @@ 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) diff -u --recursive --new-file v2.1.65/linux/fs/ext2/fsync.c linux/fs/ext2/fsync.c --- v2.1.65/linux/fs/ext2/fsync.c Mon Nov 17 18:47:21 1997 +++ linux/fs/ext2/fsync.c Mon Nov 24 13:04:56 1997 @@ -96,10 +96,8 @@ */ 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++) { diff -u --recursive --new-file v2.1.65/linux/fs/hpfs/hpfs_fs.c linux/fs/hpfs/hpfs_fs.c --- v2.1.65/linux/fs/hpfs/hpfs_fs.c Sat Oct 25 02:44:17 1997 +++ linux/fs/hpfs/hpfs_fs.c Tue Nov 18 10:36:45 1997 @@ -1122,20 +1122,21 @@ 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 @@ * 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 @@ } } - /* - * Add the dentry, negative or otherwise. - */ - add_dentry: d_add(dentry, inode); retval = 0; free4: brelse4(&qbh); + + out: return retval; } diff -u --recursive --new-file v2.1.65/linux/fs/nfsd/nfsfh.c linux/fs/nfsd/nfsfh.c --- v2.1.65/linux/fs/nfsd/nfsfh.c Tue Nov 18 17:22:08 1997 +++ linux/fs/nfsd/nfsfh.c Tue Nov 18 10:35:16 1997 @@ -42,18 +42,22 @@ 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 @@ 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 @@ 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 @@ #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 @@ 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 @@ */ static void expire_old(int cache, int age) { + struct list_head *tmp; struct fh_entry *fhe; int i; @@ -403,6 +573,17 @@ } /* + * 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 ... */ while (nfsd_nr_paths > NFSD_MAX_PATHS) { @@ -484,6 +665,31 @@ #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 @@ #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 @@ /* * 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 @@ 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 @@ 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 @@ } /* - * 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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); } + diff -u --recursive --new-file v2.1.65/linux/fs/smbfs/cache.c linux/fs/smbfs/cache.c --- v2.1.65/linux/fs/smbfs/cache.c Sat Nov 1 11:04:27 1997 +++ linux/fs/smbfs/cache.c Mon Nov 24 10:30:40 1997 @@ -265,7 +265,7 @@ * 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 @@ * 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 @@ * '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; } diff -u --recursive --new-file v2.1.65/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c --- v2.1.65/linux/fs/smbfs/dir.c Sat Nov 1 11:04:27 1997 +++ linux/fs/smbfs/dir.c Mon Nov 24 10:30:40 1997 @@ -9,6 +9,7 @@ #include #include #include + #include #include @@ -62,7 +63,9 @@ NULL, /* bmap */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL, /* updatepage */ + smb_revalidate_inode, /* revalidate */ }; static ssize_t @@ -72,42 +75,25 @@ } /* - * 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 @@ /* * 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 @@ 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 @@ * 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; - error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr); - if (!error) - { - 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; - } - } #ifdef SMBFS_DEBUG_VERBOSE -printk("smb_instantiate: file %s/%s, error=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, error); +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) + 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) + { + inode->u.smbfs_i.fileid = fileid; + inode->u.smbfs_i.access = SMB_O_RDWR; + inode->u.smbfs_i.open = server->generation; + } + /* 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 @@ 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 @@ 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 @@ * 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 @@ * 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)); diff -u --recursive --new-file v2.1.65/linux/fs/smbfs/file.c linux/fs/smbfs/file.c --- v2.1.65/linux/fs/smbfs/file.c Sat Oct 25 02:44:18 1997 +++ linux/fs/smbfs/file.c Mon Nov 24 10:30:40 1997 @@ -12,20 +12,19 @@ #include #include #include -#include #include #include #include #include +#include +#include + #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 @@ 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 @@ count -= result; offset += result; buffer += result; + inode->i_atime = CURRENT_TIME; if (result < rsize) break; } while (count); @@ -130,7 +136,7 @@ { 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 @@ 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 @@ 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 @@ #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 @@ 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 @@ 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 @@ 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 @@ 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 @@ smb_writepage, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ - NULL, /* permission */ + smb_file_permission, /* permission */ NULL, /* smap */ smb_updatepage, /* updatepage */ smb_revalidate_inode, /* revalidate */ diff -u --recursive --new-file v2.1.65/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c --- v2.1.65/linux/fs/smbfs/inode.c Sat Nov 1 11:04:27 1997 +++ linux/fs/smbfs/inode.c Mon Nov 24 10:30:40 1997 @@ -19,6 +19,7 @@ #include #include #include + #include #include #include @@ -83,6 +84,26 @@ 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) { @@ -153,6 +174,16 @@ 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. */ if (jiffies < inode->u.smbfs_i.oldmtime + HZ/10) @@ -245,7 +276,6 @@ 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 @@ 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 @@ 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 @@ goto out; } - /* - * Make sure our inode is up-to-date ... - */ error = smb_revalidate_inode(inode); if (error) goto out; @@ -506,13 +542,7 @@ { 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 @@ 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; } diff -u --recursive --new-file v2.1.65/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c --- v2.1.65/linux/fs/smbfs/proc.c Sat Nov 1 11:04:27 1997 +++ linux/fs/smbfs/proc.c Mon Nov 24 10:30:40 1997 @@ -17,6 +17,7 @@ #include #include #include + #include #include #include @@ -24,6 +25,7 @@ #include #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 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ } 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 @@ case ERRbadmem: return EFAULT; case ERRbadenv: - return EREMOTEIO; case ERRbadformat: return EREMOTEIO; case ERRbadaccess: @@ -364,7 +389,7 @@ 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 @@ 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 @@ 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 @@ case ERRnotready: return EUCLEAN; case ERRbadcmd: - return EIO; case ERRdata: return EIO; case ERRbadreq: @@ -419,15 +443,15 @@ 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 @@ 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 @@ /* * 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 @@ 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 @@ (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 @@ /* 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 @@ * 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 @@ 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 @@ } /* - * 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) @@ -827,12 +891,43 @@ 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. */ 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 @@ } } +/* + * 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 @@ 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 @@ 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 @@ 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 @@ 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: 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 @@ 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 @@ 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 @@ printk("smb_proc_getattr_trans2: for %s: result=%d, rcls=%d, err=%d\n", ¶m[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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ } 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 @@ { 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; } diff -u --recursive --new-file v2.1.65/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c --- v2.1.65/linux/fs/smbfs/sock.c Sat Nov 1 11:04:27 1997 +++ linux/fs/smbfs/sock.c Mon Nov 24 10:30:40 1997 @@ -420,8 +420,8 @@ #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) diff -u --recursive --new-file v2.1.65/linux/include/linux/fs.h linux/include/linux/fs.h --- v2.1.65/linux/include/linux/fs.h Wed Nov 12 13:34:28 1997 +++ linux/include/linux/fs.h Tue Nov 25 15:55:31 1997 @@ -654,6 +654,7 @@ 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 -u --recursive --new-file v2.1.65/linux/include/linux/ftape-header-segment.h linux/include/linux/ftape-header-segment.h --- v2.1.65/linux/include/linux/ftape-header-segment.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/ftape-header-segment.h Tue Nov 25 14:45:28 1997 @@ -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)< #include +#endif +#include +#include +#include +#if LINUX_VERSION_CODE <= KERNEL_VER(1,2,13) +typedef daddr_t __kernel_daddr_t; /* needed for mtio.h */ +#endif +#include -#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) +#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 */ + +/* 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 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 SOFT_RETRIES 6 /* number of low level retries (triple) */ -# define RETRIES_ON_ECC_ERROR 3 /* ecc error when correcting segment */ +# 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 diff -u --recursive --new-file v2.1.65/linux/include/linux/mtio.h linux/include/linux/mtio.h --- v2.1.65/linux/include/linux/mtio.h Wed May 28 10:51:33 1997 +++ linux/include/linux/mtio.h Tue Nov 25 14:45:28 1997 @@ -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 #include +#include /* * Structures and definitions for mag tape io control commands @@ -104,7 +109,7 @@ #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 @@ 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 */ @@ -181,6 +289,16 @@ */ #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. diff -u --recursive --new-file v2.1.65/linux/include/linux/pci.h linux/include/linux/pci.h --- v2.1.65/linux/include/linux/pci.h Mon Nov 17 18:47:22 1997 +++ linux/include/linux/pci.h Wed Nov 19 22:46:12 1997 @@ -565,8 +565,8 @@ #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 -u --recursive --new-file v2.1.65/linux/include/linux/qic117.h linux/include/linux/qic117.h --- v2.1.65/linux/include/linux/qic117.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/qic117.h Tue Nov 25 14:45:28 1997 @@ -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 */ diff -u --recursive --new-file v2.1.65/linux/include/linux/serial.h linux/include/linux/serial.h --- v2.1.65/linux/include/linux/serial.h Mon Jun 30 15:23:12 1997 +++ linux/include/linux/serial.h Mon Nov 24 08:45:45 1997 @@ -132,113 +132,6 @@ #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 -#include - -/* - * 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 -u --recursive --new-file v2.1.65/linux/include/linux/serialP.h linux/include/linux/serialP.h --- v2.1.65/linux/include/linux/serialP.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/serialP.h Mon Nov 24 08:45:45 1997 @@ -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 +#include + +/* + * 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 */ diff -u --recursive --new-file v2.1.65/linux/include/linux/smb_fs.h linux/include/linux/smb_fs.h --- v2.1.65/linux/include/linux/smb_fs.h Sat Nov 1 11:04:27 1997 +++ linux/include/linux/smb_fs.h Tue Nov 25 15:58:46 1997 @@ -65,6 +65,18 @@ #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 @@ /* 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_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); diff -u --recursive --new-file v2.1.65/linux/include/linux/smb_fs_i.h linux/include/linux/smb_fs_i.h --- v2.1.65/linux/include/linux/smb_fs_i.h Wed Oct 15 16:04:24 1997 +++ linux/include/linux/smb_fs_i.h Mon Nov 24 10:30:40 1997 @@ -28,6 +28,7 @@ __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 */ }; diff -u --recursive --new-file v2.1.65/linux/include/linux/smbno.h linux/include/linux/smbno.h --- v2.1.65/linux/include/linux/smbno.h Tue Sep 23 16:48:50 1997 +++ linux/include/linux/smbno.h Mon Nov 24 10:30:40 1997 @@ -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. */ @@ -100,6 +100,14 @@ #define ERRFCBunavail 35 #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 diff -u --recursive --new-file v2.1.65/linux/include/linux/tty.h linux/include/linux/tty.h --- v2.1.65/linux/include/linux/tty.h Sun Sep 7 13:10:43 1997 +++ linux/include/linux/tty.h Tue Nov 25 15:55:31 1997 @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -223,14 +224,17 @@ 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 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; diff -u --recursive --new-file v2.1.65/linux/include/linux/tty_driver.h linux/include/linux/tty_driver.h --- v2.1.65/linux/include/linux/tty_driver.h Mon Jul 7 16:02:02 1997 +++ linux/include/linux/tty_driver.h Tue Nov 25 15:55:31 1997 @@ -92,6 +92,18 @@ * 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 @@ 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 -u --recursive --new-file v2.1.65/linux/include/linux/zftape.h linux/include/linux/zftape.h --- v2.1.65/linux/include/linux/zftape.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/zftape.h Tue Nov 25 14:45:28 1997 @@ -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 + +#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 diff -u --recursive --new-file v2.1.65/linux/init/main.c linux/init/main.c --- v2.1.65/linux/init/main.c Wed Nov 12 13:34:28 1997 +++ linux/init/main.c Tue Nov 25 14:45:28 1997 @@ -242,6 +242,9 @@ #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 @@ #ifdef CONFIG_PMAC_CONSOLE { "console=", pmac_cons_setup }, { "vmode=", pmac_vmode_setup }, +#endif +#ifdef CONFIG_FTAPE + { "ftape=", ftape_setup}, #endif { 0, 0 } }; diff -u --recursive --new-file v2.1.65/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v2.1.65/linux/kernel/ksyms.c Mon Nov 17 18:47:22 1997 +++ linux/kernel/ksyms.c Mon Nov 24 08:45:44 1997 @@ -242,6 +242,8 @@ 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); diff -u --recursive --new-file v2.1.65/linux/mm/filemap.c linux/mm/filemap.c --- v2.1.65/linux/mm/filemap.c Wed Nov 12 13:34:28 1997 +++ linux/mm/filemap.c Tue Nov 25 14:45:26 1997 @@ -1220,9 +1220,7 @@ 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)) { diff -u --recursive --new-file v2.1.65/linux/net/netlink.c linux/net/netlink.c --- v2.1.65/linux/net/netlink.c Wed Sep 24 20:05:48 1997 +++ linux/net/netlink.c Mon Nov 24 08:02:45 1997 @@ -81,11 +81,11 @@ * 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 @@ * 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) diff -u --recursive --new-file v2.1.65/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c --- v2.1.65/linux/net/sunrpc/clnt.c Wed Oct 15 16:04:24 1997 +++ linux/net/sunrpc/clnt.c Tue Nov 25 12:54:33 1997 @@ -338,8 +338,10 @@ } 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 @@ /* 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 @@ task->tk_action = call_encode; return NULL; } + printk("RPC: garbage, exit EIO\n"); rpc_exit(task, -EIO); return NULL; } diff -u --recursive --new-file v2.1.65/linux/net/sunrpc/xprt.c linux/net/sunrpc/xprt.c --- v2.1.65/linux/net/sunrpc/xprt.c Tue Sep 23 16:48:50 1997 +++ linux/net/sunrpc/xprt.c Tue Nov 25 12:54:33 1997 @@ -1009,18 +1009,11 @@ } 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 @@ 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;