Remove long-unmaintained ftape driver subsystem.
It's bitrotten, long unmaintained, long hidden under BROKEN_ON_SMP, etc. As scheduled in feature-removal-schedule.txt, and ack'd several times on lkml. Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
		| @@ -104,8 +104,6 @@ firmware_class/ | ||||
| 	- request_firmware() hotplug interface info. | ||||
| floppy.txt | ||||
| 	- notes and driver options for the floppy disk driver. | ||||
| ftape.txt | ||||
| 	- notes about the floppy tape device driver. | ||||
| hayes-esp.txt | ||||
| 	- info on using the Hayes ESP serial driver. | ||||
| highuid.txt | ||||
|   | ||||
| @@ -234,14 +234,6 @@ Who:	Jean Delvare <khali@linux-fr.org> | ||||
|  | ||||
| --------------------------- | ||||
|  | ||||
| What:	ftape | ||||
| When:	2.6.20 | ||||
| Why:	Orphaned for ages.  SMP bugs long unfixed.  Few users left | ||||
| 	in the world. | ||||
| Who:	Jeff Garzik <jeff@garzik.org> | ||||
|  | ||||
| --------------------------- | ||||
|  | ||||
| What:	IPv4 only connection tracking/NAT/helpers | ||||
| When:	2.6.22 | ||||
| Why:	The new layer 3 independant connection tracking replaces the old | ||||
|   | ||||
| @@ -1,307 +0,0 @@ | ||||
| Intro | ||||
| ===== | ||||
|  | ||||
| This file describes some issues involved when using the "ftape" | ||||
| floppy tape device driver that comes with the Linux kernel. | ||||
|  | ||||
| ftape has a home page at | ||||
|  | ||||
| http://ftape.dot-heine.de/ | ||||
|  | ||||
| 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. | ||||
|  | ||||
| NOTE: This is an unmaintained set of drivers, and it is not guaranteed to work. | ||||
| If you are interested in taking over maintenance, contact Claus-Justus Heine | ||||
| <ch@dot-heine.de>, the former maintainer. | ||||
|  | ||||
| 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. Formatting | ||||
|    5. 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 date 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://ftape.dot-heine.de/ | ||||
|  | ||||
| ******************************************************************************* | ||||
|  | ||||
| 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. | ||||
|  | ||||
|    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 block size defaults to 10k which is the default block size of | ||||
|    GNU tar. | ||||
|  | ||||
|    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. | ||||
|  | ||||
| 4. Formatting | ||||
|    ~~~~~~~~~~ | ||||
|    ftape DOES support formatting of floppy tape cartridges. You need the | ||||
|    `ftformat' program that is shipped with the modules version of ftape. | ||||
|    Please get the latest version of ftape from | ||||
|  | ||||
|    ftp://sunsite.unc.edu/pub/Linux/kernel/tapes | ||||
|  | ||||
|    or from the ftape home page at | ||||
|  | ||||
|    http://ftape.dot-heine.de/ | ||||
|  | ||||
|    `ftformat' is contained in the `./contrib/' subdirectory of that | ||||
|    separate ftape package. | ||||
|  | ||||
| 5. Interchanging cartridges with other operating systems | ||||
|    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
|    The internal emulation of Unix tape device file marks has changed | ||||
|    completely. ftape 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. | ||||
|  | ||||
|    However, it isn't possible to extract the data dumped to the tape | ||||
|    by some MSDOS program with ftape. 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 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 | ||||
|  | ||||
|        modprobe ftape 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 'modprobe' at the shell prompt: | ||||
|  | ||||
|    modprobe ftape ft_tracing=4 | ||||
|  | ||||
|    or by editing the file `/etc/modprobe.conf' in which case they take | ||||
|    effect each time when the module is loaded with `modprobe' (please | ||||
|    refer to the respective manual pages). Thus, you should add a line | ||||
|  | ||||
|    options ftape ft_tracing=4 | ||||
|  | ||||
|    to `/etc/modprobe.conf` 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. | ||||
|  | ||||
| 	ft_probe_fc10 is set to a non-zero value if you wish for ftape to | ||||
| 	probe for a Colorado FC-10 or FC-20 controller. | ||||
|  | ||||
| 	ft_mach2 is set to a non-zero value if you wish for ftape to probe | ||||
| 	for a Mountain MACH-2 controller. | ||||
|  | ||||
|         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/modprobe.conf': | ||||
|  | ||||
|    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. | ||||
|  | ||||
|    Changelog: | ||||
|    ========== | ||||
|  | ||||
| ~1996:		Original Document | ||||
|  | ||||
| 10-24-2004:	General cleanup and updating, noting additional module options. | ||||
| 		James Nelson <james4765@gmail.com> | ||||
| @@ -557,9 +557,6 @@ and is between 256 and 4096 characters. It is defined in the file | ||||
| 	floppy=		[HW] | ||||
| 			See Documentation/floppy.txt. | ||||
|  | ||||
| 	ftape=		[HW] Floppy Tape subsystem debugging options. | ||||
| 			See Documentation/ftape.txt. | ||||
|  | ||||
| 	gamecon.map[2|3]= | ||||
| 			[HW,JOY] Multisystem joystick and NES/SNES/PSX pad | ||||
| 			support via parallel port (up to 5 devices per port) | ||||
|   | ||||
| @@ -1166,11 +1166,6 @@ P:	David Howells | ||||
| M:	dhowells@redhat.com | ||||
| S:	Maintained | ||||
|  | ||||
| FTAPE/QIC-117 | ||||
| L:	linux-tape@vger.kernel.org | ||||
| W:	http://sourceforge.net/projects/ftape | ||||
| S:	Orphan | ||||
|  | ||||
| FUSE: FILESYSTEM IN USERSPACE | ||||
| P:	Miklos Szeredi | ||||
| M:	miklos@szeredi.hu | ||||
|   | ||||
| @@ -855,39 +855,6 @@ config TANBAC_TB0219 | ||||
| 	depends TANBAC_TB022X | ||||
| 	select GPIO_VR41XX | ||||
|  | ||||
| menu "Ftape, the floppy tape device driver" | ||||
|  | ||||
| config FTAPE | ||||
| 	tristate "Ftape (QIC-80/Travan) support" | ||||
| 	depends on BROKEN_ON_SMP && (ALPHA || X86) | ||||
| 	---help--- | ||||
| 	  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 say Y here. | ||||
|  | ||||
| 	  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 | ||||
| 	  below 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 <file:drivers/char/ftape/README.PCI>. | ||||
|  | ||||
| 	  The ftape kernel driver is also available as a runtime loadable | ||||
| 	  module. To compile this driver as a module, choose M here: the | ||||
| 	  module will be called ftape. | ||||
|  | ||||
| source "drivers/char/ftape/Kconfig" | ||||
|  | ||||
| endmenu | ||||
|  | ||||
| source "drivers/char/agp/Kconfig" | ||||
|  | ||||
| source "drivers/char/drm/Kconfig" | ||||
|   | ||||
| @@ -78,7 +78,6 @@ obj-$(CONFIG_TOSHIBA)		+= toshiba.o | ||||
| obj-$(CONFIG_I8K)		+= i8k.o | ||||
| obj-$(CONFIG_DS1620)		+= ds1620.o | ||||
| obj-$(CONFIG_HW_RANDOM)		+= hw_random/ | ||||
| obj-$(CONFIG_FTAPE)		+= ftape/ | ||||
| obj-$(CONFIG_COBALT_LCD)	+= lcd.o | ||||
| obj-$(CONFIG_PPDEV)		+= ppdev.o | ||||
| obj-$(CONFIG_NWBUTTON)		+= nwbutton.o | ||||
|   | ||||
| @@ -1,330 +0,0 @@ | ||||
| # | ||||
| # Ftape configuration | ||||
| # | ||||
| config ZFTAPE | ||||
| 	tristate "Zftape, the VFS interface" | ||||
| 	depends on FTAPE | ||||
| 	---help--- | ||||
| 	  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. | ||||
|  | ||||
| 	  To compile this driver as a module, choose M here: the | ||||
| 	  module will be called zftape. | ||||
|  | ||||
| 	  Regardless of whether you say Y or M here, an additional runtime | ||||
| 	  loadable module called `zft-compressor' which contains code to | ||||
| 	  support user transparent on-the-fly compression based on Ross | ||||
| 	  William's lzrw3 algorithm will be produced.  If you have enabled the | ||||
| 	  kernel module loader (i.e. have said Y to "Kernel module loader | ||||
| 	  support", above) then `zft-compressor' will be loaded | ||||
| 	  automatically by zftape when needed. | ||||
|  | ||||
| 	  Despite its name, zftape does NOT use compression by default. | ||||
|  | ||||
| config ZFT_DFLT_BLK_SZ | ||||
| 	int "Default block size" | ||||
| 	depends on ZFTAPE | ||||
| 	default "10240" | ||||
| 	---help--- | ||||
| 	  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. 62 K). 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). | ||||
|  | ||||
| comment "The compressor will be built as a module only!" | ||||
| 	depends on FTAPE && ZFTAPE | ||||
|  | ||||
| config ZFT_COMPRESSOR | ||||
| 	tristate | ||||
| 	depends on FTAPE!=n && ZFTAPE!=n | ||||
| 	default m | ||||
|  | ||||
| config FT_NR_BUFFERS | ||||
| 	int "Number of ftape buffers (EXPERIMENTAL)" | ||||
| 	depends on FTAPE && EXPERIMENTAL | ||||
| 	default "3" | ||||
| 	help | ||||
| 	  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 a 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 32 KB of memory. Please note that this memory cannot be | ||||
| 	  swapped out. | ||||
|  | ||||
| config FT_PROC_FS | ||||
| 	bool "Enable procfs status report (+2kb)" | ||||
| 	depends on FTAPE && PROC_FS | ||||
| 	---help--- | ||||
| 	  Optional. Saying Y will result in creation of a directory | ||||
| 	  `/proc/ftape' under the /proc file system. The 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 2 KB. | ||||
|  | ||||
| 	  WARNING: When compiling ftape as a module (i.e. saying M to "Floppy | ||||
| 	  tape drive") 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. | ||||
|  | ||||
| choice | ||||
| 	prompt "Debugging output" | ||||
| 	depends on FTAPE | ||||
| 	default FT_NORMAL_DEBUG | ||||
|  | ||||
| config FT_NORMAL_DEBUG | ||||
| 	bool "Normal" | ||||
| 	---help--- | ||||
| 	  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 <file:Documentation/ftape.txt> for a short description | ||||
| 	  how to control the amount of debugging output. | ||||
|  | ||||
| config FT_FULL_DEBUG | ||||
| 	bool "Excessive" | ||||
| 	help | ||||
| 	  Extremely verbose output for driver debugging purposes. | ||||
|  | ||||
| config FT_NO_TRACE | ||||
| 	bool "Reduced" | ||||
| 	help | ||||
| 	  Reduced tape driver debugging output. | ||||
|  | ||||
| config FT_NO_TRACE_AT_ALL | ||||
| 	bool "None" | ||||
| 	help | ||||
| 	  Suppress all debugging output from the tape drive. | ||||
|  | ||||
| endchoice | ||||
|  | ||||
| comment "Hardware configuration" | ||||
| 	depends on FTAPE | ||||
|  | ||||
| choice | ||||
| 	prompt "Floppy tape controllers" | ||||
| 	depends on FTAPE | ||||
| 	default FT_STD_FDC | ||||
|  | ||||
| config FT_STD_FDC | ||||
| 	bool "Standard" | ||||
| 	---help--- | ||||
| 	  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 "Floppy tape drive") or module load time (i.e. if you | ||||
| 	  have said M to "Floppy tape drive"). | ||||
|  | ||||
| 	  Please read also the file <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 | ||||
| 	  <file:drivers/char/ftape/README.PCI>. | ||||
|  | ||||
| config FT_MACH2 | ||||
| 	bool "MACH-2" | ||||
|  | ||||
| config FT_PROBE_FC10 | ||||
| 	bool "FC-10/FC-20" | ||||
|  | ||||
| config FT_ALT_FDC | ||||
| 	bool "Alt/82078" | ||||
|  | ||||
| endchoice | ||||
|  | ||||
| comment "Consult the manuals of your tape drive for the correct settings!" | ||||
| 	depends on FTAPE && !FT_STD_FDC | ||||
|  | ||||
| config FT_FDC_BASE | ||||
| 	hex "IO base of the floppy disk controller" | ||||
| 	depends on FTAPE && !FT_STD_FDC | ||||
| 	default "0" | ||||
| 	---help--- | ||||
| 	  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 specified Y to | ||||
| 	  "Floppy tape drive") or module load time (i.e. if you have said M to | ||||
| 	  "Floppy tape drive"). | ||||
|  | ||||
| 	  Please read also the file <file:Documentation/ftape.txt> which | ||||
| 	  contains a short description of the parameters that can be set at | ||||
| 	  boot or load time. | ||||
|  | ||||
| config FT_FDC_IRQ | ||||
| 	int "IRQ channel of the floppy disk controller" | ||||
| 	depends on FTAPE && !FT_STD_FDC | ||||
| 	default "0" | ||||
| 	---help--- | ||||
| 	  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 said Y to | ||||
| 	  "Floppy tape drive") or module load time (i.e. if you said M to | ||||
| 	  "Floppy tape drive"). | ||||
|  | ||||
| 	  Please read also the file <file:Documentation/ftape.txt> which | ||||
| 	  contains a short description of the parameters that can be set at | ||||
| 	  boot or load time. | ||||
|  | ||||
| config FT_FDC_DMA | ||||
| 	int "DMA channel of the floppy disk controller" | ||||
| 	depends on FTAPE && !FT_STD_FDC | ||||
| 	default "0" | ||||
| 	---help--- | ||||
| 	  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 said Y to | ||||
| 	  "Floppy tape drive") or module load time (i.e. if you said M to | ||||
| 	  "Floppy tape drive"). | ||||
|  | ||||
| 	  Please read also the file <file:Documentation/ftape.txt> which | ||||
| 	  contains a short description of the parameters that can be set at | ||||
| 	  boot or load time. | ||||
|  | ||||
| config FT_FDC_THR | ||||
| 	int "Default FIFO threshold (EXPERIMENTAL)" | ||||
| 	depends on FTAPE && EXPERIMENTAL | ||||
| 	default "8" | ||||
| 	help | ||||
| 	  Set the FIFO threshold of the FDC. If this is higher the DMA | ||||
| 	  controller may serve the FDC after a higher latency time. If this is | ||||
| 	  lower, fewer 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 effect. | ||||
|  | ||||
| 	  If unsure, don't touch the initial value, i.e. leave it at "8". | ||||
|  | ||||
| config FT_FDC_MAX_RATE | ||||
| 	int "Maximal data rate to use (EXPERIMENTAL)" | ||||
| 	depends on FTAPE && EXPERIMENTAL | ||||
| 	default "2000" | ||||
| 	---help--- | ||||
| 	  With some motherboard/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. | ||||
|  | ||||
| config FT_ALPHA_CLOCK | ||||
| 	int "CPU clock frequency of your DEC Alpha" if ALPHA | ||||
| 	depends on FTAPE | ||||
| 	default "0" | ||||
| 	help | ||||
| 	  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. | ||||
|  | ||||
| @@ -1,28 +0,0 @@ | ||||
| # | ||||
| #       Copyright (C) 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/Makefile,v $ | ||||
| # $Revision: 1.4 $ | ||||
| # $Date: 1997/10/05 19:17:56 $ | ||||
| # | ||||
| #      Makefile for the QIC-40/80/3010/3020 floppy-tape driver for | ||||
| #      Linux. | ||||
| # | ||||
|  | ||||
| obj-$(CONFIG_FTAPE)		+= lowlevel/ | ||||
| obj-$(CONFIG_ZFTAPE)		+= zftape/ | ||||
| obj-$(CONFIG_ZFT_COMPRESSOR)	+= compressor/ | ||||
| @@ -1,81 +0,0 @@ | ||||
| Some notes for ftape users with PCI motherboards: | ||||
| ================================================= | ||||
|  | ||||
| The problem: | ||||
| ------------ | ||||
|  | ||||
| There have been some problem reports from people using PCI-bus based | ||||
| systems getting overrun errors. | ||||
| I wasn't able to reproduce these until I ran ftape on a Intel Plato | ||||
| (Premiere PCI II) motherboard with bios version 1.00.08AX1. | ||||
| It turned out that if GAT (Guaranteed Access Timing) is enabled (?) | ||||
| ftape gets a lot of overrun errors. | ||||
| The problem disappears when disabling GAT in the bios. | ||||
| Note that Intel removed this setting (permanently disabled) from the | ||||
| 1.00.10AX1 bios ! | ||||
|  | ||||
| It looks like that if GAT is enabled there are often large periods | ||||
| (greater than 120 us !??) on the ISA bus that the DMA controller cannot | ||||
| service the floppy disk controller. | ||||
| I cannot imagine this being acceptable in a decent PCI implementation. | ||||
| Maybe this is a `feature' of the chipset. I can only speculate why | ||||
| Intel choose to remove the option from the latest Bios... | ||||
|  | ||||
| The lesson of this all is that there may be other motherboard | ||||
| implementations having the same of similar problems. | ||||
| If you experience a lot of overrun errors during a backup to tape, | ||||
| see if there is some setting in the Bios that may influence the | ||||
| bus timing. | ||||
|  | ||||
| I judge this a hardware problem and not a limitation of ftape ;-) | ||||
| My DOS backup software seems to be suffering from the same problems | ||||
| and even refuses to run at 1 Mbps ! | ||||
| Ftape will reduce the data-rate from 1 Mbps to 500 Kbps if the number | ||||
| of overrun errors on a track exceeds a threshold. | ||||
|  | ||||
|  | ||||
| Possible solutions: | ||||
| ------------------- | ||||
|  | ||||
| Some of the problems were solved by upgrading the (flash) bios. | ||||
| Other suggest that it has to do with the FDC being on the PCI | ||||
| bus, but that is not the case with the Intel Premiere II boards. | ||||
| [If upgrading the bios doesn't solve the problem you could try | ||||
| a floppy disk controller on the isa-bus]. | ||||
|  | ||||
| Here is a list of systems and recommended BIOS settings: | ||||
|  | ||||
|  | ||||
|         Intel Premiere PCI (Revenge): | ||||
|  | ||||
| Bios version 1.00.09.AF2 is reported to work. | ||||
|  | ||||
|  | ||||
|  | ||||
|         Intel Premiere PCI II (Plato): | ||||
|  | ||||
| Bios version 1.00.10.AX1 and version 11 beta are ok. | ||||
| If using version 1.00.08.AX1, GAT must be disabled ! | ||||
|  | ||||
|  | ||||
|  | ||||
|         ASUS PCI/I-SP3G: | ||||
|  | ||||
| Preferred settings:     ISA-GAT-mode : disabled | ||||
|                         DMA-linebuffer-mode : standard | ||||
|                         ISA-masterbuffer-mode : standard | ||||
|  | ||||
|  | ||||
|         DELL Dimension XPS P90 | ||||
|  | ||||
| Bios version A2 is reported to be broken, while bios version A5 works. | ||||
| You can get a flash bios upgrade from http://www.dell.com | ||||
|  | ||||
|  | ||||
| To see if you're having the GAT problem, try making a backup | ||||
| under DOS. If it's very slow and often repositions you're | ||||
| probably having this problem. | ||||
|  | ||||
|                         --//-- | ||||
|  LocalWords:  ftape PCI bios GAT ISA DMA chipset Mbps Kbps FDC isa AF ok ASUS | ||||
|  LocalWords:  SP linebuffer masterbuffer XPS http www com | ||||
| @@ -1,966 +0,0 @@ | ||||
| Hey, Emacs, we're -*-Text-*- mode! | ||||
|  | ||||
| ===== Release notes for ftape-3.04d 25/11/97 ===== | ||||
| - The correct pre-processor statement for "else if" is "#elif" not | ||||
|   "elsif". | ||||
| - Need to call zft_reset_position() when overwriting cartridges | ||||
|   previously written with ftape-2.x, sftape, or ancient | ||||
|   (pre-ftape-3.x) versions of zftape. | ||||
|  | ||||
| ===== Release notes for ftape-3.04c 16/11/97 ===== | ||||
| - fdc_probe() was calling DUMPREGS with a result length of "1" which | ||||
|   was just fine. Undo previous change. | ||||
|  | ||||
| ===== Release notes for ftape-3.04b 14/11/97 ===== | ||||
|  | ||||
| - patches/2.x.x/floppy.c.diff was somewhat broken, releasing i/o | ||||
|   regions it never had allocated. | ||||
| - fdc_probe() was calling DUMPREGS with a result length of "1" instead | ||||
|   of "10" | ||||
| - Writing deleted data marks if the first segents on track zero are | ||||
|   should work now. | ||||
| - ftformat should now be able to handle those cases where the tape | ||||
|   drive sets the read only status bit (QIC-40/80 cartridges with | ||||
|   QIC-3010/3020 tape drives) because the header segment is damaged. | ||||
| - the MTIOCFTCMD ioctl may now be issued by the superuser ONLY. | ||||
|  | ||||
| ===== Release notes for ftape-3.04a 12/11/97 ===== | ||||
| - Fix an "infinite loop can't be killed by signal" bug in | ||||
|   ftape_get_drive_status(). Only relevant when trying to access | ||||
|   buggy/misconfigured hardware | ||||
| - Try to compensate a bug in the HP Colorado T3000's firmware: it | ||||
|   doesn't set the write protect bit for QIC80/QIC40 cartridges. | ||||
|  | ||||
| ===== Release notes for ftape-3.04 06/11/97 ===== | ||||
| - If positioning with fast seeking fails fall back to a slow seek | ||||
|   before giving up. | ||||
| - (nearly) no retries on "no data errors" when verifying after | ||||
|   formatting. Improved tuning of the bad sector map after formatting. | ||||
| - the directory layout has changed again to allow for easier kernel | ||||
|   integration | ||||
| - Module parameter "ftape_tracing" now is called "ft_tracing" because | ||||
|   the "ftape_tracing" variable has the version checksum attached to it. | ||||
| - `/proc/ftape' interface for 2.0.* kernels. `/proc/ftape' no longer | ||||
|   is a directory but a file that contains all the information formerly | ||||
|   provided in separate files under the `/proc/ftape/' directory. | ||||
| - Most of the configuration options have been prefixed by "CONFIG_FT_" | ||||
|   in preparation of the kernel inclusion. The Makefiles under | ||||
|   "./ftape/" should be directly usable by the kernel. | ||||
| - The MODVERSIONS stuff is now auto-detected. | ||||
| - Broke backslashed multi line options in MCONFIG into separate lines | ||||
|   using GNU-make's "+=" feature. | ||||
| - The html and dvi version of the manual is now installed under | ||||
|   '/usr/doc/ftape` with 'make install` | ||||
| - New SMP define in MCONFIG. ftape works with SMP if this is defined. | ||||
| - attempt to cope with "excessive overrun errors" by gradually | ||||
|   increasing FDC FIFO threshold. But this doesn't seem to have too | ||||
|   much an effect. | ||||
| - New load time configuration parameter "ft_fdc_rate_limit". If you | ||||
|   encounter too many overrun errors with a 2Mb controller then you | ||||
|   might want to set this to 1000. | ||||
| - overrun errors on the last sector in a segment sometimes result in | ||||
|   a zero DMA residue. Dunno why, but compensate for it. | ||||
| - there were still fdc_read() timeout errors. I think I have fixed it | ||||
|   now, please FIXME. | ||||
| - Sometimes ftape_write() failed to re-start the tape drive when a | ||||
|   segment without a good sector was reached ("wait for empty segment | ||||
|   failed"). This is fixed. Especially important for > QIC-3010. | ||||
| - sftape (aka ftape-2.x) has vanished. I didn't work on it for | ||||
|   ages. It is probably still possible to use the old code with | ||||
|   ftape-3.04, if one really needs it (BUT RECOMPILE IT) | ||||
| - zftape no longer alters the contents of already existing volume | ||||
|   table entries, which makes it possible to fill in missing fields, | ||||
|   like time stamps using some user space program. | ||||
| - ./contrib/vtblc/ contains such a program. | ||||
| - new perl script ./contrib/scripts/listtape that list the contents of a | ||||
|   floppy tape cartridge parsing the output of "mt volinfo" + "mt fsf" | ||||
| - the MTWEOF implementation has changed a little bit (after I had a | ||||
|   look at amanda). Calling MTWEOF while the tape is still held open | ||||
|   after writing something to the tape now will terminate the current | ||||
|   volume, and start a new one at the current position. | ||||
| - the volume table maintained by zftape now is a doubly linked list | ||||
|   that grows dynamically as needed. | ||||
|  | ||||
|   formatting floppy tape cartridges | ||||
|   --------------------------------- | ||||
|   * there is a new user space formatting program that does most of the | ||||
|     dirty work in user space (auto-detect, computing the sector | ||||
|     coordinates, adjusting time stamps and statistics). It has a | ||||
|     simple command line interface. | ||||
|   * ftape-format.o has vanished, it has been folded into the low level | ||||
|     ftape.o module, and the ioctl interface into zftape.o. Most of the | ||||
|     complicated stuff has been moved to user space, so there was no | ||||
|     need for a separate module anymore. | ||||
|   * there is a new ioctl MTIOCFTCMD that sends a bare QIC-117 command | ||||
|     to the tape drive. | ||||
|   * there is a new mmap() feature to map the dma buffers into user | ||||
|     space to be used by the user level formatting program. | ||||
|   * Formatting of yet unformatted or totally degaussed cartridges | ||||
|     should be possible now. FIXME. | ||||
|  | ||||
| ===== Release notes for ftape-3.03b, <forgot the exact date> ==== | ||||
|  | ||||
| ftape-3.03b was released as a beta release only. Its main new feature | ||||
| was support of the DITTO-2GB drive. This was made possible by reverse | ||||
| engineering done by <fill in his name> after Iomega failed to support | ||||
| ftape. Although they had promised to do so (this makes me feel a bit | ||||
| sad and uncomfortable about Iomega). | ||||
|  | ||||
| ===== Release notes for ftape-3.03a, 22/05/97 ==== | ||||
|  | ||||
| - Finally fixed auto-un-loading of modules for kernels > 2.1.18 | ||||
| - Add an "uninstall" target to the Makefile | ||||
| - removed the kdtime hack | ||||
| - texi2www didn't properly set the back-reference from a footnote back | ||||
|   to the regular text. | ||||
|  | ||||
|   zftape specific | ||||
|   --------------- | ||||
|   * hide the old compression map volume. Taper doesn't accept the | ||||
|     presence of non-Taper volumes and Taper-written volume on the same | ||||
|     tape. | ||||
|   * EOD (End Of Data) handling was still broken: the expected behavior | ||||
|     is to return a zero byte count at the first attempt to read past | ||||
|     EOD, return a zero byte count at the second attempt to read past | ||||
|     EOD and THEN return -EIO. | ||||
|    | ||||
|   ftape-format specific | ||||
|   --------------------- | ||||
|   * Detection of QIC-40 cartridges in select_tape_format() was broken | ||||
|     and made it impossible to format QIC-3010/3020 cartridges. | ||||
|   * There are strange "TR-1 Extra" cartridges out there which weren't | ||||
|     detected properly because the don't strictly conform to the | ||||
|     QIC-80, Rev. N, spec. | ||||
|  | ||||
| ===== Release notes for ftape-3.03, 30/04/97 ===== | ||||
|  | ||||
| - Removed kernel integration code from the package. I plan to provide | ||||
|   a package that can be integrated into the stock kernel separately | ||||
|   (hopefully soon). | ||||
|   As a result, a simple `make' command now will build everything. | ||||
| - ALL compile time configuration options have been moved to the file | ||||
|   `MCONFIG'. | ||||
| - Quite a few `low level' changes to allow formatting of cartridges. | ||||
| - formatting is implemented as a separate module `ftape-format.o'. The | ||||
|   modified `mt' program contains sample code that shows how to use it. | ||||
| - The VFS interface has been moved from the `ftape.o' module to the | ||||
|   high level modules `zftape.o' resp. `sftape.o'. `ftape.o' contains | ||||
|   the hardware support only. | ||||
| - A bit of /proc support for kernels > 2.1.28 | ||||
| - Moved documentation to Doc subdir. INSTALL now contains some real | ||||
|   installation notes. | ||||
| - `install' target in Makefile. | ||||
|  | ||||
| zftape specific: | ||||
| ---------------- | ||||
|  | ||||
| - zftape works for large cartridges now ( > 2^31 bytes) | ||||
| - MTIOCVOLINFO and MTIOCGETSIZE now return the size in KILOBYTES, | ||||
|   NO LONGER in bytes. | ||||
|  | ||||
| - permissions for write access to a cartridge have changed: | ||||
|   * zftape now also takes the file access mode into account | ||||
|   * zftape no longer allows writing in the middle of the recorded | ||||
|     media. The tape has to be positioned at BOT or EOD for write | ||||
|     access. | ||||
|  | ||||
| - MTBSF has changed. It used to position at the beginning of the | ||||
|   previous file when called with count 1. This was different from the | ||||
|   expected behavior for other Un*x tape drivers (i.e. SCSI). MTBSF | ||||
|   with count 1 should merely position at the beginning of the current | ||||
|   volume. Fixed. As a result, `tar --verify' now produces the desired | ||||
|   result: it verifies the last written volume, not the pre-last | ||||
|   written volume. | ||||
|  | ||||
| - The compression map has vanished --> no need for `mt erase' any | ||||
|   more. Fast seeking in a compressed volume is still be possible, but | ||||
|   takes slightly longer. As a side effect, you may experience an | ||||
|   additional volume showing up in front of all others for old | ||||
|   cartridges. This is the tape volume that holds the compression map. | ||||
|  | ||||
| - The compression support for zftape has been moved to a separate | ||||
|   module `zft-compressor'. DON'T forget to load it before trying to | ||||
|   read back compressed volumes. The stock `zftape.o' module probes for | ||||
|   the module `zft-compressor' using the kerneld message channel; you | ||||
|   have to install `zft-compressor.o' in a place where modprobe can | ||||
|   find it if you want to use this. | ||||
|  | ||||
| - New experimental feature that tries to get the broken down GMT time | ||||
|   from user space via a kernel daemon message channel. You need to | ||||
|   compile and start the `kdtime' daemon contained in the contrib | ||||
|   directory to use it. Needed (?) for time stamps in the header | ||||
|   segments and the volume table. | ||||
|  | ||||
| - variable block size mode via MTSETBLK 0 | ||||
|  | ||||
| - keep modules locked in memory after the block size has been changed | ||||
|  | ||||
| sftape specific: | ||||
| ---------------- | ||||
|  | ||||
| - end of tape handling should be fixed, i.e. multi volume archives | ||||
|   written with `afio' can be read back now. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-3.02a, 09/01/97 ===== | ||||
|  | ||||
| No big news: | ||||
| - call zft_init() resp. sft_init() when compiling the entire stuff | ||||
|   into the kernel image. | ||||
| - fix bug in ftape-setup.c when NO_TRACE_AT_ALL was defined. | ||||
| - fix bug in sftape-eof.c/zftape-eof.c for old kernels (1.2.*) | ||||
| - add support for new module interface for recent kernels | ||||
|  | ||||
| ===== Release notes for ftape-3.02, 16/12/96 ===== | ||||
| - Fixed the `FDC unlock command failed' bug in fdc-io.c. When the FIFO | ||||
|   was already locked when ftape was loaded, ftape failed to unlock it. | ||||
| - Fixed compilation of `contrib/gnumt'. It now finds `mtio.h' even if | ||||
|   ftape is NOT included into the kernel source tree. | ||||
| - fc-10.c: include <asm/io.h> for inb() and outb(). | ||||
| - ftape/sftape/zftape: all global variable now have either a `ftape_', | ||||
|   a `ft_', `sft_', `zft_' or `qic_' prefix to prevent name clashes | ||||
|   with other parts of the kernel when including ftape into the kernel | ||||
|   source tree. | ||||
| - Kerneld support has changed. `ftape' now searches for a module | ||||
|   `ftape-frontend' when none of the frontend (`sftape' or `zftape') is | ||||
|   loaded. Please refer to the `Installation/Loading ftape' section of | ||||
|   the TeXinfo manual. | ||||
| - Add load resp. boot-time configuration of ftape. There are now | ||||
|   variables ft_fdc_base, ft_fdc_dma and ft_fdc_irq corresponding to | ||||
|   the former FDC_BASE etc. compile time definitions. One can also use | ||||
|   the kernel command line parameters to configure the driver if it is | ||||
|   compiled into the kernel. Also, the FC-10/FC-20 support is load-time | ||||
|   configurable now as well as the MACH-II hack (ft_probe_fc10, | ||||
|   resp. ft_mach2). Please refer to the section `Installation/Configure | ||||
|   ftape' of the TeXinfo manual. | ||||
| - I removed the MODVERSIONS option from `Makefile.module'. Let me alone | ||||
|   with ftape and MODVERSIONS unless you include the ftape sources into | ||||
|   the kernel source tree. | ||||
| - new vendors in `vendors.h': | ||||
|   * HP Colorado T3000  | ||||
|   * ComByte DoublePlay (including a bug fix for their broken | ||||
|     formatting software, thanks to whraven@njackn.com) | ||||
|   * Iomega DITTO 2GIG. NOTE: this drive cannot work with ftape because | ||||
|     the logical data layout of the cartridges used by this drive does | ||||
|     NOT conform to the QIC standards, it is a special Iomega specific | ||||
|     format. I've sent mail to Iomega but didn't receive an answer | ||||
|     yet. If you want this drive to be supported by ftape, ask Iomega | ||||
|     to give me information about it. | ||||
| - zftape: | ||||
|   * re-introduced the MTIOC_ZFTAPE_GETBLKSZ ioctl for compatibility | ||||
|     with zftape 1.06a and earlier. Please don't use it when writing | ||||
|     new software, use the MTIOCVOLINFO ioctl instead. | ||||
|   * Major overhaul of the code that updates the header segments. Never | ||||
|     change the tape label unless erasing the tape. Thus we almost | ||||
|     never need to write the header segments, unless we would modify | ||||
|     the bad sector map which isn't done yet. Updating of volume table | ||||
|     and compression map more secure now although it takes a bit | ||||
|     longer. | ||||
|   * Fixed bug when aborting a write operation with a signal: zftape | ||||
|     now finishes the current volume (i.e. writes an eof marker) at the | ||||
|     current position. It didn't before which led to somehow *strange* | ||||
|     behavior in this cases. | ||||
|   * Keep module locked in memory when using it with  the non-rewinding | ||||
|     devices and the tape is not logical at BOT. Needed for kerneld | ||||
|     support. | ||||
| - sftape: | ||||
|   * Keep module locked in memory when using it with  the non-rewinding | ||||
|     devices and the tape is not logical at BOT. Needed for kerneld | ||||
|     support. | ||||
|  | ||||
| ===== Release notes for ftape-3.01, 14/11/96 ===== | ||||
|  | ||||
| - Fixed silly bugs in ftape-3.00: | ||||
|   * MAKEDEV.ftape: major device number must be 27, not 23  | ||||
|   * sftape/sftape-read.c: sftape_read_header_segments() called  | ||||
|     itself recursively instead of calling ftape_read_header_segment() | ||||
|   * zftape/qic-vtbl.h: conversion of ftape's file marks to zftape's | ||||
|     internal volume table was broken. | ||||
|   * patches/2.x.x/linux-2.0.21.dif: my RCS (resp. CVS) system replaced | ||||
|     the `$Revison:' etc. macros in the `ftape.h' concerning part of the | ||||
|     patch :-( Fixed. | ||||
|   * info/ftape.info: Fixed misspellings (`cp' <-> `cp -r' etc.) | ||||
|   * when ftape/sftape or ftape/zftape was compiled into the kernel the | ||||
|     variable ftape_status was declared twice. Fixed. | ||||
|   * removed reference to undeclared variable kernel_version when not | ||||
|     compiling as module | ||||
|   * fixed a bug introduced by the use of bit-fields for some flags | ||||
|     (i.e. write_protected, no_cartridge, formatted) | ||||
|   * flag `header_read' is now reset correctly to zero when tape is | ||||
|     removed. | ||||
| - fixed a bug in sftape/sftape-eof.c that was already in the original | ||||
|   ftape code. MTFSF/BSF was not handled correctly when positioned | ||||
|   right before the file mark (think of tar) | ||||
| - Changed TRACE macros (following a suggestion of Marcin Dalecki) to use | ||||
|   the predefined __FUNCTION__ macro of GCC. Spares about 4k of code. | ||||
| - added new vendor id for Iomega DITTO 2GIG | ||||
| - fixed a bug already present in zftape-1.06 when aborting a write | ||||
|   with a signal: we now finish the current volume at that | ||||
|   position. Header segments remain NOT up to date until an explicit call | ||||
|   to MTREW or MTOFFL is done.   | ||||
|  | ||||
| ===== Release notes for ftape-3.00, 14/10/96 ===== | ||||
|  | ||||
| - Merged ftape with zftape. There are three modules now: | ||||
|   ftape for the hardware support, sftape for the implementation of the | ||||
|   original ftape eof mark stuff and zftape that implements zftape's way | ||||
|   of handling things (compression, volume table, tape blocks of | ||||
|   constant length) | ||||
| - Documentation in TeXinfo format in the `info' subdirectory. | ||||
| - New ioctls for zftape. See zftape/zftape.h | ||||
| - Dummy formatting ioctl for ftape. See ftape.h | ||||
| - Kernel patch files for the 2.*.* series to include ftape-3.00 in the | ||||
|   kernel source tree. These includes a kernel compatible Config.in | ||||
|   script and fairly large online information for the kernel configure | ||||
|   script. | ||||
| - Support for compiling with Linux-1.2.13.  | ||||
| - Modified GNU mt from their cpio package that can handle the new | ||||
|   ioctls. | ||||
| - ftape/sftape/zftape is kerneld save now! | ||||
|  | ||||
| Notes on sftape: | ||||
| - sftape implements the eof handling code of the original ftape. If | ||||
|   you like to stick with the original ftape stuff, you have to use | ||||
|   this module, not zftape. | ||||
| - sftape is kerneld save, unlike the original ftape. | ||||
| - we keep the entire header segment now in memory, so no need to read | ||||
|   it before updating the header segments. Additional memory | ||||
|   consumption: 256 bytes.  | ||||
|  | ||||
| Notes for zftape: | ||||
| - zftape has support for tapes with format code 6 now, which use a | ||||
|   slightly different volume table format compared with other floppy | ||||
|   tapes. | ||||
| - new ioctls for zftape. Have a look at zftape/zftape.h | ||||
| - The internal volume table representation has changed for zftape. Old | ||||
|   cartridges are converted automatically. | ||||
| - zftape no longer uses compression map segments, which have vanished | ||||
|   from the QIC specs, but creates volume table entry that reserves | ||||
|   enough space for the compression map.  | ||||
| - zftape is kerneld save now. | ||||
| - we keep the entire header segment now in memory, so no need to read | ||||
|   it before updating the header segments. Additional memory | ||||
|   consumption: 256 bytes.  | ||||
|  | ||||
| Notes for contrib/gnumt: | ||||
| - modified mt from the GNU cpio package that supports all the new | ||||
|   ioctls of zftape. | ||||
| Notes for contrib/swapout: | ||||
| - This contains the swapout.c program that was written by Kai | ||||
|   Harrekilde-Pederson. I simply added a Makefile. | ||||
|  | ||||
| ===== Release notes for ftape-2.10, 14/10/96 ===== | ||||
|  | ||||
| The ftape maintainer has changed.  | ||||
| Kai Harrekilde-Petersen <khp@dolphinics.no> | ||||
| has resigned from maintaining ftape, and I, | ||||
| Claus-Justus Heine <claus@momo.math.rwth-aachen.de>, | ||||
| have taken over. | ||||
|  | ||||
| - Added support for tapes with `format code 6', i.e. QIC-3020 tapes | ||||
|   with more than 2^16 segments. | ||||
| - merged changes made by Bas Laarhoven with ftape-2.09. Refer | ||||
|   to his release notes below. I've included them into this | ||||
|   file unchanged for your reference. | ||||
| - disabled call stack back trace for now. This new feature | ||||
|   introduced by the interim release 2.0.x still seems to | ||||
|   be buggy. | ||||
| - Tried to minimize differences between the ftape version | ||||
|   to be included into the kernel source tree and the standalone | ||||
|   module version. | ||||
| - Reintroduced support for Linux-1.2.13. Please refer to the | ||||
|   Install-guide.  | ||||
|  | ||||
| ===== Release notes for ftape-2.09, 16/06/96 ===== | ||||
|  | ||||
| There aren't any really big news in this release, mostly just that I | ||||
| (the maintainer) have changed my email address (due to a new job).  My | ||||
| new address is <khp@dolphinics.no> | ||||
|  | ||||
| - The CLK_48MHZ and FDC_82078SL options has gone (all 2Mbps cards seem | ||||
|   to use a 48MHz oscillator anyway and I haven't heard of an 'SL | ||||
|   chip out there). | ||||
| - The S82078B has been `downgraded' to i82077AA compability. | ||||
| - TESTING option revived.  Right now, it'll enable the (seriously broken) | ||||
|   2Mbps code.  If you enable it, you'll experience a tape drive that's | ||||
|   *really* out to lunch! | ||||
| - Some (bold) changes in the init code.  Please notify me if they | ||||
|   break things for you. | ||||
|  | ||||
| ===== Release notes for ftape-2.08, 14/03/96 ===== | ||||
|  | ||||
| If you correct a problem with ftape, please send your patch to | ||||
| khp@dolphinics.no too. | ||||
|  | ||||
| - Updated to reflect that NR_MEM_LISTS is gone in 1.3.74 | ||||
| - Teac 700 added to list of known drives. | ||||
| - The registered device name is now "ft" rather than "ftape". | ||||
|  | ||||
| ===== Release notes for ftape-2.07a, 14/03/96 ===== | ||||
|  | ||||
| Bugfixes by Marcin Dalecki <dalecki@namu03.gwdg.de>: | ||||
| - In the last release it just compiled against 1.3.70; | ||||
|   now the params to request_irq() and free_irq are() are fixed, so it also  | ||||
|   works in 1.3.73 :-) | ||||
| - Support for modules is now correct for newer kernels. | ||||
|  | ||||
| ===== Release notes for ftape-2.07, 04/03/96 ===== | ||||
|  | ||||
|  | ||||
| - ftape updated to compile against 1.3.70. | ||||
| - Iomega 700 and Wangtek 3200 recognised. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.06b, 13/02/96 ===== | ||||
|  | ||||
| Another simple bugfix version. | ||||
|  | ||||
| - Jumbo 700 recognised. | ||||
| - Typo in vendors.h fixed. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.06a, 10/02/96 ===== | ||||
|  | ||||
| This release is a simple bugfix version. | ||||
|  | ||||
| - Linux/SMP: ftape *should* work. | ||||
| - FC-10/20: Only accepts IRQs 3-7, or 9.  If IRQ 9, properly tell the card | ||||
|   to use IRQ 2.  Thanks to Greg Crider (gcrider@iclnet.org) for finding and | ||||
|   locating this bug and testing the patch. | ||||
| - Insight drive recognised correctly again. | ||||
| - Motor-on wakeup version of the Iomega 250 drive added | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.06, 28/01/96 ===== | ||||
|  | ||||
| Special thanks go to Neal Friedman and Steven Sorbom for their | ||||
| help in producing and testing this release. | ||||
|  | ||||
| I have continued to clean up the code, with an eye towards inclusion | ||||
| of ftape in Linus' official kernel (In fact, as I type this, I am | ||||
| running on a kernel with ftape support statically linked).  I have | ||||
| test-compiled ftape against my 1.2.13 tree without problems. | ||||
| Hopefully, everything should be OK for the v1.2.x people. | ||||
|  | ||||
| WARNING! Alan Cox has mailed me that ftape does *NOT* work with | ||||
| Linux/SMP.  If you try to run ftape under Linux/SMP, it will cause a | ||||
| kernel deadlock (which is worse than a panic). | ||||
|  | ||||
| - QIC-3020/TR-3: 1Mbps support works.  Neal is capable of reading and | ||||
|   writing data to a tape.  ftape will automatically detect the type of | ||||
|   tape (e.g. TR-3 vs QIC-80) and move the fdc in and out of | ||||
|   "perpendicular mode" as necessary. | ||||
| - 2Mbps support is disabled by default, since it is not fully | ||||
|   debugged.  If you are adventurous, remove -DFDC_82078SL in the | ||||
|   Makefile and see what happens :-) | ||||
| - fdc detection: silly bugs removed (Only 2Mbps fdcs were affected) | ||||
|   and added detection of the National Semiconductors PC8744 fdc chip | ||||
|   (used in the PC873xx "super-IO" chips). | ||||
| - Removed warning about incompatible types when compiling with Linux | ||||
|   1.2.x. | ||||
| - README.PCI updated with info about the DELL Dimension XPS P90. | ||||
| - Connor TST3200R added to detected drives. | ||||
| - `swapout' utility added to distribution.  It will dirty 5Meg of | ||||
|   memory, trying to swap out other programs.  Just say `make swapout' | ||||
|   to build it.  ftape will do this automatically Real Soon Now (ie: | ||||
|   when I have found out which kernel memory alloc function to call). | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.05, 08/01/96 ===== | ||||
|  | ||||
| - For v1.2.x Kernels, you must apply the patch linux-1.2/ksyms.patch to | ||||
|   the kernel and rebuild it (it adds the __get_dma_pages symbol to | ||||
|   ksyms.c). | ||||
| - Included new asm-i386/io.h file from v1.3.x kernel series, to enable | ||||
|   gcc v.2.7.[12] to compile v1.2.x kernels (linux-1.2/io.h). | ||||
| - Module versions: If you wish to compile ftape as a versioned module, | ||||
|   you must first compile your kernel with CONFIG_MODVERSIONS=y. | ||||
|   Otherwise, you will get complaints that <linux/modversions.h> does not | ||||
|   exist (if that happens, a `touch modversions.h' will help you out). | ||||
| - CLK_48MHZ: new define in the Makefile (default: non-zero).  If you have | ||||
|   a tape controller card that uses the i82078(-1) chip, but cannot get | ||||
|   it to work with ftape, try set it to 0 (and please report this). | ||||
| - QIC-3010/3020: Complete support is still missing, but will hopefully | ||||
|   come soon.  Steven Sorbom has kindly provided me with hints about | ||||
|   this.  Writing of QIC-3020 tapes definitely does NOT work (do not try | ||||
|   it! - the drive will not be in "perpendicular mode" and this will ruin | ||||
|   the formatting info on the tape). | ||||
| - ftape_num_buffers is out of fashion: use NR_BUFFERS instead (and | ||||
|   recompile if you want to change it :-). | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.04, 01/01/96 ===== | ||||
|  | ||||
| This version by Kai Harrekilde-Petersen <khp@dolphinics.no> | ||||
|  | ||||
| - ALERT! Support for Kernels earlier then v1.1.85 is about to go away. | ||||
|   I intend to clean up some of the code (getting rid of an annoyingly | ||||
|   large numbers of #ifdef mostly), which means that support for | ||||
|   pre-1.1.85 kernels must go as well. | ||||
| - NR_FTAPE_BUFFERS is gone; You can instead select the number of dma | ||||
|   buffers by saying `insmod ftape.o ftape_num_buffer=<n>' instead. | ||||
| - Configure script gone.  ftape will now automagically determine your | ||||
|   kernel version by /usr/include/linux/version.h instead. | ||||
| - CONFIG_MODVERSIONS now work.  All combinations of versioned / | ||||
|   unversioned kernel and ftape module works (at least with my 1.3.52 | ||||
|   kernel). | ||||
| - If you have problems with inserting ftape into an old (1.2.x) | ||||
|   kernel (e.g. insmod says "1.2.8 does not match 1.2.8), recompile | ||||
|   your modules utilities with your new compiler. | ||||
| - Reveal TB1400 drive added to vendors.h | ||||
| - Support for the i82078-1 (2Mbps) chip is coming along.  The | ||||
|   biggest problem is that I don't have such a card, which makes | ||||
|   testing / debugging somewhat problematic.  The second biggest | ||||
|   problem is that I do not have the QIC-3010/3020 standards either. | ||||
|   Status right now is that the chip is detected, and it should be | ||||
|   possible to put it into 2Mbps mode.  However, I do not know what | ||||
|   "extras" are needed to complete the support.  Although putting the | ||||
|   i82078 into 1Mbps mode ought to work out of the box, it doesn't | ||||
|   (right now, ftape complains about id am errors). | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.04beta5, 29/12/95 ===== | ||||
|  | ||||
| Bas offline linux-tape | ||||
| ---------------------- | ||||
| For reasons only known to the majordomo mail list processor, Bas was | ||||
| kicked off the linux-tape list sometime during the summer.  Being | ||||
| overworked at his for-pay job, he didn't notice it much.  Instead I | ||||
| (Kai, khp@dolphinics.no) has worked on ftape to produce the 2.04(beta) | ||||
| version. | ||||
|  | ||||
| zftape | ||||
| ------ | ||||
| Note that there exists a much improved version of ftape, written by | ||||
| Claus-Justus Heine <claus@willi.math.rwth-aachen.de> which is named | ||||
| zftape, which conforms to the QIC-80 specs on how to mark backups, and | ||||
| is capable of doing automatic compression.  However, zftape makes | ||||
| substantial changes to ftape, and I (Kai) have therefore declined to | ||||
| integrate zftape into ftape.  Hopefully, this will happen soon. | ||||
|  | ||||
| CONFIG_QIC117 removed from the kernel | ||||
| ------------------------------------- | ||||
| The biggest change of all is that ftape now will allocate its dma | ||||
| buffers when it is inserted.  The means that the CONFIG_QIC117 option | ||||
| has disappeared from the Linux kernel as of v1.3.34.  If you have an | ||||
| earlier kernel, simply answer 'no' to the question will do the trick | ||||
| (if you get complains about __get_free_pages() missing, contact the | ||||
| linux-tape mailing list). | ||||
|  | ||||
| Note that ftape-2.04beta will work equally well on kernels with and | ||||
| without `ftape support'.  The only catch is, that you will waste | ||||
| around 96-128Kb of precious DMA'able memory on a box that has ftape | ||||
| support compiled in. | ||||
|  | ||||
| Now for the real changes: | ||||
|  | ||||
| - FC-20 can now use DMA channels 1, 2, and 3. Thanks to Daniel | ||||
|   Cohen, catman@wpi.edu. | ||||
| - ftape no longer requires a (gigantic) 96Kb buffer to be statically | ||||
|   allocated by the kernel. | ||||
| - Added new Iomega drive (8882) to vendors.h | ||||
| - -fno-strength-reduce added to Makefile, since GCC is broken. | ||||
| - i82078-1 (2Mbps) FDC support started. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.03b, 27/05/95 ===== | ||||
|  | ||||
| - Prevented verify_area to return error if called with zero length. | ||||
| - Fixed a bug in flush_buffers that caused too much padding to be | ||||
|   written when a final segment had bad sectors. | ||||
| - Increased maximum fast-seek overshoot value from 5 to 10 segments. | ||||
| - Breaking loop after 5 retries when positioning fails. | ||||
| - Fixed wrong calculation of tape length for QIC-3010 and QIC-3020 | ||||
|   tapes (densities were swapped). | ||||
| - Fixed wrong calculation of overshoot on seek_forward: Wrong sign | ||||
|   of error. | ||||
| - Suppress (false) error message due to new tape loaded. | ||||
| - Added two new CMS drives (11c3 and 11c5) to vendors.h. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.03a, 09/05/95 ===== | ||||
|  | ||||
| - Fixed display of old error (even if already cleared) in ftape_open. | ||||
| - Improved tape length detection, ioctls would fail for 425 ft tapes. | ||||
|   Until the tape length is calculated with data from the header | ||||
|   segment, we'll use worst-case values. | ||||
| - Clear eof_mark after rewinding ioctls. | ||||
| - Fixed wrong version message (2.03 had 2.02g id). | ||||
| - Fixed bug that caused the fdc to be reset very frequently. | ||||
|   This shouldn't affect normal operation but the timing of the | ||||
|   report routines has changed again and that may cause problems. | ||||
|   We'll just have to find out.... | ||||
| - Implemented correct write precompensation setting for QIC-3010/3020. | ||||
| - Cleaned up fdc_interrupt_wait routine. Hope it still works :-) | ||||
| - Finally removed (already disabled) special eof mark handling for | ||||
|   gnu tar. | ||||
| - Changed order of get_dma_residue and disable_dma in fdc-isr.c | ||||
|   because the current order would fail on at least one system. | ||||
|   We're back to the original order again, hope (and expect) this | ||||
|   doesn't break any other system. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.03, 07/05/95 ===== | ||||
|  | ||||
| (Changes refer to the first ftape-2.02 release) | ||||
|  | ||||
| Support for wide and extended length tapes | ||||
| ------------------------------------------ | ||||
| The Conner TSM 420 and 850 drives are reported to be working. | ||||
| I haven't received any reports about other brands; the TSM 420 | ||||
| and 850 seem to be the most widely used wide drives. | ||||
| Extended length tapes (425 ft) with normal QIC-80 drives | ||||
| are operating too (At least I've had no reports stating otherwise). | ||||
| _Not_ yet completely supported (although they may work) are | ||||
| QIC-3020 drives and 2 Mbps floppy disk controllers won't work at | ||||
| the highest speed. | ||||
| If someone is kind enough to send me one of these, I'll include | ||||
| support for it too ;-) | ||||
|  | ||||
| Easier configuration | ||||
| -------------------- | ||||
| Problems due to wrong settings in the Makefile are prevented | ||||
| by using a configuration script that sets the necessary (kernel | ||||
| version dependent) compile time options. | ||||
| This kernel version is now determined from the sources found | ||||
| at /usr/src/linux, or if not found, the old way using | ||||
| /proc/version. | ||||
| Versioned modules will be used automatically when supported | ||||
| by- and configured in- the kernel. | ||||
| Note that the current modules code (1.1.87) is still broken | ||||
| and _needs_ the fix included in the insmod directory. | ||||
| Please don't send me any more Oops reports caused by insmod :-( | ||||
|  | ||||
| Reduced module size | ||||
| ------------------- | ||||
| The standard module size is much reduced and some compile time | ||||
| options can even reduce it further. (I don't recommend this | ||||
| for normal use but it can be handy for rescue diskettes) | ||||
|  | ||||
| Option:           Approx. module size: | ||||
|  | ||||
| <standard>             150 Kb | ||||
| NO_TRACE               125 Kb | ||||
| NO_TRACE_AT_ALL         67 Kb | ||||
|  | ||||
|  | ||||
| Much improved driver interruption | ||||
| --------------------------------- | ||||
| Most possible loops have been broken and signal detection | ||||
| has been improved. | ||||
| In most cases the driver can be aborted by ^C (SIGINT) and | ||||
| SIGKILL (kill -9) will generate be a sure kill. | ||||
| (Note that aborting a tape operation may damage the last | ||||
| data written to tape) | ||||
|  | ||||
| Improved error recovery | ||||
| ----------------------- | ||||
| Ftape now returns an error (ENODATA) to the application if | ||||
| a segment proves to be unrecoverable and then skips the | ||||
| bad segment. | ||||
| This causes most applications to continue to work (tar | ||||
| and afio) loosing only a small amount (up to 29 Kb) of data. | ||||
| Retried read operations will now be done slightly off-track | ||||
| to improve the chance of success. Serious head off-track | ||||
| errors will be detected. | ||||
|  | ||||
| FC-10 and FC-20 controllers | ||||
| --------------------------- | ||||
| Ftape now supports both the old CMS FC-10 and the newer FC-20 | ||||
| controllers. | ||||
| Because the operation of these cards is still undocumented, | ||||
| thus far they will only work with the default settings (See | ||||
| Makefile). Any feed-back on how to use them with other settings | ||||
| will be welcome ! | ||||
| Compilation will fail if one changes the settings to illegal | ||||
| values. | ||||
|  | ||||
| Kernels and compilers | ||||
| --------------------- | ||||
| Ftape is currently being developed using the 2.5.8 compiler. | ||||
| The older 2.4.5 probably works too (Set option in Makefile!). | ||||
| I have no experience with any later compilers nor Elf support. | ||||
| Any information on this is welcome. | ||||
| The latest kernel I have tested ftape with is 1.2.6. | ||||
|  | ||||
| Compression | ||||
| ----------- | ||||
| An impressive collection of changes for ftape including | ||||
| on-the-fly compression is still lying on my desk. | ||||
| If 2.03 proves to be reliable I might start integrating these | ||||
| but as usual, I'm short in time :-( | ||||
|  | ||||
| Formatting | ||||
| ---------- | ||||
| There is still no way to format tapes under Linux. As far as | ||||
| I know all attempts to write such a program have died now. | ||||
| Since formatted tapes are rather common now, I think all we | ||||
| need is a utility that writes a worst case pattern and verifies | ||||
| that with the drive put in verify mode, reducing margins. | ||||
| Any takers ? | ||||
|  | ||||
| Furthermore | ||||
| ----------- | ||||
| Cleaned up messages. | ||||
| Prepared to support multiple tape drives on one fdc. | ||||
| Thanks to all the people who sent bug reports and helped me | ||||
| improve the driver. Without trying to be complete I'll mention | ||||
| Gary Anderson (without his accurate reports and unreliable | ||||
| hardware there wouldn't be a 2.03), Stefan Kneifel (FC-20), | ||||
| Robert Broughton (FC-20, you were almost there ;-), Bjorn | ||||
| Ekwall (for the versioned modules and buggy insmod ;-), Peter | ||||
| Fox, Christopher Oliver, Ralph Whittaker and not the least | ||||
| Linus Torvalds (for Linux and keeping me busy because of | ||||
| changes to the kernel ;-) | ||||
| Thanks to anyone I forgot, for the bug reports, the ftape | ||||
| bashing and the mental support... | ||||
|  | ||||
|  | ||||
| That's it for now. Have Fun, | ||||
|  | ||||
| Bas. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.02g, 06/05/95 ===== | ||||
|  | ||||
| - Added extra test to break read-id loop with signal. | ||||
| - Changed rewind code to handle negative overshoot for drives | ||||
|   that take very long to start or stop. | ||||
| - Let use of get/set i/o-regions depend on kernel version. | ||||
| - Changed code to use a more general test for conditional | ||||
|   compilations depending on kernel version. | ||||
| - Improved micro-step functionality to go off-track only | ||||
|   while reading (id & data). | ||||
| - Added failure on tape-not-referenced bit in ftape_command. | ||||
| - Added FOREVER option to read-wait routine. | ||||
| - Changed read-id to use shorter timeout causing smaller | ||||
|   rewinds on timeout. | ||||
| - Made kernel-interface functions static. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.02f, 03/05/95 ===== | ||||
|  | ||||
| - Added support for dual tape drives on my system, extended Configure | ||||
|   script to detect host 'dodo'. | ||||
| - Log media defect in history if ecc failed and no data was returned. | ||||
| - Fixed Configure script that was failing for kernel versions with | ||||
|   double digit version or revision numbers. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.02e, 01/05/95 ===== | ||||
|  | ||||
| - Fixed reposition loop at logical eot (failing read_id). | ||||
| - Fixed 34 segment offset when rewinding. | ||||
| - Added fast seek capability for more than 255 segments. | ||||
| - Fixed wrong busy result from ftape_command causing reverse | ||||
|   seek to fail. | ||||
| - Added breakout from infinite rewind loop (if something fails). | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.02d, 30/04/95 ===== | ||||
|  | ||||
| - Improved abortion on signals: Interrupt will make a graceful | ||||
|   exit, Kill will be less nice and should be used if everything | ||||
|   else fails. | ||||
| - Included check for tape-head off track. | ||||
| - Implemented exit from tape-start loop. | ||||
| - Added kernel io-port registration. | ||||
| - Implemented skip of failing segment (ENODATA) on ecc failure. | ||||
|   This allows afio and tar to continue when the tape is damaged. | ||||
| - Made distinction between drive names with different codes. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.02c, 22/04/95 ===== | ||||
|  | ||||
| - Fixed too tight command queueing after tape stop/pause command | ||||
|   issued from within interrupt service routine (Showed as timeout | ||||
|   on Acknowledge errors during retries on some systems) | ||||
| - Tried to fix timeouts when using 425 ft tape because the extended | ||||
|   length doesn't seem to be detected by the hardware. | ||||
|   We now use the format code from the header segment so adjust the | ||||
|   timing after reading the header segment. | ||||
| - Fixed some messages stating 'unexpected something...' being not | ||||
|   unexpected anymore. | ||||
| - Started preparations for merge of dynamic buffer allocation and | ||||
|   compression code. | ||||
| - Changed some debug messages to include relevant segment information | ||||
|   at level 4. | ||||
| - Included early bail-out when drive offline, preventing a lot of | ||||
|   false messages. | ||||
| - Moved ftape_parameter_xxx() offsets into function instead of in calls. | ||||
| - Removed 'weird, drive busy but no data' error when caused by | ||||
|   an error during a read-id. | ||||
| - Improved 'timeout on acknowledge' diagnostics. | ||||
| - Moved MODULE option into Configure. | ||||
| - Reduced code size when no tracing at all was set (Claus Heine). | ||||
| - No longer log error code 0 (no error) as an error. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.02b, 09/04/95 ===== | ||||
|  | ||||
| - Relaxed timing for status operation and displaying | ||||
|   abnormal results. Hopefully this shows what's going | ||||
|   wrong with the Conner TSM850R drives. | ||||
| - Created script for configuration, using version number | ||||
|   of kernel source if available, otherwise /proc/version. | ||||
| - Fixed conditionals in kernel-interface.c. | ||||
| - Removed unavoidable TRACE output. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.02a, 01/04/95 ===== | ||||
|  | ||||
| - Implemented `new-style' (versioned) modules support for new | ||||
|   kernels. | ||||
| - Reduced size of module by moving static data to bss. | ||||
| - Now using version number of kernel source instead of running | ||||
|   kernel for kernel versions >= 1.1.82 | ||||
| - Added feedback on drive speeds to vendor information. | ||||
| - Included fixed insmod sources to distribution (Let's hope | ||||
|   the modules distribution get fixed soon :-/). | ||||
|  | ||||
| Note that I haven't yet implemented any of the code extension I | ||||
| received. I hope to find some time to do this soon. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.02, 15/01/95 ===== | ||||
|  | ||||
|  | ||||
| - Fixed failing repositioning when overshoot was incremented. | ||||
| - Fixed rate selection: Because of a deficiency in the QIC-117 | ||||
|   specification one cannot distinguish between a not implemented | ||||
|   and a failing command. Therefor we now try to find out if the | ||||
|   drive does support this command before usage. | ||||
| - Fixed error retry using wrong offset in fdc-isr. | ||||
| - Improved retry code to retry only once on a single no-data | ||||
|   error in a segment. | ||||
| - Validate sector number extracted from eof mark because an | ||||
|   invalid file mark (due to ???) could cause kernel panic. | ||||
| - Split ftape-io.c into ftape-io.c and ftape-ctl.c files. | ||||
| - Corrected too high media error count after writing to | ||||
|   a bad tape. | ||||
| - Added #include <asm/segment.h> again because old kernel versions | ||||
|   need it. | ||||
| - Fixed fdc not being disabled when open failed because no tape | ||||
|   drive was found. | ||||
| - Fixed problem with soft error in sector 32 (shift operator with | ||||
|   shiftcount 32 is not defined). | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.01, 08/01/95 ===== | ||||
|  | ||||
|  | ||||
| - Removed TESTING setting from distributed Makefile. | ||||
| - Fixed `mt asf' failure: Rewind was deferred to close which | ||||
|   overruled the fsf ioctl. | ||||
| - Prevented non-interruptible commands being interrupted. | ||||
| - Added missing timeout.pause setting. | ||||
| - Maximum tape speed read from drive type information table. | ||||
|   If the information is not in the table (0) the drive will | ||||
|   determine the speed itself and put a message in the logfile. | ||||
|   This information should then be added to the table in the | ||||
|   vendors.h file (and reported to me). | ||||
| - Added call to ftape_init_drive after soft reset for those | ||||
|   (antique) drives that don't do an implicit seek_load_point | ||||
|   after a reset or power up. | ||||
| - Don't try to set data rate if reset failed. | ||||
| - Prevent update of seek variables when starting from the | ||||
|   beginning or the end of the tape. | ||||
| - Fixed wrong adjustment of overshoot in seek_forward(). | ||||
| - Added sync to Makefile (again). | ||||
| - Added code to diagnose timer problems (calibr.c). | ||||
| - Replaced time differences by timediff calls. | ||||
| - Removed reference to do_floppy from object for recent kernels. | ||||
| - Fixed wrong display of 'failing dma controller' message. | ||||
| - Removed various no longer used #include statements. | ||||
| - Added max. tape speed value to vendor-struct. | ||||
| - Changed ftape-command to check pre-conditions and wait | ||||
|   if needed. | ||||
| - Further updated qic117.h to rev G. | ||||
| - Combined command name table and restrictions table to one. | ||||
|   Extended this table with some new fields. | ||||
| - Increased timeout on Ack timer value and included code to | ||||
|   report out of spec behaviour. | ||||
| - Increased rewind timeout margin to calculated + 20%. | ||||
| - Improved data rate selection so it won't fail on some | ||||
|   older (pre standard) drives. | ||||
| - Changed initialisation code so drive will be rewound if the | ||||
|   driver is reloaded and the tape is not at bot. | ||||
| - Moved some of the flush operations from close to the ioctls. | ||||
| - Added exit code value to failing verify area message. | ||||
| - Loop until tape halted in smart-stop. | ||||
| - Fast seek handled specially if located at bot or eot. | ||||
| - Being more conservative on overshoot value. | ||||
|  | ||||
|  | ||||
| ===== Release notes for ftape-2.00, 31/12/94 ===== | ||||
|  | ||||
|   The Install-guide is completely rewritten and now also includes | ||||
| some information on how to use the driver. If you're either new | ||||
| to ftape or new to Unix tape devices make sure to read it ! | ||||
|  | ||||
|   If you own a pci system and experience problems with the | ||||
| ftape driver make sure to read the README.PCI file. It contains | ||||
| some hints on how to fix your hardware. | ||||
|  | ||||
|   For anybody who hasn't noticed: The version number of the | ||||
| driver has been incremented (The latest released version has | ||||
| been version 1.14d). | ||||
|   This has been done for two major reasons: | ||||
|  | ||||
|   o  A new (better) error recovery scheme is implemented. | ||||
|   o  Support for new drive types has been added. | ||||
|  | ||||
|   All these improvements/changes will probably include a couple | ||||
| of new (and old?) bugs. If you encounter any problems that you think | ||||
| I'm not yet aware of, feel free to send a report to <bas@vimec.nl>. | ||||
|   I recommend keeping a version of ftape-1.14d available, just | ||||
| in case ;-) | ||||
|  | ||||
|   This version should work with all kernel versions from 1.0.9 up | ||||
| to 1.1.72 (and probably earlier and later versions too). | ||||
|  | ||||
|  | ||||
| Major new features: | ||||
|  | ||||
| - Better handling of tapes with defects: When a sector repeatedly | ||||
|   (SOFT_RETRIES in ftape.h) cannot be written to or read from it is | ||||
|   marked as an hard error and gets skipped. | ||||
|   The error correction code can handle up to three of these hard | ||||
|   errors provided there are no other errors in that segment (32 Kb). | ||||
|    | ||||
| - Allows writing to tapes with defects (although the risk of loosing | ||||
|   data increases !) | ||||
|   Look for the media-defects entry printed with the statistics when | ||||
|   the tape is closed. A non-zero value here shows a bad tape. | ||||
|   [the actual count is wrong (too high), this is a known bug]. | ||||
|  | ||||
| - Use of backup header segment if first one is failing. | ||||
|  | ||||
| - Support for extended length tapes with QIC-80: both 425 and 1100 ft. | ||||
|   0.25 inch tapes are now recognized and handled. | ||||
|  | ||||
| - Support for new QIC-80 drives with 8 mm `wide' tapes (e.g. Conner | ||||
|   TSM 420). | ||||
|  | ||||
| - Support for new QIC-3010 and QIC-3020 drives (experimental) with | ||||
|   both 0.25 inch and 8 mm tapes. | ||||
|  | ||||
| Some minor features were added, a couple of small bugs were fixed and | ||||
| probably some new ones introduced ;-). | ||||
|  | ||||
| [lseek() didn't make it into this version] | ||||
|  | ||||
| Have fun, | ||||
|  | ||||
| Bas. | ||||
| ---- | ||||
|  LocalWords:  ftape MCONFIG mt VFS zftape resp sftape proc subdir MTIOCVOLINFO | ||||
|  LocalWords:  MTIOCGETSIZE BOT EOD MTBSF zft kerneld modprobe kdtime contrib TR | ||||
|  LocalWords:  MTSETBLK afio uninstall texi www EIO QIC init sft eof aka dma GB | ||||
|  LocalWords:  SIGKILL MTIOCFTCMD mmap Iomega FDC fdc io gnumt mtio fc asm inb | ||||
|  LocalWords:  outb ft qic frontend TeXinfo irq mach MODVERSIONS CONFIG html dvi | ||||
|  LocalWords:  usr doc SMP Mb Dunno FIXME vtblc perl listtape volinfo fsf MTWEOF | ||||
|  LocalWords:  amanda degaussed ComByte DoublePlay whraven njackn com MTIOC vtbl | ||||
|  LocalWords:  GETBLKSZ MAKEDEV zftape's linux dif CVS Revison cp MTREW MTOFFL | ||||
|  LocalWords:  MTFSF BSF Marcin Dalecki GCC Config cpio swapout Kai Harrekilde | ||||
|  LocalWords:  Pederson khp dolphinics Justus claus momo rwth aachen Laarhoven | ||||
| @@ -1,31 +0,0 @@ | ||||
| # | ||||
| #       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. | ||||
| # | ||||
|  | ||||
| obj-$(CONFIG_ZFT_COMPRESSOR) += zft-compressor.o | ||||
|  | ||||
| zft-compressor-objs := zftape-compress.o lzrw3.o | ||||
|  | ||||
| CFLAGS_lzrw3.o	:= -O6 -funroll-all-loops | ||||
| @@ -1,743 +0,0 @@ | ||||
| /* | ||||
|  * $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( | ||||
| 	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) | ||||
|  | ||||
| /******************************************************************************/ | ||||
|  | ||||
| /* 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.        */ | ||||
| LOCAL void compress_compress(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=NULL; | ||||
|  UBYTE **p_h2=NULL; | ||||
|    | ||||
|  /* 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) | ||||
|                {*p_h2=p_ziv-2; p_h2=NULL;} | ||||
|              *p_h1=p_ziv-1; p_h1=NULL; | ||||
|             } | ||||
|              | ||||
|           /* 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 | ||||
| } | ||||
|  | ||||
| /******************************************************************************/ | ||||
|  | ||||
| /* 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].       */ | ||||
| LOCAL void compress_decompress( 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                               */ | ||||
| /******************************************************************************/ | ||||
| @@ -1,253 +0,0 @@ | ||||
| #ifndef _LZRW3_H | ||||
| #define _LZRW3_H | ||||
| /* | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/compressor/lzrw3.h,v $ | ||||
|  * $Revision: 1.1 $ | ||||
|  * $Date: 1997/10/05 19:12:30 $ | ||||
|  * | ||||
|  *  include files for lzrw3. Only slighty modified from the original | ||||
|  *  version. Assembles the three include files compress.h, port.h and | ||||
|  *  fastcopy.h from the original lzrw3 package. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <linux/types.h> | ||||
| #include <linux/string.h> | ||||
|  | ||||
| /******************************************************************************/ | ||||
| /*                                                                            */ | ||||
| /*                                 COMPRESS.H                                 */ | ||||
| /*                                                                            */ | ||||
| /******************************************************************************/ | ||||
| /*                                                                            */ | ||||
| /* Author : Ross Williams.                                                    */ | ||||
| /* Date   : December 1989.                                                    */ | ||||
| /*                                                                            */ | ||||
| /* This header file defines the interface to a set of functions called        */ | ||||
| /* 'compress', each member of which implements a particular data compression  */ | ||||
| /* algorithm.                                                                 */ | ||||
| /*                                                                            */ | ||||
| /* Normally in C programming, for each .H file, there is a corresponding .C   */ | ||||
| /* file that implements the functions promised in the .H file.                */ | ||||
| /* Here, there are many .C files corresponding to this header file.           */ | ||||
| /* Each comforming implementation file contains a single function             */ | ||||
| /* called 'compress' that implements a single data compression                */ | ||||
| /* algorithm that conforms with the interface specified in this header file.  */ | ||||
| /* Only one algorithm can be linked in at a time in this organization.        */ | ||||
| /*                                                                            */ | ||||
| /******************************************************************************/ | ||||
| /*                                                                            */ | ||||
| /*                    DEFINITION OF FUNCTION COMPRESS                         */ | ||||
| /*                    ===============================                         */ | ||||
| /*                                                                            */ | ||||
| /* Summary of Function Compress                                               */ | ||||
| /* ----------------------------                                               */ | ||||
| /* The action that 'compress' takes depends on its first argument called      */ | ||||
| /* 'action'.  The function provides three actions:                            */ | ||||
| /*                                                                            */ | ||||
| /*    - Return information about the algorithm.                               */ | ||||
| /*    - Compress   a block of memory.                                         */ | ||||
| /*    - Decompress a block of memory.                                         */ | ||||
| /*                                                                            */ | ||||
| /* Parameters                                                                 */ | ||||
| /* ----------                                                                 */ | ||||
| /* See the formal C definition later for a description of the parameters.     */ | ||||
| /*                                                                            */ | ||||
| /* Constants                                                                  */ | ||||
| /* ---------                                                                  */ | ||||
| /* COMPRESS_OVERRUN: The constant COMPRESS_OVERRUN defines by how many bytes  */ | ||||
| /* an algorithm is allowed to expand a block during a compression operation.  */ | ||||
| /*                                                                            */ | ||||
| /* Although compression algorithms usually compress data, there will always   */ | ||||
| /* be data that a given compressor will expand (this can be proven).          */ | ||||
| /* Fortunately, the degree of expansion can be limited to a single bit, by    */ | ||||
| /* copying over the input data if the data gets bigger during compression.    */ | ||||
| /* To allow for this possibility, the first bit of a compressed               */ | ||||
| /* representation can be used as a flag indicating whether the                */ | ||||
| /* input data was copied over, or truly compressed. In practice, the first    */ | ||||
| /* byte would be used to store this bit so as to maintain byte alignment.     */ | ||||
| /*                                                                            */ | ||||
| /* Unfortunately, in general, the only way to tell if an algorithm will       */ | ||||
| /* expand a particular block of data is to run the algorithm on the data.     */ | ||||
| /* If the algorithm does not continuously monitor how many output bytes it    */ | ||||
| /* has written, it might write an output block far larger than the input      */ | ||||
| /* block before realizing that it has done so.                                */ | ||||
| /* On the other hand, continuous checks on output length are inefficient.     */ | ||||
| /*                                                                            */ | ||||
| /* To cater for all these problems, this interface definition:                */ | ||||
| /* > Allows a compression algorithm to return an output block that is up to   */ | ||||
| /*   COMPRESS_OVERRUN bytes longer than the input block.                      */ | ||||
| /* > Allows a compression algorithm to write up to COMPRESS_OVERRUN bytes     */ | ||||
| /*   more than the length of the input block to the memory of the output      */ | ||||
| /*   block regardless of the length of the output block eventually returned.  */ | ||||
| /*   This allows an algorithm to overrun the length of the input block in the */ | ||||
| /*   output block by up to COMPRESS_OVERRUN bytes between expansion checks.   */ | ||||
| /*                                                                            */ | ||||
| /* The problem does not arise for decompression.                              */ | ||||
| /*                                                                            */ | ||||
| /* Identity Action                                                            */ | ||||
| /* ---------------                                                            */ | ||||
| /* > action must be COMPRESS_ACTION_IDENTITY.                                 */ | ||||
| /* > p_dst_len must point to a longword to receive a longword address.        */ | ||||
| /* > The value of the other parameters does not matter.                       */ | ||||
| /* > After execution, the longword that p_dst_len points to will be a pointer */ | ||||
| /*   to a structure of type compress_identity.                                */ | ||||
| /*   Thus, for example, after the call, (*p_dst_len)->memory will return the  */ | ||||
| /*   number of bytes of working memory that the algorithm requires to run.    */ | ||||
| /* > The values of the identity structure returned are fixed constant         */ | ||||
| /*   attributes of the algorithm and must not vary from call to call.         */ | ||||
| /*                                                                            */ | ||||
| /* Common Requirements for Compression and Decompression Actions              */ | ||||
| /* -------------------------------------------------------------              */ | ||||
| /* > wrk_mem must point to an unused block of memory of a length specified in */ | ||||
| /*   the algorithm's identity block. The identity block can be obtained by    */ | ||||
| /*   making a separate call to compress, specifying the identity action.      */ | ||||
| /* > The INPUT BLOCK is defined to be Memory[src_addr,src_addr+src_len-1].    */ | ||||
| /* > dst_len will be used to denote *p_dst_len.                               */ | ||||
| /* > dst_len is not read by compress, only written.                           */ | ||||
| /* > The value of dst_len is defined only upon termination.                   */ | ||||
| /* > The OUTPUT BLOCK is defined to be Memory[dst_addr,dst_addr+dst_len-1].   */ | ||||
| /*                                                                            */ | ||||
| /* Compression Action                                                         */ | ||||
| /* ------------------                                                         */ | ||||
| /* > action must be COMPRESS_ACTION_COMPRESS.                                 */ | ||||
| /* > src_len must be in the range [0,COMPRESS_MAX_ORG].                       */ | ||||
| /* > The OUTPUT ZONE is defined to be                                         */ | ||||
| /*      Memory[dst_addr,dst_addr+src_len-1+COMPRESS_OVERRUN].                 */ | ||||
| /* > The function can modify any part of the output zone regardless of the    */ | ||||
| /*   final length of the output block.                                        */ | ||||
| /* > The input block and the output zone must not overlap.                    */ | ||||
| /* > dst_len will be in the range [0,src_len+COMPRESS_OVERRUN].               */ | ||||
| /* > dst_len will be in the range [0,COMPRESS_MAX_COM] (from prev fact).      */ | ||||
| /* > The output block will consist of a representation of the input block.    */ | ||||
| /*                                                                            */ | ||||
| /* Decompression Action                                                       */ | ||||
| /* --------------------                                                       */ | ||||
| /* > action must be COMPRESS_ACTION_DECOMPRESS.                               */ | ||||
| /* > The input block must be the result of an earlier compression operation.  */ | ||||
| /* > If the previous fact is true, the following facts must also be true:     */ | ||||
| /*   > src_len will be in the range [0,COMPRESS_MAX_COM].                     */ | ||||
| /*   > dst_len will be in the range [0,COMPRESS_MAX_ORG].                     */ | ||||
| /* > The input and output blocks must not overlap.                            */ | ||||
| /* > Only the output block is modified.                                       */ | ||||
| /* > Upon termination, the output block will consist of the bytes contained   */ | ||||
| /*   in the input block passed to the earlier compression operation.          */ | ||||
| /*                                                                            */ | ||||
| /******************************************************************************/ | ||||
|  | ||||
| /******************************************************************************/ | ||||
| /*                                                                            */ | ||||
| /*                                    PORT.H                                  */ | ||||
| /*                                                                            */ | ||||
| /******************************************************************************/ | ||||
| /*                                                                            */ | ||||
| /* This module contains macro definitions and types that are likely to        */ | ||||
| /* change between computers.                                                  */ | ||||
| /*                                                                            */ | ||||
| /******************************************************************************/ | ||||
|  | ||||
| #ifndef DONE_PORT       /* Only do this if not previously done.               */ | ||||
|  | ||||
|    #ifdef THINK_C | ||||
|       #define UBYTE unsigned char      /* Unsigned byte                       */ | ||||
|       #define UWORD unsigned int       /* Unsigned word (2 bytes)             */ | ||||
|       #define ULONG unsigned long      /* Unsigned word (4 bytes)             */ | ||||
|       #define BOOL  unsigned char      /* Boolean                             */ | ||||
|       #define FOPEN_BINARY_READ  "rb"  /* Mode string for binary reading.     */ | ||||
|       #define FOPEN_BINARY_WRITE "wb"  /* Mode string for binary writing.     */ | ||||
|       #define FOPEN_TEXT_APPEND  "a"   /* Mode string for text appending.     */ | ||||
|       #define REAL double              /* USed for floating point stuff.      */ | ||||
|    #endif | ||||
|    #if defined(LINUX) || defined(linux) | ||||
|       #define UBYTE __u8               /* Unsigned byte                       */ | ||||
|       #define UWORD __u16              /* Unsigned word (2 bytes)             */ | ||||
|       #define ULONG __u32              /* Unsigned word (4 bytes)             */ | ||||
|       #define LONG  __s32              /* Signed   word (4 bytes)             */ | ||||
|       #define BOOL  is not used here   /* Boolean                             */ | ||||
|       #define FOPEN_BINARY_READ  not used  /* Mode string for binary reading. */ | ||||
|       #define FOPEN_BINARY_WRITE not used  /* Mode string for binary writing. */ | ||||
|       #define FOPEN_TEXT_APPEND  not used  /* Mode string for text appending. */ | ||||
|       #define REAL not used                /* USed for floating point stuff.  */ | ||||
|       #ifndef TRUE | ||||
|       #define TRUE 1 | ||||
|       #endif | ||||
|    #endif | ||||
|  | ||||
|    #define DONE_PORT                   /* Don't do all this again.            */ | ||||
|    #define MALLOC_FAIL NULL            /* Failure status from malloc()        */ | ||||
|    #define LOCAL static                /* For non-exported routines.          */ | ||||
|    #define EXPORT                      /* Signals exported function.          */ | ||||
|    #define then                        /* Useful for aligning ifs.            */ | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /******************************************************************************/ | ||||
| /*                              End of PORT.H                                 */ | ||||
| /******************************************************************************/ | ||||
|  | ||||
| #define COMPRESS_ACTION_IDENTITY   0 | ||||
| #define COMPRESS_ACTION_COMPRESS   1 | ||||
| #define COMPRESS_ACTION_DECOMPRESS 2 | ||||
|  | ||||
| #define COMPRESS_OVERRUN 1024 | ||||
| #define COMPRESS_MAX_COM 0x70000000 | ||||
| #define COMPRESS_MAX_ORG (COMPRESS_MAX_COM-COMPRESS_OVERRUN) | ||||
|  | ||||
| #define COMPRESS_MAX_STRLEN 255 | ||||
|  | ||||
| /* The following structure provides information about the algorithm.         */ | ||||
| /* > The top bit of id must be zero. The remaining bits must be chosen by    */ | ||||
| /*   the author of the algorithm by tossing a coin 31 times.                 */ | ||||
| /* > The amount of memory requested by the algorithm is specified in bytes   */ | ||||
| /*   and must be in the range [0,0x70000000].                                */ | ||||
| /* > All strings s must be such that strlen(s)<=COMPRESS_MAX_STRLEN.         */ | ||||
| struct compress_identity | ||||
|   { | ||||
|    ULONG id;           /* Identifying number of algorithm.            */ | ||||
|    ULONG memory;       /* Number of bytes of working memory required. */ | ||||
|  | ||||
|    char  *name;        /* Name of algorithm.                          */ | ||||
|    char  *version;     /* Version number.                             */ | ||||
|    char  *date;        /* Date of release of this version.            */ | ||||
|    char  *copyright;   /* Copyright message.                          */ | ||||
|  | ||||
|    char  *author;      /* Author of algorithm.                        */ | ||||
|    char  *affiliation; /* Affiliation of author.                      */ | ||||
|    char  *vendor;      /* Where the algorithm can be obtained.        */ | ||||
|   }; | ||||
|  | ||||
| void  lzrw3_compress(        /* Single function interface to compression algorithm. */ | ||||
| UWORD     action,      /* Action to be performed.                             */ | ||||
| UBYTE   *wrk_mem,      /* Working memory temporarily given to routine to use. */ | ||||
| UBYTE   *src_adr,      /* Address of input  data.                             */ | ||||
| LONG     src_len,      /* Length  of input  data.                             */ | ||||
| UBYTE   *dst_adr,      /* Address of output data.                             */ | ||||
| void  *p_dst_len       /* Pointer to a longword where routine will write:     */ | ||||
|                        /*    If action=..IDENTITY   => Adr of id structure.   */ | ||||
|                        /*    If action=..COMPRESS   => Length of output data. */ | ||||
|                        /*    If action=..DECOMPRESS => Length of output data. */ | ||||
| ); | ||||
|  | ||||
| /******************************************************************************/ | ||||
| /*                             End of COMPRESS.H                              */ | ||||
| /******************************************************************************/ | ||||
|  | ||||
|  | ||||
| /******************************************************************************/ | ||||
| /*                                  fast_copy.h                               */ | ||||
| /******************************************************************************/ | ||||
|  | ||||
| /* This function copies a block of memory very quickly.                       */ | ||||
| /* The exact speed depends on the relative alignment of the blocks of memory. */ | ||||
| /* PRE  : 0<=src_len<=(2^32)-1 .                                              */ | ||||
| /* PRE  : Source and destination blocks must not overlap.                     */ | ||||
| /* POST : MEM[dst_adr,dst_adr+src_len-1]=MEM[src_adr,src_adr+src_len-1].      */ | ||||
| /* POST : MEM[dst_adr,dst_adr+src_len-1] is the only memory changed.          */ | ||||
|  | ||||
| #define fast_copy(src,dst,len) memcpy(dst,src,len) | ||||
|  | ||||
| /******************************************************************************/ | ||||
| /*                               End of fast_copy.h                           */ | ||||
| /******************************************************************************/ | ||||
|  | ||||
| #endif | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,83 +0,0 @@ | ||||
| #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 */ | ||||
| @@ -1,43 +0,0 @@ | ||||
| # | ||||
| #       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. | ||||
| # | ||||
|  | ||||
| obj-$(CONFIG_FTAPE) += ftape.o | ||||
|  | ||||
| ftape-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 ftape_syms.o | ||||
|  | ||||
| ifeq ($(CONFIG_FTAPE),y) | ||||
| ftape-objs += ftape-setup.o | ||||
| endif | ||||
|  | ||||
| ifndef CONFIG_FT_NO_TRACE_AT_ALL | ||||
| ftape-objs += ftape-tracing.o | ||||
| endif | ||||
|  | ||||
| ifeq ($(CONFIG_FT_PROC_FS),y) | ||||
| ftape-objs += ftape-proc.o | ||||
| endif | ||||
| @@ -1,175 +0,0 @@ | ||||
| /* | ||||
|  * | ||||
|  | ||||
|    Copyright (C) 1993,1994 Jon Tombs. | ||||
|  | ||||
|    This program is distributed in the hope that it will be useful, | ||||
|    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|    GNU General Public License for more details. | ||||
|  | ||||
|    The entire guts of this program was written by dosemu, modified to | ||||
|    record reads and writes to the ports in the 0x180-0x188 address space, | ||||
|    while running the CMS program TAPE.EXE V2.0.5 supplied with the drive. | ||||
|  | ||||
|    Modified to use an array of addresses and generally cleaned up (made | ||||
|    much shorter) 4 June 94, dosemu isn't that good at writing short code it | ||||
|    would seem :-). Made independent of 0x180, but I doubt it will work | ||||
|    at any other address. | ||||
|  | ||||
|    Modified for distribution with ftape source. 21 June 94, SJL. | ||||
|  | ||||
|    Modifications on 20 October 95, by Daniel Cohen (catman@wpi.edu): | ||||
|    Modified to support different DMA, IRQ, and IO Ports.  Borland's | ||||
|    Turbo Debugger in virtual 8086 mode (TD386.EXE with hardware breakpoints | ||||
|    provided by the TDH386.SYS Device Driver) was used on the CMS program | ||||
|    TAPE V4.0.5.  I set breakpoints on I/O to ports 0x180-0x187.  Note that | ||||
|    CMS's program will not successfully configure the tape drive if you set | ||||
|    breakpoints on IO Reads, but you can set them on IO Writes without problems. | ||||
|    Known problems: | ||||
|    - You can not use DMA Channels 5 or 7. | ||||
|  | ||||
|    Modification on 29 January 96, by Daniel Cohen (catman@wpi.edu): | ||||
|    Modified to only accept IRQs 3 - 7, or 9.  Since we can only send a 3 bit | ||||
|    number representing the IRQ to the card, special handling is required when | ||||
|    IRQ 9 is selected.  IRQ 2 and 9 are the same, and we should request IRQ 9 | ||||
|    from the kernel while telling the card to use IRQ 2.  Thanks to Greg | ||||
|    Crider (gcrider@iclnet.org) for finding and locating this bug, as well as | ||||
|    testing the patch. | ||||
|  | ||||
|    Modification on 11 December 96, by Claus Heine (claus@momo.math.rwth-aachen.de): | ||||
|    Modified a little to use variahle ft_fdc_base, ft_fdc_irq, ft_fdc_dma  | ||||
|    instead of preprocessor symbols. Thus we can compile this into the module | ||||
|    or kernel and let the user specify the options as command line arguments. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fc-10.c,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:18:04 $ | ||||
|  * | ||||
|  *      This file contains code for the CMS FC-10/FC-20 card. | ||||
|  */ | ||||
|  | ||||
| #include <asm/io.h> | ||||
| #include <linux/ftape.h> | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
| #include "../lowlevel/fdc-io.h" | ||||
| #include "../lowlevel/fc-10.h" | ||||
|  | ||||
| static __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 | ||||
| }; | ||||
|  | ||||
| static __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; | ||||
| } | ||||
| @@ -1,39 +0,0 @@ | ||||
| #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 | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,252 +0,0 @@ | ||||
| #ifndef _FDC_IO_H | ||||
| #define _FDC_IO_H | ||||
|  | ||||
| /* | ||||
|  *    Copyright (C) 1993-1996 Bas Laarhoven, | ||||
|  *              (C) 1996-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-io.h,v $ | ||||
|  * $Revision: 1.3 $ | ||||
|  * $Date: 1997/10/05 19:18:06 $ | ||||
|  * | ||||
|  *      This file contains the declarations for the low level | ||||
|  *      functions that communicate with the floppy disk controller, | ||||
|  *      for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for | ||||
|  *      Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/fdreg.h> | ||||
|  | ||||
| #include "../lowlevel/ftape-bsm.h" | ||||
|  | ||||
| #define FDC_SK_BIT      (0x20) | ||||
| #define FDC_MT_BIT      (0x80) | ||||
|  | ||||
| #define FDC_READ        (FD_READ & ~(FDC_SK_BIT | FDC_MT_BIT)) | ||||
| #define FDC_WRITE       (FD_WRITE & ~FDC_MT_BIT) | ||||
| #define FDC_READ_DELETED  (0x4c) | ||||
| #define FDC_WRITE_DELETED (0x49) | ||||
| #define FDC_VERIFY        (0x56) | ||||
| #define FDC_READID      (0x4a) | ||||
| #define FDC_SENSED      (0x04) | ||||
| #define FDC_SENSEI      (FD_SENSEI) | ||||
| #define FDC_FORMAT      (FD_FORMAT) | ||||
| #define FDC_RECAL       (FD_RECALIBRATE) | ||||
| #define FDC_SEEK        (FD_SEEK) | ||||
| #define FDC_SPECIFY     (FD_SPECIFY) | ||||
| #define FDC_RECALIBR    (FD_RECALIBRATE) | ||||
| #define FDC_VERSION     (FD_VERSION) | ||||
| #define FDC_PERPEND     (FD_PERPENDICULAR) | ||||
| #define FDC_DUMPREGS    (FD_DUMPREGS) | ||||
| #define FDC_LOCK        (FD_LOCK) | ||||
| #define FDC_UNLOCK      (FD_UNLOCK) | ||||
| #define FDC_CONFIGURE   (FD_CONFIGURE) | ||||
| #define FDC_DRIVE_SPEC  (0x8e)	/* i82078 has this (any others?) */ | ||||
| #define FDC_PARTID      (0x18)	/* i82078 has this */ | ||||
| #define FDC_SAVE        (0x2e)	/* i82078 has this (any others?) */ | ||||
| #define FDC_RESTORE     (0x4e)	/* i82078 has this (any others?) */ | ||||
|  | ||||
| #define FDC_STATUS_MASK (STATUS_BUSY | STATUS_DMA | STATUS_DIR | STATUS_READY) | ||||
| #define FDC_DATA_READY  (STATUS_READY) | ||||
| #define FDC_DATA_OUTPUT (STATUS_DIR) | ||||
| #define FDC_DATA_READY_MASK (STATUS_READY | STATUS_DIR) | ||||
| #define FDC_DATA_OUT_READY  (STATUS_READY | STATUS_DIR) | ||||
| #define FDC_DATA_IN_READY   (STATUS_READY) | ||||
| #define FDC_BUSY        (STATUS_BUSY) | ||||
| #define FDC_CLK48_BIT   (0x80) | ||||
| #define FDC_SEL3V_BIT   (0x40) | ||||
|  | ||||
| #define ST0_INT_MASK    (ST0_INTR) | ||||
| #define FDC_INT_NORMAL  (ST0_INTR & 0x00) | ||||
| #define FDC_INT_ABNORMAL (ST0_INTR & 0x40) | ||||
| #define FDC_INT_INVALID (ST0_INTR & 0x80) | ||||
| #define FDC_INT_READYCH (ST0_INTR & 0xC0) | ||||
| #define ST0_SEEK_END    (ST0_SE) | ||||
| #define ST3_TRACK_0     (ST3_TZ) | ||||
|  | ||||
| #define FDC_RESET_NOT   (0x04) | ||||
| #define FDC_DMA_MODE    (0x08) | ||||
| #define FDC_MOTOR_0     (0x10) | ||||
| #define FDC_MOTOR_1     (0x20) | ||||
|  | ||||
| typedef struct { | ||||
| 	void (**hook) (void);	/* our wedge into the isr */ | ||||
| 	enum { | ||||
| 		no_fdc, i8272, i82077, i82077AA, fc10, | ||||
| 		i82078, i82078_1 | ||||
| 	} type;			/* FDC type */ | ||||
| 	unsigned int irq; /* FDC irq nr */ | ||||
| 	unsigned int dma; /* FDC dma channel nr */ | ||||
| 	__u16 sra;	  /* Status register A (PS/2 only) */ | ||||
| 	__u16 srb;	  /* Status register B (PS/2 only) */ | ||||
| 	__u16 dor;	  /* Digital output register */ | ||||
| 	__u16 tdr;	  /* Tape Drive Register (82077SL-1 & | ||||
| 			     82078 only) */ | ||||
| 	__u16 msr;	  /* Main Status Register */ | ||||
| 	__u16 dsr;	  /* Datarate Select Register (8207x only) */ | ||||
| 	__u16 fifo;	  /* Data register / Fifo on 8207x */ | ||||
| 	__u16 dir;	  /* Digital Input Register */ | ||||
| 	__u16 ccr;	  /* Configuration Control Register */ | ||||
| 	__u16 dor2;	  /* Alternate dor on MACH-2 controller, | ||||
| 			     also used with FC-10, meaning unknown */ | ||||
| } fdc_config_info; | ||||
|  | ||||
| typedef enum { | ||||
| 	fdc_data_rate_250  = 2, | ||||
| 	fdc_data_rate_300  = 1,	/* any fdc in default configuration */ | ||||
| 	fdc_data_rate_500  = 0, | ||||
| 	fdc_data_rate_1000 = 3, | ||||
| 	fdc_data_rate_2000 = 1,	/* i82078-1: when using Data Rate Table #2 */ | ||||
| } fdc_data_rate_type; | ||||
|  | ||||
| typedef enum { | ||||
| 	fdc_idle          = 0, | ||||
| 	fdc_reading_data  = FDC_READ, | ||||
| 	fdc_seeking       = FDC_SEEK, | ||||
| 	fdc_writing_data  = FDC_WRITE, | ||||
| 	fdc_deleting      = FDC_WRITE_DELETED, | ||||
| 	fdc_reading_id    = FDC_READID, | ||||
| 	fdc_recalibrating = FDC_RECAL, | ||||
| 	fdc_formatting    = FDC_FORMAT, | ||||
| 	fdc_verifying     = FDC_VERIFY | ||||
| } fdc_mode_enum; | ||||
|  | ||||
| typedef enum { | ||||
| 	waiting = 0, | ||||
| 	reading, | ||||
| 	writing, | ||||
| 	formatting, | ||||
| 	verifying, | ||||
| 	deleting, | ||||
| 	done, | ||||
| 	error, | ||||
| 	mmapped, | ||||
| } buffer_state_enum; | ||||
|  | ||||
| typedef struct { | ||||
| 	__u8 *address; | ||||
| 	volatile buffer_state_enum status; | ||||
| 	volatile __u8 *ptr; | ||||
| 	volatile unsigned int bytes; | ||||
| 	volatile unsigned int segment_id; | ||||
|  | ||||
| 	/* bitmap for remainder of segment not yet handled. | ||||
| 	 * one bit set for each bad sector that must be skipped. | ||||
| 	 */ | ||||
| 	volatile SectorMap bad_sector_map; | ||||
|  | ||||
| 	/* bitmap with bad data blocks in data buffer. | ||||
| 	 * the errors in this map may be retried. | ||||
| 	 */ | ||||
| 	volatile SectorMap soft_error_map; | ||||
|  | ||||
| 	/* bitmap with bad data blocks in data buffer | ||||
| 	 * the errors in this map may not be retried. | ||||
| 	 */ | ||||
| 	volatile SectorMap hard_error_map; | ||||
|  | ||||
| 	/* retry counter for soft errors. | ||||
| 	 */ | ||||
| 	volatile int retry; | ||||
|  | ||||
| 	/* sectors to skip on retry ??? | ||||
| 	 */ | ||||
| 	volatile unsigned int skip; | ||||
|  | ||||
| 	/* nr of data blocks in data buffer | ||||
| 	 */ | ||||
| 	volatile unsigned int data_offset; | ||||
|  | ||||
| 	/* offset in segment for first sector to be handled. | ||||
| 	 */ | ||||
| 	volatile unsigned int sector_offset; | ||||
|  | ||||
| 	/* size of cluster of good sectors to be handled. | ||||
| 	 */ | ||||
| 	volatile unsigned int sector_count; | ||||
|  | ||||
| 	/* size of remaining part of segment to be handled. | ||||
| 	 */ | ||||
| 	volatile unsigned int remaining; | ||||
|  | ||||
| 	/* points to next segment (contiguous) to be handled, | ||||
| 	 * or is zero if no read-ahead is allowed. | ||||
| 	 */ | ||||
| 	volatile unsigned int next_segment; | ||||
|  | ||||
| 	/* flag being set if deleted data was read. | ||||
| 	 */ | ||||
| 	volatile int deleted; | ||||
|  | ||||
| 	/* floppy coordinates of first sector in segment */ | ||||
| 	volatile __u8 head; | ||||
| 	volatile __u8 cyl; | ||||
| 	volatile __u8 sect; | ||||
|  | ||||
| 	/* gap to use when formatting */ | ||||
| 	__u8 gap3; | ||||
| 	/* flag set when buffer is mmaped */ | ||||
| 	int mmapped; | ||||
| } buffer_struct; | ||||
|  | ||||
| /* | ||||
|  *      fdc-io.c defined public variables | ||||
|  */ | ||||
| extern volatile fdc_mode_enum fdc_mode; | ||||
| extern int fdc_setup_error;	/* outdated ??? */ | ||||
| extern wait_queue_head_t ftape_wait_intr; | ||||
| 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_interrupt_wait(unsigned int time); | ||||
| 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 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 | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,55 +0,0 @@ | ||||
| #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 | ||||
| @@ -1,491 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1994-1996 Bas Laarhoven, | ||||
|  *                (C) 1996-1997 Claus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.c,v $ | ||||
|  * $Revision: 1.3 $ | ||||
|  * $Date: 1997/10/05 19:15:15 $ | ||||
|  * | ||||
|  *      This file contains the bad-sector map handling code for | ||||
|  *      the QIC-117 floppy tape driver for Linux. | ||||
|  *      QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented. | ||||
|  */ | ||||
|  | ||||
| #include <linux/string.h> | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
| #include "../lowlevel/ftape-bsm.h" | ||||
| #include "../lowlevel/ftape-ctl.h" | ||||
| #include "../lowlevel/ftape-rw.h" | ||||
|  | ||||
| /*      Global vars. | ||||
|  */ | ||||
|  | ||||
| /*      Local vars. | ||||
|  */ | ||||
| static __u8 *bad_sector_map; | ||||
| static SectorCount *bsm_hash_ptr;  | ||||
|  | ||||
| typedef enum { | ||||
| 	forward, backward | ||||
| } mode_type; | ||||
|  | ||||
| #if 0 | ||||
| static void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map); | ||||
| #endif | ||||
|  | ||||
| #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; | ||||
| 		__u16 *ptr16; | ||||
|  | ||||
| 		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 | ||||
| 		 */ | ||||
| 		ptr16 = (__u16*)ptr; | ||||
| 		while ((sector = get_unaligned(ptr16++)) != 0) { | ||||
| 			TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d", | ||||
| 			      sector, get_unaligned(ptr16++)); | ||||
| 		} | ||||
| 	} 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; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| #if 0 | ||||
| static 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; | ||||
| } | ||||
| #endif  /*  0  */ | ||||
|  | ||||
| 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; | ||||
|  | ||||
| 		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; | ||||
| } | ||||
| @@ -1,66 +0,0 @@ | ||||
| #ifndef _FTAPE_BSM_H | ||||
| #define _FTAPE_BSM_H | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 1994-1996 Bas Laarhoven, | ||||
|  *           (C) 1996-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.h,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:18:07 $ | ||||
|  * | ||||
|  *      This file contains definitions for the bad sector map handling | ||||
|  *      routines for the QIC-117 floppy-tape driver for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include <linux/ftape-header-segment.h> | ||||
|  | ||||
| #define EMPTY_SEGMENT           (0xffffffff) | ||||
| #define FAKE_SEGMENT            (0xfffffffe) | ||||
|  | ||||
| /*  maximum (format code 4) bad sector map size (bytes). | ||||
|  */ | ||||
| #define BAD_SECTOR_MAP_SIZE     (29 * SECTOR_SIZE - 256) | ||||
|  | ||||
| /*  format code 4 bad sector entry, ftape uses this | ||||
|  *  internally for all format codes | ||||
|  */ | ||||
| typedef __u32 SectorMap; | ||||
| /*  variable and 1100 ft bad sector map entry. These three bytes represent | ||||
|  *  a single sector address measured from BOT.  | ||||
|  */ | ||||
| typedef struct NewSectorMap {           | ||||
| 	__u8 bytes[3]; | ||||
| } SectorCount; | ||||
|  | ||||
|  | ||||
| /* | ||||
|  *      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 __u8 *ftape_find_end_of_bsm_list(__u8 * address); | ||||
| extern void ftape_init_bsm(void); | ||||
|  | ||||
| #endif | ||||
| @@ -1,130 +0,0 @@ | ||||
| /* | ||||
|  *      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 <linux/slab.h> | ||||
| #include <linux/mm.h> | ||||
| #include <linux/mman.h> | ||||
| #include <asm/dma.h> | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include "../lowlevel/ftape-rw.h" | ||||
| #include "../lowlevel/ftape-read.h" | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
| #include "../lowlevel/ftape-buffer.h" | ||||
|  | ||||
| /*  DMA'able memory allocation stuff. | ||||
|  */ | ||||
|  | ||||
| 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) { | ||||
| 		struct page *page; | ||||
|  | ||||
| 		for (page = virt_to_page(addr); page < virt_to_page(addr+size); page++) | ||||
| 			SetPageReserved(page); | ||||
| 	} | ||||
| 	return (void *)addr; | ||||
| } | ||||
|  | ||||
| static inline void dmafree(void *addr, size_t size) | ||||
| { | ||||
| 	if (size > 0) { | ||||
| 		struct page *page; | ||||
|  | ||||
| 		for (page = virt_to_page((unsigned long)addr); | ||||
| 		     page < virt_to_page((unsigned long)addr+size); page++) | ||||
| 			ClearPageReserved(page); | ||||
| 		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; | ||||
| } | ||||
| @@ -1,32 +0,0 @@ | ||||
| #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 | ||||
| @@ -1,275 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1993-1996 Bas Laarhoven. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-calibr.c,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:18:08 $ | ||||
|  * | ||||
|  *      GP calibration routine for processor speed dependent | ||||
|  *      functions. | ||||
|  */ | ||||
|  | ||||
| #include <linux/errno.h> | ||||
| #include <linux/jiffies.h> | ||||
| #include <asm/system.h> | ||||
| #include <asm/io.h> | ||||
| #if defined(__alpha__) | ||||
| # include <asm/hwrpb.h> | ||||
| #elif defined(__x86_64__) | ||||
| # include <asm/msr.h> | ||||
| # include <asm/timex.h> | ||||
| #elif defined(__i386__) | ||||
| # include <linux/timex.h> | ||||
| #endif | ||||
| #include <linux/ftape.h> | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
| #include "../lowlevel/ftape-calibr.h" | ||||
| #include "../lowlevel/fdc-io.h" | ||||
|  | ||||
| #undef DEBUG | ||||
|  | ||||
| #if !defined(__alpha__) && !defined(__i386__) && !defined(__x86_64__) | ||||
| # error Ftape is not implemented for this architecture! | ||||
| #endif | ||||
|  | ||||
| #if defined(__alpha__) || defined(__x86_64__) | ||||
| static unsigned long ps_per_cycle = 0; | ||||
| #endif | ||||
|  | ||||
| static spinlock_t calibr_lock; | ||||
|  | ||||
| /* | ||||
|  * 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 it's 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(__x86_64__) | ||||
| 	unsigned long r; | ||||
| 	rdtscl(r); | ||||
| 	return r; | ||||
| #elif defined(__i386__) | ||||
|  | ||||
| /* | ||||
|  * Note that there is some time between counter underflowing and jiffies | ||||
|  * increasing, so the code below won't always give correct output. | ||||
|  * -Vojtech | ||||
|  */ | ||||
|  | ||||
| 	unsigned long flags; | ||||
| 	__u16 lo; | ||||
| 	__u16 hi; | ||||
|  | ||||
| 	spin_lock_irqsave(&calibr_lock, flags); | ||||
| 	outb_p(0x00, 0x43);	/* latch the count ASAP */ | ||||
| 	lo = inb_p(0x40);	/* read the latched count */ | ||||
| 	lo |= inb(0x40) << 8; | ||||
| 	hi = jiffies; | ||||
| 	spin_unlock_irqrestore(&calibr_lock, flags); | ||||
| 	return ((hi + 1) * (unsigned int) LATCH) - lo;  /* downcounter ! */ | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static unsigned int short_ftape_timestamp(void) | ||||
| { | ||||
| #if defined(__alpha__) || defined(__x86_64__) | ||||
| 	return ftape_timestamp(); | ||||
| #elif defined(__i386__) | ||||
| 	unsigned int count; | ||||
|  	unsigned long flags; | ||||
|   | ||||
| 	spin_lock_irqsave(&calibr_lock, flags); | ||||
|  	outb_p(0x00, 0x43);	/* latch the count ASAP */ | ||||
| 	count = inb_p(0x40);	/* read the latched count */ | ||||
| 	count |= inb(0x40) << 8; | ||||
| 	spin_unlock_irqrestore(&calibr_lock, flags); | ||||
| 	return (LATCH - count);	/* normal: downcounter */ | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static unsigned int diff(unsigned int t0, unsigned int t1) | ||||
| { | ||||
| #if defined(__alpha__) || defined(__x86_64__) | ||||
| 	return (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__) || defined(__x86_64__) | ||||
| 	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); | ||||
|  | ||||
| 	spin_lock_irqsave(&calibr_lock, flags); | ||||
| 	t0 = short_ftape_timestamp(); | ||||
| 	for (i = 0; i < 1000; ++i) { | ||||
| 		status = inb(fdc.msr); | ||||
| 	} | ||||
| 	t1 = short_ftape_timestamp(); | ||||
| 	spin_unlock_irqrestore(&calibr_lock, flags); | ||||
| 	TRACE(ft_t_info, "inb() duration: %d nsec", ftape_timediff(t0, t1)); | ||||
| 	TRACE_EXIT; | ||||
| } | ||||
|  | ||||
| static void init_clock(void) | ||||
| { | ||||
| 	TRACE_FUN(ft_t_any); | ||||
|  | ||||
| #if defined(__x86_64__) | ||||
| 	ps_per_cycle = 1000000000UL / cpu_khz; | ||||
| #elif defined(__alpha__) | ||||
| 	extern struct hwrpb_struct *hwrpb; | ||||
| 	ps_per_cycle = (1000*1000*1000*1000UL) / hwrpb->cycle_freq; | ||||
| #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 */ | ||||
| 		spin_lock_irqsave(&calibr_lock, flags); | ||||
| 		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); | ||||
| 		spin_unlock_irqrestore(&calibr_lock, 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__) || defined(__x86_64__) | ||||
| 		/* | ||||
| 		 * 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; | ||||
| } | ||||
| @@ -1,37 +0,0 @@ | ||||
| #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 */ | ||||
| @@ -1,896 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1993-1996 Bas Laarhoven, | ||||
|  *                    1996-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ctl.c,v $ | ||||
|  * $Revision: 1.4 $ | ||||
|  * $Date: 1997/11/11 14:37:44 $ | ||||
|  * | ||||
|  *      This file contains the non-read/write ftape functions for the | ||||
|  *      QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/errno.h> | ||||
| #include <linux/mm.h> | ||||
| #include <linux/mman.h> | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include <linux/qic117.h> | ||||
| #include <asm/uaccess.h> | ||||
| #include <asm/io.h> | ||||
|  | ||||
| /* ease porting between pre-2.4.x and later kernels */ | ||||
| #define vma_get_pgoff(v)      ((v)->vm_pgoff) | ||||
|  | ||||
| #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 | ||||
| } | ||||
|  | ||||
| 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; | ||||
| } | ||||
|  | ||||
| static 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; | ||||
| } | ||||
|  | ||||
| static 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; | ||||
| } | ||||
|  | ||||
| static 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; | ||||
| } | ||||
|  | ||||
| static 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; | ||||
| } | ||||
|  | ||||
| static 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->vm_flags & (VM_READ|VM_WRITE))) { | ||||
| 		TRACE_ABORT(-EINVAL, ft_t_err, "Undefined mmap() access"); | ||||
| 	} | ||||
| 	if (vma_get_pgoff(vma) != 0) { | ||||
| 		TRACE_ABORT(-EINVAL, ft_t_err, "page offset must be 0"); | ||||
| 	} | ||||
| 	if ((vma->vm_end - vma->vm_start) % FT_BUFF_SIZE != 0) { | ||||
| 		TRACE_ABORT(-EINVAL, ft_t_err, | ||||
| 			    "size = %ld, should be a multiple of %d", | ||||
| 			    vma->vm_end - vma->vm_start, | ||||
| 			    FT_BUFF_SIZE); | ||||
| 	} | ||||
| 	num_buffers = (vma->vm_end - vma->vm_start) / FT_BUFF_SIZE; | ||||
| 	if (num_buffers > ft_nr_buffers) { | ||||
| 		TRACE_ABORT(-EINVAL, | ||||
| 			    ft_t_err, "size = %ld, should be less than %d", | ||||
| 			    vma->vm_end - vma->vm_start, | ||||
| 			    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++) { | ||||
| 		unsigned long pfn; | ||||
|  | ||||
| 		pfn = virt_to_phys(ft_buffer[i]->address) >> PAGE_SHIFT; | ||||
| 		TRACE_CATCH(remap_pfn_range(vma, vma->vm_start + | ||||
| 					     i * FT_BUFF_SIZE, | ||||
| 					     pfn, | ||||
| 					     FT_BUFF_SIZE, | ||||
| 					     vma->vm_page_prot), | ||||
| 			    _res = -EAGAIN); | ||||
| 		TRACE(ft_t_noise, "remapped dma buffer @ %p to location @ %p", | ||||
| 		      ft_buffer[i]->address, | ||||
| 		      (void *)(vma->vm_start + 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 (sigtestsetmask(¤t->pending.signal, _DONT_BLOCK) &&  | ||||
| 	    !(sigtestsetmask(¤t->pending.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; | ||||
| } | ||||
| @@ -1,162 +0,0 @@ | ||||
| #ifndef _FTAPE_CTL_H | ||||
| #define _FTAPE_CTL_H | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 1993-1996 Bas Laarhoven, | ||||
|  *           (C) 1996-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ctl.h,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:18:09 $ | ||||
|  * | ||||
|  *      This file contains the non-standard IOCTL related definitions | ||||
|  *      for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for | ||||
|  *      Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/ioctl.h> | ||||
| #include <linux/mtio.h> | ||||
| #include <linux/ftape-vendors.h> | ||||
|  | ||||
| #include "../lowlevel/ftape-rw.h" | ||||
| #include <linux/ftape-header-segment.h> | ||||
|  | ||||
| typedef struct { | ||||
| 	int used;		/* any reading or writing done */ | ||||
| 	/* isr statistics */ | ||||
| 	unsigned int id_am_errors;	/* id address mark not found */ | ||||
| 	unsigned int id_crc_errors;	/* crc error in id address mark */ | ||||
| 	unsigned int data_am_errors;	/* data address mark not found */ | ||||
| 	unsigned int data_crc_errors;	/* crc error in data field */ | ||||
| 	unsigned int overrun_errors;	/* fdc access timing problem */ | ||||
| 	unsigned int no_data_errors;	/* sector not found */ | ||||
| 	unsigned int retries;	/* number of tape retries */ | ||||
| 	/* ecc statistics */ | ||||
| 	unsigned int crc_errors;	/* crc error in data */ | ||||
| 	unsigned int crc_failures;	/* bad data without crc error */ | ||||
| 	unsigned int ecc_failures;	/* failed to correct */ | ||||
| 	unsigned int corrected;	/* total sectors corrected */ | ||||
| 	/* general statistics */ | ||||
| 	unsigned int rewinds;	/* number of tape rewinds */ | ||||
| 	unsigned int defects;	/* bad sectors due to media defects */ | ||||
| } history_record; | ||||
|  | ||||
| /* this structure contains * ALL * information that we want | ||||
|  * our child modules to know about, but don't want them to | ||||
|  * modify.  | ||||
|  */ | ||||
| typedef struct { | ||||
| 	/*  vendor information */ | ||||
| 	vendor_struct fti_drive_type; | ||||
| 	/*  data rates */ | ||||
| 	unsigned int fti_used_data_rate; | ||||
| 	unsigned int fti_drive_max_rate; | ||||
| 	unsigned int fti_fdc_max_rate; | ||||
| 	/*  drive selection, either FTAPE_SEL_A/B/C/D */ | ||||
| 	int fti_drive_sel;       | ||||
| 	/*  flags set after decode the drive and tape status   */ | ||||
| 	unsigned int fti_formatted      :1; | ||||
| 	unsigned int fti_no_tape        :1; | ||||
| 	unsigned int fti_write_protected:1; | ||||
| 	unsigned int fti_new_tape       :1; | ||||
| 	/*  values of last queried drive/tape status and error */ | ||||
| 	ft_drive_error  fti_last_error; | ||||
| 	ft_drive_status fti_last_status; | ||||
| 	/*  cartridge geometry */ | ||||
| 	unsigned int fti_tracks_per_tape; | ||||
| 	unsigned int fti_segments_per_track; | ||||
| 	/*  location of header segments, etc. */ | ||||
| 	int fti_used_header_segment; | ||||
| 	int fti_header_segment_1; | ||||
| 	int fti_header_segment_2; | ||||
| 	int fti_first_data_segment; | ||||
| 	int fti_last_data_segment; | ||||
| 	/*  the format code as stored in the header segment  */ | ||||
| 	ft_format_type  fti_format_code; | ||||
| 	/*  the following is the sole reason for the ftape_set_status() call */ | ||||
| 	unsigned int fti_qic_std; | ||||
| 	/*  is tape running? */ | ||||
| 	volatile enum runner_status_enum fti_runner_status; | ||||
| 	/*  is tape reading/writing/verifying/formatting/deleting */ | ||||
| 	buffer_state_enum fti_state; | ||||
| 	/*  flags fatal hardware error */ | ||||
| 	unsigned int fti_failure:1; | ||||
| 	/*  history record */ | ||||
| 	history_record fti_history; | ||||
| } ftape_info; | ||||
|  | ||||
| /* vendor information */ | ||||
| #define ft_drive_type          ftape_status.fti_drive_type | ||||
| /*  data rates */ | ||||
| #define ft_data_rate           ftape_status.fti_used_data_rate | ||||
| #define ft_drive_max_rate      ftape_status.fti_drive_max_rate | ||||
| #define ft_fdc_max_rate        ftape_status.fti_fdc_max_rate | ||||
| /*  drive selection, either FTAPE_SEL_A/B/C/D */ | ||||
| #define ft_drive_sel           ftape_status.fti_drive_sel | ||||
| /*  flags set after decode the drive and tape status   */ | ||||
| #define ft_formatted           ftape_status.fti_formatted | ||||
| #define ft_no_tape             ftape_status.fti_no_tape | ||||
| #define ft_write_protected     ftape_status.fti_write_protected | ||||
| #define ft_new_tape            ftape_status.fti_new_tape | ||||
| /*  values of last queried drive/tape status and error */ | ||||
| #define ft_last_error          ftape_status.fti_last_error | ||||
| #define ft_last_status         ftape_status.fti_last_status | ||||
| /*  cartridge geometry */ | ||||
| #define ft_tracks_per_tape     ftape_status.fti_tracks_per_tape | ||||
| #define ft_segments_per_track  ftape_status.fti_segments_per_track | ||||
| /*  the format code as stored in the header segment  */ | ||||
| #define ft_format_code         ftape_status.fti_format_code | ||||
| /*  the qic status as returned by report drive configuration */ | ||||
| #define ft_qic_std             ftape_status.fti_qic_std | ||||
| #define ft_used_header_segment ftape_status.fti_used_header_segment | ||||
| #define ft_header_segment_1    ftape_status.fti_header_segment_1 | ||||
| #define ft_header_segment_2    ftape_status.fti_header_segment_2 | ||||
| #define ft_first_data_segment  ftape_status.fti_first_data_segment | ||||
| #define ft_last_data_segment   ftape_status.fti_last_data_segment | ||||
| /*  is tape running? */ | ||||
| #define ft_runner_status       ftape_status.fti_runner_status | ||||
| /*  is tape reading/writing/verifying/formatting/deleting */ | ||||
| #define ft_driver_state        ftape_status.fti_state | ||||
| /*  flags fatal hardware error */ | ||||
| #define ft_failure             ftape_status.fti_failure | ||||
| /*  history record */ | ||||
| #define ft_history             ftape_status.fti_history | ||||
|  | ||||
| /* | ||||
|  *      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 const ftape_info *ftape_get_status(void); | ||||
| #endif | ||||
| @@ -1,853 +0,0 @@ | ||||
| /* | ||||
|  * | ||||
|  *      Copyright (c) 1993 Ning and David Mosberger. | ||||
|   | ||||
|  This is based on code originally written by Bas Laarhoven (bas@vimec.nl) | ||||
|  and David L. Brown, Jr., and incorporates improvements suggested by | ||||
|  Kai Harrekilde-Petersen. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or | ||||
|  modify it under the terms of the GNU General Public License as | ||||
|  published by the Free Software Foundation; either version 2, or (at | ||||
|  your option) any later version. | ||||
|   | ||||
|  This program is distributed in the hope that it will be useful, but | ||||
|  WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  General Public License for more details. | ||||
|   | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, | ||||
|  USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ecc.c,v $ | ||||
|  * $Revision: 1.3 $ | ||||
|  * $Date: 1997/10/05 19:18:10 $ | ||||
|  * | ||||
|  *      This file contains the Reed-Solomon error correction code  | ||||
|  *      for the QIC-40/80 floppy-tape driver for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
|  | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
| #include "../lowlevel/ftape-ecc.h" | ||||
|  | ||||
| /* Machines that are big-endian should define macro BIG_ENDIAN. | ||||
|  * Unfortunately, there doesn't appear to be a standard include file | ||||
|  * that works for all OSs. | ||||
|  */ | ||||
|  | ||||
| #if defined(__sparc__) || defined(__hppa) | ||||
| #define BIG_ENDIAN | ||||
| #endif				/* __sparc__ || __hppa */ | ||||
|  | ||||
| #if defined(__mips__) | ||||
| #error Find a smart way to determine the Endianness of the MIPS CPU | ||||
| #endif | ||||
|  | ||||
| /* Notice: to minimize the potential for confusion, we use r to | ||||
|  *         denote the independent variable of the polynomials in the | ||||
|  *         Galois Field GF(2^8).  We reserve x for polynomials that | ||||
|  *         that have coefficients in GF(2^8). | ||||
|  *          | ||||
|  * The Galois Field in which coefficient arithmetic is performed are | ||||
|  * the polynomials over Z_2 (i.e., 0 and 1) modulo the irreducible | ||||
|  * polynomial f(r), where f(r)=r^8 + r^7 + r^2 + r + 1.  A polynomial | ||||
|  * is represented as a byte with the MSB as the coefficient of r^7 and | ||||
|  * the LSB as the coefficient of r^0.  For example, the binary | ||||
|  * representation of f(x) is 0x187 (of course, this doesn't fit into 8 | ||||
|  * bits).  In this field, the polynomial r is a primitive element. | ||||
|  * That is, r^i with i in 0,...,255 enumerates all elements in the | ||||
|  * field. | ||||
|  * | ||||
|  * The generator polynomial for the QIC-80 ECC is | ||||
|  * | ||||
|  *      g(x) = x^3 + r^105*x^2 + r^105*x + 1 | ||||
|  * | ||||
|  * which can be factored into: | ||||
|  * | ||||
|  *      g(x) = (x-r^-1)(x-r^0)(x-r^1) | ||||
|  * | ||||
|  * the byte representation of the coefficients are: | ||||
|  * | ||||
|  *      r^105 = 0xc0 | ||||
|  *      r^-1  = 0xc3 | ||||
|  *      r^0   = 0x01 | ||||
|  *      r^1   = 0x02 | ||||
|  * | ||||
|  * Notice that r^-1 = r^254 as exponent arithmetic is performed | ||||
|  * modulo 2^8-1 = 255. | ||||
|  * | ||||
|  * For more information on Galois Fields and Reed-Solomon codes, refer | ||||
|  * to any good book.  I found _An Introduction to Error Correcting | ||||
|  * Codes with Applications_ by S. A. Vanstone and P. C. van Oorschot | ||||
|  * to be a good introduction into the former.  _CODING THEORY: The | ||||
|  * Essentials_ I found very useful for its concise description of | ||||
|  * Reed-Solomon encoding/decoding. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| typedef __u8 Matrix[3][3]; | ||||
|  | ||||
| /* | ||||
|  * gfpow[] is defined such that gfpow[i] returns r^i if | ||||
|  * i is in the range [0..255]. | ||||
|  */ | ||||
| static const __u8 gfpow[] = | ||||
| { | ||||
| 	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, | ||||
| 	0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4, | ||||
| 	0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb, | ||||
| 	0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd, | ||||
| 	0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31, | ||||
| 	0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67, | ||||
| 	0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc, | ||||
| 	0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b, | ||||
| 	0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4, | ||||
| 	0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26, | ||||
| 	0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21, | ||||
| 	0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba, | ||||
| 	0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30, | ||||
| 	0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, | ||||
| 	0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3, | ||||
| 	0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a, | ||||
| 	0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9, | ||||
| 	0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44, | ||||
| 	0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef, | ||||
| 	0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85, | ||||
| 	0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6, | ||||
| 	0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf, | ||||
| 	0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff, | ||||
| 	0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58, | ||||
| 	0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a, | ||||
| 	0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24, | ||||
| 	0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8, | ||||
| 	0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64, | ||||
| 	0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2, | ||||
| 	0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda, | ||||
| 	0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77, | ||||
| 	0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3, 0x01 | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * This is a log table.  That is, gflog[r^i] returns i (modulo f(r)). | ||||
|  * gflog[0] is undefined and the first element is therefore not valid. | ||||
|  */ | ||||
| static const __u8 gflog[256] = | ||||
| { | ||||
| 	0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a, | ||||
| 	0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a, | ||||
| 	0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1, | ||||
| 	0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3, | ||||
| 	0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83, | ||||
| 	0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4, | ||||
| 	0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35, | ||||
| 	0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38, | ||||
| 	0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70, | ||||
| 	0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48, | ||||
| 	0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24, | ||||
| 	0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15, | ||||
| 	0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f, | ||||
| 	0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10, | ||||
| 	0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7, | ||||
| 	0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b, | ||||
| 	0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08, | ||||
| 	0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a, | ||||
| 	0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91, | ||||
| 	0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb, | ||||
| 	0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2, | ||||
| 	0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf, | ||||
| 	0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52, | ||||
| 	0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86, | ||||
| 	0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc, | ||||
| 	0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc, | ||||
| 	0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8, | ||||
| 	0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44, | ||||
| 	0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1, | ||||
| 	0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97, | ||||
| 	0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5, | ||||
| 	0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7 | ||||
| }; | ||||
|  | ||||
| /* This is a multiplication table for the factor 0xc0 (i.e., r^105 (mod f(r)). | ||||
|  * gfmul_c0[f] returns r^105 * f(r) (modulo f(r)). | ||||
|  */ | ||||
| static const __u8 gfmul_c0[256] = | ||||
| { | ||||
| 	0x00, 0xc0, 0x07, 0xc7, 0x0e, 0xce, 0x09, 0xc9, | ||||
| 	0x1c, 0xdc, 0x1b, 0xdb, 0x12, 0xd2, 0x15, 0xd5, | ||||
| 	0x38, 0xf8, 0x3f, 0xff, 0x36, 0xf6, 0x31, 0xf1, | ||||
| 	0x24, 0xe4, 0x23, 0xe3, 0x2a, 0xea, 0x2d, 0xed, | ||||
| 	0x70, 0xb0, 0x77, 0xb7, 0x7e, 0xbe, 0x79, 0xb9, | ||||
| 	0x6c, 0xac, 0x6b, 0xab, 0x62, 0xa2, 0x65, 0xa5, | ||||
| 	0x48, 0x88, 0x4f, 0x8f, 0x46, 0x86, 0x41, 0x81, | ||||
| 	0x54, 0x94, 0x53, 0x93, 0x5a, 0x9a, 0x5d, 0x9d, | ||||
| 	0xe0, 0x20, 0xe7, 0x27, 0xee, 0x2e, 0xe9, 0x29, | ||||
| 	0xfc, 0x3c, 0xfb, 0x3b, 0xf2, 0x32, 0xf5, 0x35, | ||||
| 	0xd8, 0x18, 0xdf, 0x1f, 0xd6, 0x16, 0xd1, 0x11, | ||||
| 	0xc4, 0x04, 0xc3, 0x03, 0xca, 0x0a, 0xcd, 0x0d, | ||||
| 	0x90, 0x50, 0x97, 0x57, 0x9e, 0x5e, 0x99, 0x59, | ||||
| 	0x8c, 0x4c, 0x8b, 0x4b, 0x82, 0x42, 0x85, 0x45, | ||||
| 	0xa8, 0x68, 0xaf, 0x6f, 0xa6, 0x66, 0xa1, 0x61, | ||||
| 	0xb4, 0x74, 0xb3, 0x73, 0xba, 0x7a, 0xbd, 0x7d, | ||||
| 	0x47, 0x87, 0x40, 0x80, 0x49, 0x89, 0x4e, 0x8e, | ||||
| 	0x5b, 0x9b, 0x5c, 0x9c, 0x55, 0x95, 0x52, 0x92, | ||||
| 	0x7f, 0xbf, 0x78, 0xb8, 0x71, 0xb1, 0x76, 0xb6, | ||||
| 	0x63, 0xa3, 0x64, 0xa4, 0x6d, 0xad, 0x6a, 0xaa, | ||||
| 	0x37, 0xf7, 0x30, 0xf0, 0x39, 0xf9, 0x3e, 0xfe, | ||||
| 	0x2b, 0xeb, 0x2c, 0xec, 0x25, 0xe5, 0x22, 0xe2, | ||||
| 	0x0f, 0xcf, 0x08, 0xc8, 0x01, 0xc1, 0x06, 0xc6, | ||||
| 	0x13, 0xd3, 0x14, 0xd4, 0x1d, 0xdd, 0x1a, 0xda, | ||||
| 	0xa7, 0x67, 0xa0, 0x60, 0xa9, 0x69, 0xae, 0x6e, | ||||
| 	0xbb, 0x7b, 0xbc, 0x7c, 0xb5, 0x75, 0xb2, 0x72, | ||||
| 	0x9f, 0x5f, 0x98, 0x58, 0x91, 0x51, 0x96, 0x56, | ||||
| 	0x83, 0x43, 0x84, 0x44, 0x8d, 0x4d, 0x8a, 0x4a, | ||||
| 	0xd7, 0x17, 0xd0, 0x10, 0xd9, 0x19, 0xde, 0x1e, | ||||
| 	0xcb, 0x0b, 0xcc, 0x0c, 0xc5, 0x05, 0xc2, 0x02, | ||||
| 	0xef, 0x2f, 0xe8, 0x28, 0xe1, 0x21, 0xe6, 0x26, | ||||
| 	0xf3, 0x33, 0xf4, 0x34, 0xfd, 0x3d, 0xfa, 0x3a | ||||
| }; | ||||
|  | ||||
|  | ||||
| /* Returns V modulo 255 provided V is in the range -255,-254,...,509. | ||||
|  */ | ||||
| static inline __u8 mod255(int v) | ||||
| { | ||||
| 	if (v > 0) { | ||||
| 		if (v < 255) { | ||||
| 			return v; | ||||
| 		} else { | ||||
| 			return v - 255; | ||||
| 		} | ||||
| 	} else { | ||||
| 		return v + 255; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Add two numbers in the field.  Addition in this field is equivalent | ||||
|  * to a bit-wise exclusive OR operation---subtraction is therefore | ||||
|  * identical to addition. | ||||
|  */ | ||||
| static inline __u8 gfadd(__u8 a, __u8 b) | ||||
| { | ||||
| 	return a ^ b; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Add two vectors of numbers in the field.  Each byte in A and B gets | ||||
|  * added individually. | ||||
|  */ | ||||
| static inline unsigned long gfadd_long(unsigned long a, unsigned long b) | ||||
| { | ||||
| 	return a ^ b; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Multiply two numbers in the field: | ||||
|  */ | ||||
| static inline __u8 gfmul(__u8 a, __u8 b) | ||||
| { | ||||
| 	if (a && b) { | ||||
| 		return gfpow[mod255(gflog[a] + gflog[b])]; | ||||
| 	} else { | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Just like gfmul, except we have already looked up the log of the | ||||
|  * second number. | ||||
|  */ | ||||
| static inline __u8 gfmul_exp(__u8 a, int b) | ||||
| { | ||||
| 	if (a) { | ||||
| 		return gfpow[mod255(gflog[a] + b)]; | ||||
| 	} else { | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Just like gfmul_exp, except that A is a vector of numbers.  That | ||||
|  * is, each byte in A gets multiplied by gfpow[mod255(B)]. | ||||
|  */ | ||||
| static inline unsigned long gfmul_exp_long(unsigned long a, int b) | ||||
| { | ||||
| 	__u8 t; | ||||
|  | ||||
| 	if (sizeof(long) == 4) { | ||||
| 		return ( | ||||
| 		((t = (__u32)a >> 24 & 0xff) ? | ||||
| 		 (((__u32) gfpow[mod255(gflog[t] + b)]) << 24) : 0) | | ||||
| 		((t = (__u32)a >> 16 & 0xff) ? | ||||
| 		 (((__u32) gfpow[mod255(gflog[t] + b)]) << 16) : 0) | | ||||
| 		((t = (__u32)a >> 8 & 0xff) ? | ||||
| 		 (((__u32) gfpow[mod255(gflog[t] + b)]) << 8) : 0) | | ||||
| 		((t = (__u32)a >> 0 & 0xff) ? | ||||
| 		 (((__u32) gfpow[mod255(gflog[t] + b)]) << 0) : 0)); | ||||
| 	} else if (sizeof(long) == 8) { | ||||
| 		return ( | ||||
| 		((t = (__u64)a >> 56 & 0xff) ? | ||||
| 		 (((__u64) gfpow[mod255(gflog[t] + b)]) << 56) : 0) | | ||||
| 		((t = (__u64)a >> 48 & 0xff) ? | ||||
| 		 (((__u64) gfpow[mod255(gflog[t] + b)]) << 48) : 0) | | ||||
| 		((t = (__u64)a >> 40 & 0xff) ? | ||||
| 		 (((__u64) gfpow[mod255(gflog[t] + b)]) << 40) : 0) | | ||||
| 		((t = (__u64)a >> 32 & 0xff) ? | ||||
| 		 (((__u64) gfpow[mod255(gflog[t] + b)]) << 32) : 0) | | ||||
| 		((t = (__u64)a >> 24 & 0xff) ? | ||||
| 		 (((__u64) gfpow[mod255(gflog[t] + b)]) << 24) : 0) | | ||||
| 		((t = (__u64)a >> 16 & 0xff) ? | ||||
| 		 (((__u64) gfpow[mod255(gflog[t] + b)]) << 16) : 0) | | ||||
| 		((t = (__u64)a >> 8 & 0xff) ? | ||||
| 		 (((__u64) gfpow[mod255(gflog[t] + b)]) << 8) : 0) | | ||||
| 		((t = (__u64)a >> 0 & 0xff) ? | ||||
| 		 (((__u64) gfpow[mod255(gflog[t] + b)]) << 0) : 0)); | ||||
| 	} else { | ||||
| 		TRACE_FUN(ft_t_any); | ||||
| 		TRACE_ABORT(-1, ft_t_err, "Error: size of long is %d bytes", | ||||
| 			    (int)sizeof(long)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Divide two numbers in the field.  Returns a/b (modulo f(x)). | ||||
|  */ | ||||
| static inline __u8 gfdiv(__u8 a, __u8 b) | ||||
| { | ||||
| 	if (!b) { | ||||
| 		TRACE_FUN(ft_t_any); | ||||
| 		TRACE_ABORT(0xff, ft_t_bug, "Error: division by zero"); | ||||
| 	} else if (a == 0) { | ||||
| 		return 0; | ||||
| 	} else { | ||||
| 		return gfpow[mod255(gflog[a] - gflog[b])]; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| /* The following functions return the inverse of the matrix of the | ||||
|  * linear system that needs to be solved to determine the error | ||||
|  * magnitudes.  The first deals with matrices of rank 3, while the | ||||
|  * second deals with matrices of rank 2.  The error indices are passed | ||||
|  * in arguments L0,..,L2 (0=first sector, 31=last sector).  The error | ||||
|  * indices must be sorted in ascending order, i.e., L0<L1<L2. | ||||
|  * | ||||
|  * The linear system that needs to be solved for the error magnitudes | ||||
|  * is A * b = s, where s is the known vector of syndromes, b is the | ||||
|  * vector of error magnitudes and A in the ORDER=3 case: | ||||
|  * | ||||
|  *    A_3 = {{1/r^L[0], 1/r^L[1], 1/r^L[2]}, | ||||
|  *          {        1,        1,        1}, | ||||
|  *          { r^L[0], r^L[1], r^L[2]}}  | ||||
|  */ | ||||
| static inline int gfinv3(__u8 l0, | ||||
| 			 __u8 l1,  | ||||
| 			 __u8 l2,  | ||||
| 			 Matrix Ainv) | ||||
| { | ||||
| 	__u8 det; | ||||
| 	__u8 t20, t10, t21, t12, t01, t02; | ||||
| 	int log_det; | ||||
|  | ||||
| 	/* compute some intermediate results: */ | ||||
| 	t20 = gfpow[l2 - l0];	        /* t20 = r^l2/r^l0 */ | ||||
| 	t10 = gfpow[l1 - l0];	        /* t10 = r^l1/r^l0 */ | ||||
| 	t21 = gfpow[l2 - l1];	        /* t21 = r^l2/r^l1 */ | ||||
| 	t12 = gfpow[l1 - l2 + 255];	/* t12 = r^l1/r^l2 */ | ||||
| 	t01 = gfpow[l0 - l1 + 255];	/* t01 = r^l0/r^l1 */ | ||||
| 	t02 = gfpow[l0 - l2 + 255];	/* t02 = r^l0/r^l2 */ | ||||
| 	/* Calculate the determinant of matrix A_3^-1 (sometimes | ||||
| 	 * called the Vandermonde determinant): | ||||
| 	 */ | ||||
| 	det = gfadd(t20, gfadd(t10, gfadd(t21, gfadd(t12, gfadd(t01, t02))))); | ||||
| 	if (!det) { | ||||
| 		TRACE_FUN(ft_t_any); | ||||
| 		TRACE_ABORT(0, ft_t_err, | ||||
| 			   "Inversion failed (3 CRC errors, >0 CRC failures)"); | ||||
| 	} | ||||
| 	log_det = 255 - gflog[det]; | ||||
|  | ||||
| 	/* Now, calculate all of the coefficients: | ||||
| 	 */ | ||||
| 	Ainv[0][0]= gfmul_exp(gfadd(gfpow[l1], gfpow[l2]), log_det); | ||||
| 	Ainv[0][1]= gfmul_exp(gfadd(t21, t12), log_det); | ||||
| 	Ainv[0][2]= gfmul_exp(gfadd(gfpow[255 - l1], gfpow[255 - l2]),log_det); | ||||
|  | ||||
| 	Ainv[1][0]= gfmul_exp(gfadd(gfpow[l0], gfpow[l2]), log_det); | ||||
| 	Ainv[1][1]= gfmul_exp(gfadd(t20, t02), log_det); | ||||
| 	Ainv[1][2]= gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l2]),log_det); | ||||
|  | ||||
| 	Ainv[2][0]= gfmul_exp(gfadd(gfpow[l0], gfpow[l1]), log_det); | ||||
| 	Ainv[2][1]= gfmul_exp(gfadd(t10, t01), log_det); | ||||
| 	Ainv[2][2]= gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l1]),log_det); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
|  | ||||
| static inline int gfinv2(__u8 l0, __u8 l1, Matrix Ainv) | ||||
| { | ||||
| 	__u8 det; | ||||
| 	__u8 t1, t2; | ||||
| 	int log_det; | ||||
|  | ||||
| 	t1 = gfpow[255 - l0]; | ||||
| 	t2 = gfpow[255 - l1]; | ||||
| 	det = gfadd(t1, t2); | ||||
| 	if (!det) { | ||||
| 		TRACE_FUN(ft_t_any); | ||||
| 		TRACE_ABORT(0, ft_t_err, | ||||
| 			   "Inversion failed (2 CRC errors, >0 CRC failures)"); | ||||
| 	} | ||||
| 	log_det = 255 - gflog[det]; | ||||
|  | ||||
| 	/* Now, calculate all of the coefficients: | ||||
| 	 */ | ||||
| 	Ainv[0][0] = Ainv[1][0] = gfpow[log_det]; | ||||
|  | ||||
| 	Ainv[0][1] = gfmul_exp(t2, log_det); | ||||
| 	Ainv[1][1] = gfmul_exp(t1, log_det); | ||||
|  | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Multiply matrix A by vector S and return result in vector B.  M is | ||||
|  * assumed to be of order NxN, S and B of order Nx1. | ||||
|  */ | ||||
| static inline void gfmat_mul(int n, Matrix A,  | ||||
| 			     __u8 *s, __u8 *b) | ||||
| { | ||||
| 	int i, j; | ||||
| 	__u8 dot_prod; | ||||
|  | ||||
| 	for (i = 0; i < n; ++i) { | ||||
| 		dot_prod = 0; | ||||
| 		for (j = 0; j < n; ++j) { | ||||
| 			dot_prod = gfadd(dot_prod, gfmul(A[i][j], s[j])); | ||||
| 		} | ||||
| 		b[i] = dot_prod; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| /* 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; | ||||
| } | ||||
| @@ -1,84 +0,0 @@ | ||||
| #ifndef _FTAPE_ECC_H_ | ||||
| #define _FTAPE_ECC_H_ | ||||
|  | ||||
| /* | ||||
|  *      Copyright (C) 1993 Ning and David Mosberger. | ||||
|  *      Original: | ||||
|  *      Copyright (C) 1993 Bas Laarhoven. | ||||
|  *      Copyright (C) 1992 David L. Brown, Jr. | ||||
|   | ||||
|  This program is free software; you can redistribute it and/or | ||||
|  modify it under the terms of the GNU General Public License as | ||||
|  published by the Free Software Foundation; either version 2, or (at | ||||
|  your option) any later version. | ||||
|   | ||||
|  This program is distributed in the hope that it will be useful, but | ||||
|  WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  General Public License for more details. | ||||
|   | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, | ||||
|  USA. | ||||
|   | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ecc.h,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:18:11 $ | ||||
|  * | ||||
|  *      This file contains the definitions for the | ||||
|  *      Reed-Solomon error correction code  | ||||
|  *      for the QIC-40/80 tape streamer device driver. | ||||
|  */ | ||||
|  | ||||
| #include "../lowlevel/ftape-bsm.h" | ||||
|  | ||||
| #define BAD_CLEAR(entry) ((entry)=0) | ||||
| #define BAD_SET(entry,sector) ((entry)|=(1<<(sector))) | ||||
| #define BAD_CHECK(entry,sector) ((entry)&(1<<(sector))) | ||||
|  | ||||
| /* | ||||
|  * Return values for ecc_correct_data: | ||||
|  */ | ||||
| enum { | ||||
| 	ECC_OK,			/* Data was correct. */ | ||||
| 	ECC_CORRECTED,		/* Correctable error in data. */ | ||||
| 	ECC_FAILED,		/* Could not correct data. */ | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * Representation of an in memory segment.  MARKED_BAD lists the | ||||
|  * sectors that were marked bad during formatting.  If the N-th sector | ||||
|  * in a segment is marked bad, bit 1<<N will be set in MARKED_BAD. | ||||
|  * The sectors should be read in from the disk and packed, as if the | ||||
|  * bad sectors were not there, and the segment just contained fewer | ||||
|  * sectors.  READ_SECTORS is a bitmap of errors encountered while | ||||
|  * reading the data.  These offsets are relative to the packed data. | ||||
|  * BLOCKS is a count of the sectors not marked bad.  This is just to | ||||
|  * prevent having to count the zero bits in MARKED_BAD each time this | ||||
|  * is needed.  DATA is the actual sector packed data from (or to) the | ||||
|  * tape. | ||||
|  */ | ||||
|  struct memory_segment { | ||||
| 	SectorMap marked_bad; | ||||
| 	SectorMap read_bad; | ||||
|  	int blocks; | ||||
|  	__u8 *data; | ||||
| 	SectorMap corrected; | ||||
|  }; | ||||
|  | ||||
| /* | ||||
|  * ecc.c defined global variables: | ||||
|  */ | ||||
| #ifdef TEST | ||||
| extern int ftape_ecc_tracing; | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  * ecc.c defined global functions: | ||||
|  */ | ||||
| extern int ftape_ecc_correct_data(struct memory_segment *data); | ||||
| extern int ftape_ecc_set_segment_parity(struct memory_segment *data); | ||||
|  | ||||
| #endif	/* _FTAPE_ECC_H_ */ | ||||
| @@ -1,344 +0,0 @@ | ||||
| /* | ||||
|  * Copyright (C) 1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-format.c,v $ | ||||
|  * $Revision: 1.2.4.1 $ | ||||
|  * $Date: 1997/11/14 16:05:39 $ | ||||
|  * | ||||
|  *      This file contains the code to support formatting of floppy | ||||
|  *      tape cartridges with the QIC-40/80/3010/3020 floppy-tape | ||||
|  *      driver "ftape" for Linux. | ||||
|  */ | ||||
|   | ||||
| #include <linux/string.h> | ||||
| #include <linux/errno.h> | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include <linux/qic117.h> | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
| #include "../lowlevel/ftape-io.h" | ||||
| #include "../lowlevel/ftape-ctl.h" | ||||
| #include "../lowlevel/ftape-rw.h" | ||||
| #include "../lowlevel/ftape-ecc.h" | ||||
| #include "../lowlevel/ftape-bsm.h" | ||||
| #include "../lowlevel/ftape-format.h" | ||||
|  | ||||
| #if defined(TESTING) | ||||
| #define FT_FMT_SEGS_PER_BUF 50 | ||||
| #else | ||||
| #define FT_FMT_SEGS_PER_BUF (FT_BUFF_SIZE/(4*FT_SECTORS_PER_SEGMENT)) | ||||
| #endif | ||||
|  | ||||
| static spinlock_t ftape_format_lock; | ||||
|  | ||||
| /* | ||||
|  *  first segment of the new buffer | ||||
|  */ | ||||
| static int switch_segment; | ||||
|  | ||||
| /* | ||||
|  *  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),); | ||||
| 	spin_lock_irqsave(&ftape_format_lock, flags); | ||||
| 	TRACE_CATCH(fdc_setup_formatting(head), restore_flags(flags)); | ||||
| 	spin_unlock_irqrestore(&ftape_format_lock, 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; | ||||
| } | ||||
| @@ -1,37 +0,0 @@ | ||||
| #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 | ||||
| @@ -1,160 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1993-1996 Bas Laarhoven, | ||||
|  *                (C) 1996-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  *      This file contains the code that interfaces the kernel | ||||
|  *      for the QIC-40/80/3010/3020 floppy-tape driver for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/module.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/fs.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/signal.h> | ||||
| #include <linux/major.h> | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/qic117.h> | ||||
| #ifdef CONFIG_ZFTAPE | ||||
| #include <linux/zftape.h> | ||||
| #endif | ||||
|  | ||||
| #include "../lowlevel/ftape-init.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" | ||||
|  | ||||
|  | ||||
| #if defined(MODULE) && !defined(CONFIG_FT_NO_TRACE_AT_ALL) | ||||
| static int ft_tracing = -1; | ||||
| #endif | ||||
|  | ||||
|  | ||||
| /*  Called by modules package when installing the driver | ||||
|  *  or by kernel during the initialization phase | ||||
|  */ | ||||
| static int __init ftape_init(void) | ||||
| { | ||||
| 	TRACE_FUN(ft_t_flow); | ||||
|  | ||||
| #ifdef MODULE | ||||
| #ifndef CONFIG_FT_NO_TRACE_AT_ALL | ||||
| 	if (ft_tracing != -1) { | ||||
| 		ftape_tracing = ft_tracing; | ||||
| 	} | ||||
| #endif | ||||
| 	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"); | ||||
|         } | ||||
| #else /* !MODULE */ | ||||
| 	/* print a short no-nonsense boot message */ | ||||
| 	printk(KERN_INFO FTAPE_VERSION "\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. | ||||
| 	 */ | ||||
| #ifdef TESTING | ||||
| #ifdef MODULE | ||||
| 	while (ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS) < 0) { | ||||
| 		ftape_sleep(FT_SECOND/20); | ||||
| 		if (signal_pending(current)) { | ||||
| 			(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 defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) | ||||
| 	(void)ftape_proc_init(); | ||||
| #endif | ||||
| #ifdef CONFIG_ZFTAPE | ||||
| 	(void)zft_init(); | ||||
| #endif | ||||
| 	TRACE_EXIT 0; | ||||
| } | ||||
|  | ||||
| module_param(ft_fdc_base,       uint, 0); | ||||
| MODULE_PARM_DESC(ft_fdc_base,  "Base address of FDC controller."); | ||||
| module_param(ft_fdc_irq,        uint, 0); | ||||
| MODULE_PARM_DESC(ft_fdc_irq,   "IRQ (interrupt channel) to use."); | ||||
| module_param(ft_fdc_dma,        uint, 0); | ||||
| MODULE_PARM_DESC(ft_fdc_dma,   "DMA channel to use."); | ||||
| module_param(ft_fdc_threshold,  uint, 0); | ||||
| MODULE_PARM_DESC(ft_fdc_threshold,  "Threshold of the FDC Fifo."); | ||||
| module_param(ft_fdc_rate_limit, uint, 0); | ||||
| MODULE_PARM_DESC(ft_fdc_rate_limit, "Maximal data rate for FDC."); | ||||
| module_param(ft_probe_fc10,     bool, 0); | ||||
| MODULE_PARM_DESC(ft_probe_fc10, | ||||
| 	    "If non-zero, probe for a Colorado FC-10/FC-20 controller."); | ||||
| module_param(ft_mach2,          bool, 0); | ||||
| MODULE_PARM_DESC(ft_mach2, | ||||
| 	    "If non-zero, probe for a Mountain MACH-2 controller."); | ||||
| #if defined(MODULE) && !defined(CONFIG_FT_NO_TRACE_AT_ALL) | ||||
| module_param(ft_tracing,        int, 0644); | ||||
| MODULE_PARM_DESC(ft_tracing, | ||||
| 	    "Amount of debugging output, 0 <= tracing <= 8, default 3."); | ||||
| #endif | ||||
|  | ||||
| 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."); | ||||
| MODULE_LICENSE("GPL"); | ||||
|  | ||||
| static void __exit ftape_exit(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; | ||||
| } | ||||
|  | ||||
| module_init(ftape_init); | ||||
| module_exit(ftape_exit); | ||||
| @@ -1,43 +0,0 @@ | ||||
| #ifndef _FTAPE_INIT_H | ||||
| #define _FTAPE_INIT_H | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 1993-1996 Bas Laarhoven, | ||||
|  *           (C) 1996-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-init.h,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:18:16 $ | ||||
|  * | ||||
|  * This file contains the definitions for the interface to  | ||||
|  * the Linux kernel for floppy tape driver ftape. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <linux/linkage.h> | ||||
| #include <linux/signal.h> | ||||
|  | ||||
| #define _NEVER_BLOCK    (sigmask(SIGKILL) | sigmask(SIGSTOP)) | ||||
| #define _DONT_BLOCK     (_NEVER_BLOCK | sigmask(SIGINT)) | ||||
| #define _DO_BLOCK       (sigmask(SIGPIPE)) | ||||
|  | ||||
| #ifndef QIC117_TAPE_MAJOR | ||||
| #define QIC117_TAPE_MAJOR 27 | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
| @@ -1,992 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1993-1996 Bas Laarhoven, | ||||
|  *                (C) 1996      Kai Harrekilde-Petersen, | ||||
|  *                (C) 1997      Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-io.c,v $ | ||||
|  * $Revision: 1.4 $ | ||||
|  * $Date: 1997/11/11 14:02:36 $ | ||||
|  * | ||||
|  *      This file contains the general control functions for the | ||||
|  *      QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/errno.h> | ||||
| #include <linux/sched.h> | ||||
| #include <linux/mm.h> | ||||
| #include <asm/system.h> | ||||
| #include <linux/ioctl.h> | ||||
| #include <linux/mtio.h> | ||||
| #include <linux/delay.h> | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include <linux/qic117.h> | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
| #include "../lowlevel/fdc-io.h" | ||||
| #include "../lowlevel/ftape-io.h" | ||||
| #include "../lowlevel/ftape-ctl.h" | ||||
| #include "../lowlevel/ftape-rw.h" | ||||
| #include "../lowlevel/ftape-write.h" | ||||
| #include "../lowlevel/ftape-read.h" | ||||
| #include "../lowlevel/ftape-init.h" | ||||
| #include "../lowlevel/ftape-calibr.h" | ||||
|  | ||||
| /*      Global vars. | ||||
|  */ | ||||
| /* NOTE: sectors start numbering at 1, all others at 0 ! */ | ||||
| ft_timeout_table ftape_timeout; | ||||
| unsigned int ftape_tape_len; | ||||
| 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; | ||||
| 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 { | ||||
| 		long timeout; | ||||
| 		unsigned long flags; | ||||
| 		unsigned int ticks = (time + FT_USPT - 1) / FT_USPT; | ||||
|  | ||||
| 		TRACE(ft_t_any, "%d msec, %d ticks", time/1000, ticks); | ||||
| 		timeout = ticks; | ||||
| 		save_flags(flags); | ||||
| 		sti(); | ||||
| 		msleep_interruptible(jiffies_to_msecs(timeout)); | ||||
| 		/*  Mmm. Isn't current->blocked == 0xffffffff ? | ||||
| 		 */ | ||||
| 		if (signal_pending(current)) { | ||||
| 			TRACE(ft_t_err, "awoken by non-blocked signal :-("); | ||||
| 		} | ||||
| 		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; | ||||
| 	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; | ||||
| } | ||||
|  | ||||
| static 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_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; | ||||
| } | ||||
|  | ||||
| static int ftape_report_rom_version(int *version) | ||||
| { | ||||
|  | ||||
| 	if (ftape_report_operation(version, QIC_REPORT_ROM_VERSION, 8) < 0) { | ||||
| 		return -EIO; | ||||
| 	} else { | ||||
| 		return 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; | ||||
| 	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; | ||||
| } | ||||
| @@ -1,90 +0,0 @@ | ||||
| #ifndef _FTAPE_IO_H | ||||
| #define _FTAPE_IO_H | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 1993-1996 Bas Laarhoven, | ||||
|  *           (C) 1997      Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-io.h,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:18:18 $ | ||||
|  * | ||||
|  *      This file contains definitions for the glue part of the | ||||
|  *      QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/qic117.h> | ||||
| #include <linux/ftape-vendors.h> | ||||
|  | ||||
| typedef struct { | ||||
| 	unsigned int seek; | ||||
| 	unsigned int reset; | ||||
| 	unsigned int rewind; | ||||
| 	unsigned int head_seek; | ||||
| 	unsigned int stop; | ||||
| 	unsigned int pause; | ||||
| } ft_timeout_table; | ||||
|  | ||||
| typedef enum { | ||||
| 	prehistoric, pre_qic117c, post_qic117b, post_qic117d  | ||||
| } qic_model; | ||||
|  | ||||
| /* | ||||
|  *      ftape-io.c defined global vars. | ||||
|  */ | ||||
| extern ft_timeout_table ftape_timeout; | ||||
| extern unsigned int ftape_tape_len; | ||||
| extern volatile qic117_cmd_t ftape_current_command; | ||||
| extern const struct qic117_command_table qic117_cmds[]; | ||||
| extern int ftape_might_be_off_track; | ||||
|  | ||||
| /* | ||||
|  *      ftape-io.c defined global functions. | ||||
|  */ | ||||
| extern void ftape_udelay(unsigned int usecs); | ||||
| extern void  ftape_udelay_calibrate(void); | ||||
| extern void ftape_sleep(unsigned int time); | ||||
| extern void ftape_report_vendor_id(unsigned int *id); | ||||
| extern int  ftape_command(qic117_cmd_t command); | ||||
| extern int  ftape_command_wait(qic117_cmd_t command, | ||||
| 			       unsigned int timeout, | ||||
| 			       int *status); | ||||
| extern int  ftape_parameter(unsigned int parameter); | ||||
| extern int ftape_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_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 | ||||
| @@ -1,214 +0,0 @@ | ||||
| /* | ||||
|  *      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. | ||||
|  | ||||
|  *	Old code removed, switched to dynamic proc entry. | ||||
|  */ | ||||
|  | ||||
|  | ||||
| #if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) | ||||
|  | ||||
| #include <linux/proc_fs.h> | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/qic117.h> | ||||
|  | ||||
| #include "../lowlevel/ftape-io.h" | ||||
| #include "../lowlevel/ftape-ctl.h" | ||||
| #include "../lowlevel/ftape-proc.h" | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
|  | ||||
| static 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; | ||||
| } | ||||
|  | ||||
| static 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 = NULL; | ||||
| 	if (off+count >= len) { | ||||
| 		*eof = 1; | ||||
| 	} else { | ||||
| 		*eof = 0; | ||||
| 	} | ||||
| 	return len; | ||||
| } | ||||
|  | ||||
| int __init ftape_proc_init(void) | ||||
| { | ||||
| 	return create_proc_read_entry("ftape", 0, &proc_root, | ||||
| 		ftape_read_proc, NULL) != NULL; | ||||
| } | ||||
|  | ||||
| void ftape_proc_destroy(void) | ||||
| { | ||||
| 	remove_proc_entry("ftape", &proc_root); | ||||
| } | ||||
|  | ||||
| #endif /* defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) */ | ||||
| @@ -1,35 +0,0 @@ | ||||
| #ifndef _FTAPE_PROC_H | ||||
| #define _FTAPE_PROC_H | ||||
|  | ||||
| /* | ||||
|  *      Copyright (C) 1997 Claus-Justus Heine | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-proc.h,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:18:20 $ | ||||
|  * | ||||
|  *      This file contains definitions for the procfs interface of the | ||||
|  *      QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/proc_fs.h> | ||||
|  | ||||
| extern int  ftape_proc_init(void); | ||||
| extern void ftape_proc_destroy(void); | ||||
|  | ||||
| #endif | ||||
| @@ -1,621 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1993-1996 Bas Laarhoven, | ||||
|  *                (C) 1996-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-read.c,v $ | ||||
|  * $Revision: 1.6 $ | ||||
|  * $Date: 1997/10/21 14:39:22 $ | ||||
|  * | ||||
|  *      This file contains the reading code | ||||
|  *      for the QIC-117 floppy-tape driver for Linux. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <linux/string.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/mm.h> | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include <linux/qic117.h> | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
| #include "../lowlevel/ftape-read.h" | ||||
| #include "../lowlevel/ftape-io.h" | ||||
| #include "../lowlevel/ftape-ctl.h" | ||||
| #include "../lowlevel/ftape-rw.h" | ||||
| #include "../lowlevel/ftape-write.h" | ||||
| #include "../lowlevel/ftape-ecc.h" | ||||
| #include "../lowlevel/ftape-bsm.h" | ||||
|  | ||||
| /*      Global vars. | ||||
|  */ | ||||
|  | ||||
| /*      Local vars. | ||||
|  */ | ||||
|  | ||||
| void ftape_zap_read_buffers(void) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| 	for (i = 0; i < ft_nr_buffers; ++i) { | ||||
| /*  changed to "fit" with dynamic allocation of tape_buffer. --khp  */ | ||||
| 		ft_buffer[i]->status = waiting; | ||||
| 		ft_buffer[i]->bytes = 0; | ||||
| 		ft_buffer[i]->skip = 0; | ||||
| 		ft_buffer[i]->retry = 0; | ||||
| 	} | ||||
| /*	ftape_reset_buffer(); */ | ||||
| } | ||||
|  | ||||
| static SectorMap convert_sector_map(buffer_struct * buff) | ||||
| { | ||||
| 	int i = 0; | ||||
| 	SectorMap bad_map = ftape_get_bad_sector_entry(buff->segment_id); | ||||
| 	SectorMap src_map = buff->soft_error_map | buff->hard_error_map; | ||||
| 	SectorMap dst_map = 0; | ||||
| 	TRACE_FUN(ft_t_any); | ||||
|  | ||||
| 	if (bad_map || src_map) { | ||||
| 		TRACE(ft_t_flow, "bad_map = 0x%08lx", (long) bad_map); | ||||
| 		TRACE(ft_t_flow, "src_map = 0x%08lx", (long) src_map); | ||||
| 	} | ||||
| 	while (bad_map) { | ||||
| 		while ((bad_map & 1) == 0) { | ||||
| 			if (src_map & 1) { | ||||
| 				dst_map |= (1 << i); | ||||
| 			} | ||||
| 			src_map >>= 1; | ||||
| 			bad_map >>= 1; | ||||
| 			++i; | ||||
| 		} | ||||
| 		/* (bad_map & 1) == 1 */ | ||||
| 		src_map >>= 1; | ||||
| 		bad_map >>= 1; | ||||
| 	} | ||||
| 	if (src_map) { | ||||
| 		dst_map |= (src_map << i); | ||||
| 	} | ||||
| 	if (dst_map) { | ||||
| 		TRACE(ft_t_flow, "dst_map = 0x%08lx", (long) dst_map); | ||||
| 	} | ||||
| 	TRACE_EXIT dst_map; | ||||
| } | ||||
|  | ||||
| static int correct_and_copy_fraction(buffer_struct *buff, __u8 * destination, | ||||
| 				     int start, int size) | ||||
| { | ||||
| 	struct memory_segment mseg; | ||||
| 	int result; | ||||
| 	SectorMap read_bad; | ||||
| 	TRACE_FUN(ft_t_any); | ||||
|  | ||||
| 	mseg.read_bad = convert_sector_map(buff); | ||||
| 	mseg.marked_bad = 0;	/* not used... */ | ||||
| 	mseg.blocks = buff->bytes / FT_SECTOR_SIZE; | ||||
| 	mseg.data = buff->address; | ||||
| 	/*    If there are no data sectors we can skip this segment. | ||||
| 	 */ | ||||
| 	if (mseg.blocks <= 3) { | ||||
| 		TRACE_ABORT(0, ft_t_noise, "empty segment"); | ||||
| 	} | ||||
| 	read_bad = mseg.read_bad; | ||||
| 	ft_history.crc_errors += count_ones(read_bad); | ||||
| 	result = ftape_ecc_correct_data(&mseg); | ||||
| 	if (read_bad != 0 || mseg.corrected != 0) { | ||||
| 		TRACE(ft_t_noise, "crc error map: 0x%08lx", (unsigned long)read_bad); | ||||
| 		TRACE(ft_t_noise, "corrected map: 0x%08lx", (unsigned long)mseg.corrected); | ||||
| 		ft_history.corrected += count_ones(mseg.corrected); | ||||
| 	} | ||||
| 	if (result == ECC_CORRECTED || result == ECC_OK) { | ||||
| 		if (result == ECC_CORRECTED) { | ||||
| 			TRACE(ft_t_info, "ecc corrected segment: %d", buff->segment_id); | ||||
| 		} | ||||
| 		if(start < 0) { | ||||
| 			start= 0; | ||||
| 		} | ||||
| 		if((start+size) > ((mseg.blocks - 3) * FT_SECTOR_SIZE)) { | ||||
| 			size = (mseg.blocks - 3) * FT_SECTOR_SIZE  - start; | ||||
| 		}  | ||||
| 		if (size < 0) { | ||||
| 			size= 0; | ||||
| 		} | ||||
| 		if(size > 0) { | ||||
| 			memcpy(destination + start, mseg.data + start, size); | ||||
| 		} | ||||
| 		if ((read_bad ^ mseg.corrected) & mseg.corrected) { | ||||
| 			/* sectors corrected without crc errors set */ | ||||
| 			ft_history.crc_failures++; | ||||
| 		} | ||||
| 		TRACE_EXIT size; /* (mseg.blocks - 3) * FT_SECTOR_SIZE; */ | ||||
| 	} else { | ||||
| 		ft_history.ecc_failures++; | ||||
| 		TRACE_ABORT(-EAGAIN, | ||||
| 			    ft_t_err, "ecc failure on segment %d", | ||||
| 			    buff->segment_id); | ||||
| 	} | ||||
| 	TRACE_EXIT 0; | ||||
| } | ||||
|  | ||||
| /*      Read given segment into buffer at address. | ||||
|  */ | ||||
| int ftape_read_segment_fraction(const int segment_id, | ||||
| 				void  *address,  | ||||
| 				const ft_read_mode_t read_mode, | ||||
| 				const int start, | ||||
| 				const int size) | ||||
| { | ||||
| 	int result = 0; | ||||
| 	int retry  = 0; | ||||
| 	int bytes_read = 0; | ||||
| 	int read_done  = 0; | ||||
| 	TRACE_FUN(ft_t_flow); | ||||
|  | ||||
| 	ft_history.used |= 1; | ||||
| 	TRACE(ft_t_data_flow, "segment_id = %d", segment_id); | ||||
| 	if (ft_driver_state != reading) { | ||||
| 		TRACE(ft_t_noise, "calling ftape_abort_operation"); | ||||
| 		TRACE_CATCH(ftape_abort_operation(),); | ||||
| 		ftape_set_state(reading); | ||||
| 	} | ||||
| 	for(;;) { | ||||
| 		buffer_struct *tail; | ||||
| 		/*  Allow escape from this loop on signal ! | ||||
| 		 */ | ||||
| 		FT_SIGNAL_EXIT(_DONT_BLOCK); | ||||
| 		/*  Search all full buffers for the first matching the | ||||
| 		 *  wanted segment.  Clear other buffers on the fly. | ||||
| 		 */ | ||||
| 		tail = ftape_get_buffer(ft_queue_tail); | ||||
| 		while (!read_done && tail->status == done) { | ||||
| 			/*  Allow escape from this loop on signal ! | ||||
| 			 */ | ||||
| 			FT_SIGNAL_EXIT(_DONT_BLOCK); | ||||
| 			if (tail->segment_id == segment_id) { | ||||
| 				/*  If out buffer is already full, | ||||
| 				 *  return its contents.   | ||||
| 				 */ | ||||
| 				TRACE(ft_t_flow, "found segment in cache: %d", | ||||
| 				      segment_id); | ||||
| 				if (tail->deleted) { | ||||
| 					/*  Return a value that | ||||
| 					 *  read_header_segment | ||||
| 					 *  understands.  As this | ||||
| 					 *  should only occur when | ||||
| 					 *  searching for the header | ||||
| 					 *  segments it shouldn't be | ||||
| 					 *  misinterpreted elsewhere. | ||||
| 					 */ | ||||
| 					TRACE_EXIT 0; | ||||
| 				} | ||||
| 				result = correct_and_copy_fraction( | ||||
| 					tail, | ||||
| 					address, | ||||
| 					start, | ||||
| 					size); | ||||
| 				TRACE(ft_t_flow, "segment contains (bytes): %d", | ||||
| 				      result); | ||||
| 				if (result < 0) { | ||||
| 					if (result != -EAGAIN) { | ||||
| 						TRACE_EXIT result; | ||||
| 					} | ||||
| 					/* keep read_done == 0, will | ||||
| 					 * trigger | ||||
| 					 * ftape_abort_operation | ||||
| 					 * because reading wrong | ||||
| 					 * segment. | ||||
| 					 */ | ||||
| 					TRACE(ft_t_err, "ecc failed, retry"); | ||||
| 					++retry; | ||||
| 				} else { | ||||
| 					read_done = 1; | ||||
| 					bytes_read = result; | ||||
| 				} | ||||
| 			} else { | ||||
| 				TRACE(ft_t_flow,"zapping segment in cache: %d", | ||||
| 				      tail->segment_id); | ||||
| 			} | ||||
| 			tail->status = waiting; | ||||
| 			tail = ftape_next_buffer(ft_queue_tail); | ||||
| 		} | ||||
| 		if (!read_done && tail->status == reading) { | ||||
| 			if (tail->segment_id == segment_id) { | ||||
| 				switch(ftape_wait_segment(reading)) { | ||||
| 				case 0: | ||||
| 					break; | ||||
| 				case -EINTR: | ||||
| 					TRACE_ABORT(-EINTR, ft_t_warn, | ||||
| 						    "interrupted by " | ||||
| 						    "non-blockable signal"); | ||||
| 					break; | ||||
| 				default: | ||||
| 					TRACE(ft_t_noise, | ||||
| 					      "wait_segment failed"); | ||||
| 					ftape_abort_operation(); | ||||
| 					ftape_set_state(reading); | ||||
| 					break; | ||||
| 				} | ||||
| 			} else { | ||||
| 				/*  We're reading the wrong segment, | ||||
| 				 *  stop runner. | ||||
| 				 */ | ||||
| 				TRACE(ft_t_noise, "reading wrong segment"); | ||||
| 				ftape_abort_operation(); | ||||
| 				ftape_set_state(reading); | ||||
| 			} | ||||
| 		} | ||||
| 		/*    should runner stop ? | ||||
| 		 */ | ||||
| 		if (ft_runner_status == aborting) { | ||||
| 			buffer_struct *head = ftape_get_buffer(ft_queue_head); | ||||
| 			switch(head->status) { | ||||
| 			case error: | ||||
| 				ft_history.defects +=  | ||||
| 					count_ones(head->hard_error_map); | ||||
| 			case reading: | ||||
| 				head->status = waiting; | ||||
| 				break; | ||||
| 			default: | ||||
| 				break; | ||||
| 			} | ||||
| 			TRACE_CATCH(ftape_dumb_stop(),); | ||||
| 		} else { | ||||
| 			/*  If just passed last segment on tape: wait | ||||
| 			 *  for BOT or EOT mark. Sets ft_runner_status to | ||||
| 			 *  idle if at lEOT and successful  | ||||
| 			 */ | ||||
| 			TRACE_CATCH(ftape_handle_logical_eot(),); | ||||
| 		} | ||||
| 		/*    If we got a segment: quit, or else retry up to limit. | ||||
| 		 * | ||||
| 		 *    If segment to read is empty, do not start runner for it, | ||||
| 		 *    but wait for next read call. | ||||
| 		 */ | ||||
| 		if (read_done || | ||||
| 		    ftape_get_bad_sector_entry(segment_id) == EMPTY_SEGMENT ) { | ||||
| 			/* bytes_read = 0;  should still be zero */ | ||||
| 			TRACE_EXIT bytes_read; | ||||
|  | ||||
| 		} | ||||
| 		if (retry > FT_RETRIES_ON_ECC_ERROR) { | ||||
| 			ft_history.defects++; | ||||
| 			TRACE_ABORT(-ENODATA, ft_t_err, | ||||
| 				    "too many retries on ecc failure"); | ||||
| 		} | ||||
| 		/*    Now at least one buffer is empty ! | ||||
| 		 *    Restart runner & tape if needed. | ||||
| 		 */ | ||||
| 		TRACE(ft_t_any, "head: %d, tail: %d, ft_runner_status: %d", | ||||
| 		      ftape_buffer_id(ft_queue_head), | ||||
| 		      ftape_buffer_id(ft_queue_tail), | ||||
| 		      ft_runner_status); | ||||
| 		TRACE(ft_t_any, "buffer[].status, [head]: %d, [tail]: %d", | ||||
| 		      ftape_get_buffer(ft_queue_head)->status, | ||||
| 		      ftape_get_buffer(ft_queue_tail)->status); | ||||
| 		tail = ftape_get_buffer(ft_queue_tail); | ||||
| 		if (tail->status == waiting) { | ||||
| 			buffer_struct *head = ftape_get_buffer(ft_queue_head); | ||||
|  | ||||
| 			ftape_setup_new_segment(head, segment_id, -1); | ||||
| 			if (read_mode == FT_RD_SINGLE) { | ||||
| 				/* disable read-ahead */ | ||||
| 				head->next_segment = 0; | ||||
| 			} | ||||
| 			ftape_calc_next_cluster(head); | ||||
| 			if (ft_runner_status == idle) { | ||||
| 				result = ftape_start_tape(segment_id, | ||||
| 							  head->sector_offset); | ||||
| 				if (result < 0) { | ||||
| 					TRACE_ABORT(result, ft_t_err, "Error: " | ||||
| 						    "segment %d unreachable", | ||||
| 						    segment_id); | ||||
| 				} | ||||
| 			} | ||||
| 			head->status = reading; | ||||
| 			fdc_setup_read_write(head, FDC_READ); | ||||
| 		} | ||||
| 	} | ||||
| 	/* not reached */ | ||||
| 	TRACE_EXIT -EIO; | ||||
| } | ||||
|  | ||||
| int ftape_read_header_segment(__u8 *address) | ||||
| { | ||||
| 	int result; | ||||
| 	int header_segment; | ||||
| 	int first_failed = 0; | ||||
| 	int status; | ||||
| 	TRACE_FUN(ft_t_flow); | ||||
|  | ||||
| 	ft_used_header_segment = -1; | ||||
| 	TRACE_CATCH(ftape_report_drive_status(&status),); | ||||
| 	TRACE(ft_t_flow, "reading..."); | ||||
| 	/*  We're looking for the first header segment. | ||||
| 	 *  A header segment cannot contain bad sectors, therefor at the | ||||
| 	 *  tape start, segments with bad sectors are (according to QIC-40/80) | ||||
| 	 *  written with deleted data marks and must be skipped. | ||||
| 	 */ | ||||
| 	memset(address, '\0', (FT_SECTORS_PER_SEGMENT - 3) * FT_SECTOR_SIZE);  | ||||
| 	result = 0; | ||||
| #define HEADER_SEGMENT_BOUNDARY 68  /* why not 42? */ | ||||
| 	for (header_segment = 0; | ||||
| 	     header_segment < HEADER_SEGMENT_BOUNDARY && result == 0; | ||||
| 	     ++header_segment) { | ||||
| 		/*  Set no read-ahead, the isr will force read-ahead whenever | ||||
| 		 *  it encounters deleted data ! | ||||
| 		 */ | ||||
| 		result = ftape_read_segment(header_segment, | ||||
| 					    address, | ||||
| 					    FT_RD_SINGLE); | ||||
| 		if (result < 0 && !first_failed) { | ||||
| 			TRACE(ft_t_err, "header segment damaged, trying backup"); | ||||
| 			first_failed = 1; | ||||
| 			result = 0;	/* force read of next (backup) segment */ | ||||
| 		} | ||||
| 	} | ||||
| 	if (result < 0 || header_segment >= HEADER_SEGMENT_BOUNDARY) { | ||||
| 		TRACE_ABORT(-EIO, ft_t_err, | ||||
| 			    "no readable header segment found"); | ||||
| 	} | ||||
| 	TRACE_CATCH(ftape_abort_operation(),); | ||||
| 	ft_used_header_segment = header_segment; | ||||
| 	result = ftape_decode_header_segment(address); | ||||
|  	TRACE_EXIT result; | ||||
| } | ||||
|  | ||||
| int ftape_decode_header_segment(__u8 *address) | ||||
| { | ||||
| 	unsigned int max_floppy_side; | ||||
| 	unsigned int max_floppy_track; | ||||
| 	unsigned int max_floppy_sector; | ||||
| 	unsigned int new_tape_len; | ||||
| 	TRACE_FUN(ft_t_flow); | ||||
|  | ||||
| 	if (GET4(address, FT_SIGNATURE) == FT_D2G_MAGIC) { | ||||
| 		/* Ditto 2GB header segment. They encrypt the bad sector map. | ||||
| 		 * We decrypt it and store them in normal format. | ||||
| 		 * I hope this is correct. | ||||
| 		 */ | ||||
| 		int i; | ||||
| 		TRACE(ft_t_warn, | ||||
| 		      "Found Ditto 2GB tape, " | ||||
| 		      "trying to decrypt bad sector map"); | ||||
| 		for (i=256; i < 29 * FT_SECTOR_SIZE; i++) { | ||||
| 			address[i] = ~(address[i] - (i&0xff)); | ||||
| 		} | ||||
| 		PUT4(address, 0,FT_HSEG_MAGIC); | ||||
| 	} else if (GET4(address, FT_SIGNATURE) != FT_HSEG_MAGIC) { | ||||
| 		TRACE_ABORT(-EIO, ft_t_err, | ||||
| 			    "wrong signature in header segment"); | ||||
| 	} | ||||
| 	ft_format_code = (ft_format_type) address[FT_FMT_CODE]; | ||||
| 	if (ft_format_code != fmt_big) { | ||||
| 		ft_header_segment_1   = GET2(address, FT_HSEG_1); | ||||
| 		ft_header_segment_2   = GET2(address, FT_HSEG_2); | ||||
| 		ft_first_data_segment = GET2(address, FT_FRST_SEG); | ||||
| 		ft_last_data_segment  = GET2(address, FT_LAST_SEG); | ||||
| 	} else { | ||||
| 		ft_header_segment_1   = GET4(address, FT_6_HSEG_1); | ||||
| 		ft_header_segment_2   = GET4(address, FT_6_HSEG_2); | ||||
| 		ft_first_data_segment = GET4(address, FT_6_FRST_SEG); | ||||
| 		ft_last_data_segment  = GET4(address, FT_6_LAST_SEG); | ||||
| 	} | ||||
| 	TRACE(ft_t_noise, "first data segment: %d", ft_first_data_segment); | ||||
| 	TRACE(ft_t_noise, "last  data segment: %d", ft_last_data_segment); | ||||
| 	TRACE(ft_t_noise, "header segments are %d and %d", | ||||
| 	      ft_header_segment_1, ft_header_segment_2); | ||||
|  | ||||
| 	/*    Verify tape parameters... | ||||
| 	 *    QIC-40/80 spec:                 tape_parameters: | ||||
| 	 * | ||||
| 	 *    segments-per-track              segments_per_track | ||||
| 	 *    tracks-per-cartridge            tracks_per_tape | ||||
| 	 *    max-floppy-side                 (segments_per_track * | ||||
| 	 *                                    tracks_per_tape - 1) / | ||||
| 	 *                                    ftape_segments_per_head | ||||
| 	 *    max-floppy-track                ftape_segments_per_head / | ||||
| 	 *                                    ftape_segments_per_cylinder - 1 | ||||
| 	 *    max-floppy-sector               ftape_segments_per_cylinder * | ||||
| 	 *                                    FT_SECTORS_PER_SEGMENT | ||||
| 	 */ | ||||
| 	ft_segments_per_track = GET2(address, FT_SPT); | ||||
| 	ft_tracks_per_tape    = address[FT_TPC]; | ||||
| 	max_floppy_side       = address[FT_FHM]; | ||||
| 	max_floppy_track      = address[FT_FTM]; | ||||
| 	max_floppy_sector     = address[FT_FSM]; | ||||
| 	TRACE(ft_t_noise, "(fmt/spt/tpc/fhm/ftm/fsm) = %d/%d/%d/%d/%d/%d", | ||||
| 	      ft_format_code, ft_segments_per_track, ft_tracks_per_tape, | ||||
| 	      max_floppy_side, max_floppy_track, max_floppy_sector); | ||||
| 	new_tape_len = ftape_tape_len; | ||||
| 	switch (ft_format_code) { | ||||
| 	case fmt_425ft: | ||||
| 		new_tape_len = 425; | ||||
| 		break; | ||||
| 	case fmt_normal: | ||||
| 		if (ftape_tape_len == 0) {	/* otherwise 307 ft */ | ||||
| 			new_tape_len = 205; | ||||
| 		} | ||||
| 		break; | ||||
| 	case fmt_1100ft: | ||||
| 		new_tape_len = 1100; | ||||
| 		break; | ||||
| 	case fmt_var:{ | ||||
| 			int segments_per_1000_inch = 1;		/* non-zero default for switch */ | ||||
| 			switch (ft_qic_std) { | ||||
| 			case QIC_TAPE_QIC40: | ||||
| 				segments_per_1000_inch = 332; | ||||
| 				break; | ||||
| 			case QIC_TAPE_QIC80: | ||||
| 				segments_per_1000_inch = 488; | ||||
| 				break; | ||||
| 			case QIC_TAPE_QIC3010: | ||||
| 				segments_per_1000_inch = 730; | ||||
| 				break; | ||||
| 			case QIC_TAPE_QIC3020: | ||||
| 				segments_per_1000_inch = 1430; | ||||
| 				break; | ||||
| 			} | ||||
| 			new_tape_len = (1000 * ft_segments_per_track + | ||||
| 					(segments_per_1000_inch - 1)) / segments_per_1000_inch; | ||||
| 			break; | ||||
| 		} | ||||
| 	case fmt_big:{ | ||||
| 			int segments_per_1000_inch = 1;		/* non-zero default for switch */ | ||||
| 			switch (ft_qic_std) { | ||||
| 			case QIC_TAPE_QIC40: | ||||
| 				segments_per_1000_inch = 332; | ||||
| 				break; | ||||
| 			case QIC_TAPE_QIC80: | ||||
| 				segments_per_1000_inch = 488; | ||||
| 				break; | ||||
| 			case QIC_TAPE_QIC3010: | ||||
| 				segments_per_1000_inch = 730; | ||||
| 				break; | ||||
| 			case QIC_TAPE_QIC3020: | ||||
| 				segments_per_1000_inch = 1430; | ||||
| 				break; | ||||
| 			default: | ||||
| 				TRACE_ABORT(-EIO, ft_t_bug, | ||||
| 			"%x QIC-standard with fmt-code %d, please report", | ||||
| 					    ft_qic_std, ft_format_code); | ||||
| 			} | ||||
| 			new_tape_len = ((1000 * ft_segments_per_track + | ||||
| 					 (segments_per_1000_inch - 1)) /  | ||||
| 					segments_per_1000_inch); | ||||
| 			break; | ||||
| 		} | ||||
| 	default: | ||||
| 		TRACE_ABORT(-EIO, ft_t_err, | ||||
| 			    "unknown tape format, please report !"); | ||||
| 	} | ||||
| 	if (new_tape_len != ftape_tape_len) { | ||||
| 		ftape_tape_len = new_tape_len; | ||||
| 		TRACE(ft_t_info, "calculated tape length is %d ft", | ||||
| 		      ftape_tape_len); | ||||
| 		ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len); | ||||
| 	} | ||||
| 	if (ft_segments_per_track == 0 && ft_tracks_per_tape == 0 && | ||||
| 	    max_floppy_side == 0 && max_floppy_track == 0 && | ||||
| 	    max_floppy_sector == 0) { | ||||
| 		/*  QIC-40 Rev E and earlier has no values in the header. | ||||
| 		 */ | ||||
| 		ft_segments_per_track = 68; | ||||
| 		ft_tracks_per_tape = 20; | ||||
| 		max_floppy_side = 1; | ||||
| 		max_floppy_track = 169; | ||||
| 		max_floppy_sector = 128; | ||||
| 	} | ||||
| 	/*  This test will compensate for the wrong parameter on tapes | ||||
| 	 *  formatted by Conner software. | ||||
| 	 */ | ||||
| 	if (ft_segments_per_track == 150 && | ||||
| 	    ft_tracks_per_tape == 28 && | ||||
| 	    max_floppy_side == 7 && | ||||
| 	    max_floppy_track == 149 && | ||||
| 	    max_floppy_sector == 128) { | ||||
| TRACE(ft_t_info, "the famous CONNER bug: max_floppy_side off by one !"); | ||||
| 		max_floppy_side = 6; | ||||
| 	} | ||||
| 	/*  These tests will compensate for the wrong parameter on tapes | ||||
| 	 *  formatted by ComByte Windows software. | ||||
| 	 * | ||||
| 	 *  First, for 205 foot tapes | ||||
| 	 */ | ||||
| 	if (ft_segments_per_track == 100 && | ||||
| 	    ft_tracks_per_tape == 28 && | ||||
| 	    max_floppy_side == 9 && | ||||
| 	    max_floppy_track == 149 && | ||||
| 	    max_floppy_sector == 128) { | ||||
| TRACE(ft_t_info, "the ComByte bug: max_floppy_side incorrect!"); | ||||
| 		max_floppy_side = 4; | ||||
| 	} | ||||
| 	/* Next, for 307 foot tapes. */ | ||||
| 	if (ft_segments_per_track == 150 && | ||||
| 	    ft_tracks_per_tape == 28 && | ||||
| 	    max_floppy_side == 9 && | ||||
| 	    max_floppy_track == 149 && | ||||
| 	    max_floppy_sector == 128) { | ||||
| TRACE(ft_t_info, "the ComByte bug: max_floppy_side incorrect!"); | ||||
| 		max_floppy_side = 6; | ||||
| 	} | ||||
| 	/*  This test will compensate for the wrong parameter on tapes | ||||
| 	 *  formatted by Colorado Windows software. | ||||
| 	 */ | ||||
| 	if (ft_segments_per_track == 150 && | ||||
| 	    ft_tracks_per_tape == 28 && | ||||
| 	    max_floppy_side == 6 && | ||||
| 	    max_floppy_track == 150 && | ||||
| 	    max_floppy_sector == 128) { | ||||
| TRACE(ft_t_info, "the famous Colorado bug: max_floppy_track off by one !"); | ||||
| 		max_floppy_track = 149; | ||||
| 	} | ||||
| 	ftape_segments_per_head = ((max_floppy_sector/FT_SECTORS_PER_SEGMENT) * | ||||
| 				   (max_floppy_track + 1)); | ||||
| 	/*  This test will compensate for some bug reported by Dima | ||||
| 	 *  Brodsky.  Seems to be a Colorado bug, either. (freebee | ||||
| 	 *  Imation tape shipped together with Colorado T3000 | ||||
| 	 */ | ||||
| 	if ((ft_format_code == fmt_var || ft_format_code == fmt_big) && | ||||
| 	    ft_tracks_per_tape == 50 && | ||||
| 	    max_floppy_side == 54 && | ||||
| 	    max_floppy_track == 255 && | ||||
| 	    max_floppy_sector == 128) { | ||||
| TRACE(ft_t_info, "the famous ??? bug: max_floppy_track off by one !"); | ||||
| 		max_floppy_track = 254; | ||||
| 	} | ||||
| 	/* | ||||
| 	 *    Verify drive_configuration with tape parameters | ||||
| 	 */ | ||||
| 	if (ftape_segments_per_head == 0 || ftape_segments_per_cylinder == 0 || | ||||
| 	  ((ft_segments_per_track * ft_tracks_per_tape - 1) / ftape_segments_per_head | ||||
| 	   != max_floppy_side) || | ||||
| 	    (ftape_segments_per_head / ftape_segments_per_cylinder - 1 != max_floppy_track) || | ||||
| 	(ftape_segments_per_cylinder * FT_SECTORS_PER_SEGMENT != max_floppy_sector) | ||||
| #ifdef TESTING | ||||
| 	    || ((ft_format_code == fmt_var || ft_format_code == fmt_big) &&  | ||||
| 		(max_floppy_track != 254 || max_floppy_sector != 128)) | ||||
| #endif | ||||
| 	   ) { | ||||
| 		char segperheadz = ftape_segments_per_head ? ' ' : '?'; | ||||
| 		char segpercylz  = ftape_segments_per_cylinder ? ' ' : '?'; | ||||
| 		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%c/%d%c/%d", | ||||
| 		      ft_format_code, | ||||
| 		      ft_segments_per_track, | ||||
| 		      ft_tracks_per_tape, | ||||
| 		      ftape_segments_per_head ? | ||||
| 		      ((ft_segments_per_track * ft_tracks_per_tape -1) /  | ||||
| 		       ftape_segments_per_head ) : | ||||
| 			(ft_segments_per_track * ft_tracks_per_tape -1), | ||||
| 			segperheadz, | ||||
| 		      ftape_segments_per_cylinder ? | ||||
| 		      (ftape_segments_per_head /  | ||||
| 		       ftape_segments_per_cylinder - 1 ) : | ||||
| 			ftape_segments_per_head - 1, | ||||
| 			segpercylz, | ||||
| 		      (ftape_segments_per_cylinder * FT_SECTORS_PER_SEGMENT)); | ||||
| 		TRACE_EXIT -EIO; | ||||
| 	} | ||||
| 	ftape_extract_bad_sector_map(address); | ||||
|  	TRACE_EXIT 0; | ||||
| } | ||||
| @@ -1,51 +0,0 @@ | ||||
| #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 */ | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,111 +0,0 @@ | ||||
| #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" | ||||
|  | ||||
| #include <asm/unaligned.h> | ||||
|  | ||||
| #define GET2(address, offset) get_unaligned((__u16*)((__u8 *)address + offset)) | ||||
| #define GET4(address, offset) get_unaligned((__u32*)((__u8 *)address + offset)) | ||||
| #define GET8(address, offset) get_unaligned((__u64*)((__u8 *)address + offset)) | ||||
| #define PUT2(address, offset , value) put_unaligned((value), (__u16*)((__u8 *)address + offset)) | ||||
| #define PUT4(address, offset , value) put_unaligned((value), (__u32*)((__u8 *)address + offset)) | ||||
| #define PUT8(address, offset , value) put_unaligned((value), (__u64*)((__u8 *)address + offset)) | ||||
|  | ||||
| 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. | ||||
|  */ | ||||
| static 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 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 */ | ||||
| @@ -1,104 +0,0 @@ | ||||
| /*  | ||||
|  * Copyright (C) 1996, 1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-setup.c,v $ | ||||
|  * $Revision: 1.7 $ | ||||
|  * $Date: 1997/10/10 09:57:06 $ | ||||
|  * | ||||
|  *      This file contains the code for processing the kernel command | ||||
|  *      line options for the QIC-40/80/3010/3020 floppy-tape driver | ||||
|  *      "ftape" for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/string.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/mm.h> | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include <linux/init.h> | ||||
| #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} | ||||
| }; | ||||
|  | ||||
| static int __init ftape_setup(char *str) | ||||
| { | ||||
| 	int i; | ||||
| 	int param; | ||||
| 	int ints[2]; | ||||
|  | ||||
| 	TRACE_FUN(ft_t_flow); | ||||
|  | ||||
| 	str = get_options(str, ARRAY_SIZE(ints), ints); | ||||
| 	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); | ||||
| 					goto out; | ||||
| 				} | ||||
| 				if(config_params[i].var) { | ||||
| 					TRACE(ft_t_info, "%s=%d", str, param); | ||||
| 					*config_params[i].var = param; | ||||
| 				} | ||||
| 				goto out; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	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"); | ||||
| 	} | ||||
|  out: | ||||
| 	TRACE_EXIT 1; | ||||
| } | ||||
|  | ||||
| __setup("ftape=", ftape_setup); | ||||
| @@ -1,118 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1993-1996 Bas Laarhoven, | ||||
|  *                (C) 1996-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-tracing.c,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:18:27 $ | ||||
|  * | ||||
|  *      This file contains the reading code | ||||
|  *      for the QIC-117 floppy-tape driver for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
|  | ||||
| /*      Global vars. | ||||
|  */ | ||||
| /*      tracing | ||||
|  *      set it to:     to log : | ||||
|  *       0              bugs | ||||
|  *       1              + errors | ||||
|  *       2              + warnings | ||||
|  *       3              + information | ||||
|  *       4              + more information | ||||
|  *       5              + program flow | ||||
|  *       6              + fdc/dma info | ||||
|  *       7              + data flow | ||||
|  *       8              + everything else | ||||
|  */ | ||||
| ft_trace_t ftape_tracing = ft_t_info; /* Default level: information and up */ | ||||
| int  ftape_function_nest_level; | ||||
|  | ||||
| /*      Local vars. | ||||
|  */ | ||||
| static __u8 trace_id; | ||||
| 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); | ||||
| } | ||||
| @@ -1,179 +0,0 @@ | ||||
| #ifndef _FTAPE_TRACING_H | ||||
| #define _FTAPE_TRACING_H | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 1994-1996 Bas Laarhoven, | ||||
|  *           (C) 1996-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-tracing.h,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:18:28 $ | ||||
|  * | ||||
|  *      This file contains definitions that eases the debugging of the | ||||
|  *      QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/kernel.h> | ||||
|  | ||||
| /* | ||||
|  *  Be very careful with TRACE_EXIT and TRACE_ABORT. | ||||
|  * | ||||
|  *  if (something) TRACE_EXIT error; | ||||
|  * | ||||
|  *  will NOT work. Use | ||||
|  * | ||||
|  *  if (something) { | ||||
|  *    TRACE_EXIT error; | ||||
|  *  } | ||||
|  * | ||||
|  *  instead. Maybe a bit dangerous, but save lots of lines of code. | ||||
|  */ | ||||
|  | ||||
| #define LL_X "%d/%d KB" | ||||
| #define LL(x) (unsigned int)((__u64)(x)>>10), (unsigned int)((x)&1023) | ||||
|  | ||||
| typedef enum { | ||||
| 	ft_t_nil = -1, | ||||
| 	ft_t_bug, | ||||
| 	ft_t_err, | ||||
| 	ft_t_warn, | ||||
| 	ft_t_info, | ||||
| 	ft_t_noise, | ||||
| 	ft_t_flow, | ||||
| 	ft_t_fdc_dma, | ||||
| 	ft_t_data_flow, | ||||
| 	ft_t_any | ||||
| } ft_trace_t; | ||||
|  | ||||
| #ifdef  CONFIG_FT_NO_TRACE_AT_ALL | ||||
| /*  the compiler will optimize away most TRACE() macros | ||||
|  */ | ||||
| #define FT_TRACE_TOP_LEVEL	ft_t_bug | ||||
| #define TRACE_FUN(level)	do {} while(0) | ||||
| #define TRACE_EXIT		return | ||||
| #define TRACE(l, m, i...)						\ | ||||
| {									\ | ||||
| 	if ((ft_trace_t)(l) == FT_TRACE_TOP_LEVEL) {			\ | ||||
| 		printk(KERN_INFO"ftape%s(%s):\n"	                \ | ||||
| 		       KERN_INFO m".\n" ,__FILE__, __FUNCTION__ , ##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 (sigtestsetmask(¤t->pending.signal, sig_mask)) {	\ | ||||
| 		TRACE_ABORT(-EINTR,					\ | ||||
| 			    ft_t_warn,					\ | ||||
| 			    "interrupted by non-blockable signal");	\ | ||||
| 	} | ||||
|  | ||||
| #endif /* _FTAPE_TRACING_H */ | ||||
| @@ -1,336 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1993-1995 Bas Laarhoven, | ||||
|  *                (C) 1996-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-write.c,v $ | ||||
|  * $Revision: 1.3.4.1 $ | ||||
|  * $Date: 1997/11/14 18:07:04 $ | ||||
|  * | ||||
|  *      This file contains the writing code | ||||
|  *      for the QIC-117 floppy-tape driver for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/string.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/mm.h> | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include <linux/qic117.h> | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
| #include "../lowlevel/ftape-write.h" | ||||
| #include "../lowlevel/ftape-read.h" | ||||
| #include "../lowlevel/ftape-io.h" | ||||
| #include "../lowlevel/ftape-ctl.h" | ||||
| #include "../lowlevel/ftape-rw.h" | ||||
| #include "../lowlevel/ftape-ecc.h" | ||||
| #include "../lowlevel/ftape-bsm.h" | ||||
| #include "../lowlevel/fdc-isr.h" | ||||
|  | ||||
| /*      Global vars. | ||||
|  */ | ||||
|  | ||||
| /*      Local vars. | ||||
|  */ | ||||
| static int last_write_failed; | ||||
|  | ||||
| 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); | ||||
| 	} | ||||
| } | ||||
| @@ -1,53 +0,0 @@ | ||||
| #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 */ | ||||
|  | ||||
| @@ -1,87 +0,0 @@ | ||||
| /* | ||||
|  *      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 symbols that the ftape low level | ||||
|  *      part of the QIC-40/80/3010/3020 floppy-tape driver "ftape" | ||||
|  *      exports to its high level clients | ||||
|  */ | ||||
|  | ||||
| #include <linux/module.h> | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
| #include "../lowlevel/ftape-init.h" | ||||
| #include "../lowlevel/fdc-io.h" | ||||
| #include "../lowlevel/ftape-read.h" | ||||
| #include "../lowlevel/ftape-write.h" | ||||
| #include "../lowlevel/ftape-io.h" | ||||
| #include "../lowlevel/ftape-ctl.h" | ||||
| #include "../lowlevel/ftape-rw.h" | ||||
| #include "../lowlevel/ftape-bsm.h" | ||||
| #include "../lowlevel/ftape-buffer.h" | ||||
| #include "../lowlevel/ftape-format.h" | ||||
|  | ||||
| /* bad sector handling from ftape-bsm.c */ | ||||
| EXPORT_SYMBOL(ftape_get_bad_sector_entry); | ||||
| EXPORT_SYMBOL(ftape_find_end_of_bsm_list); | ||||
| /* from ftape-rw.c */ | ||||
| EXPORT_SYMBOL(ftape_set_state); | ||||
| /* from ftape-ctl.c */ | ||||
| EXPORT_SYMBOL(ftape_seek_to_bot); | ||||
| EXPORT_SYMBOL(ftape_seek_to_eot); | ||||
| EXPORT_SYMBOL(ftape_abort_operation); | ||||
| EXPORT_SYMBOL(ftape_get_status); | ||||
| EXPORT_SYMBOL(ftape_enable); | ||||
| EXPORT_SYMBOL(ftape_disable); | ||||
| EXPORT_SYMBOL(ftape_mmap); | ||||
| EXPORT_SYMBOL(ftape_calibrate_data_rate); | ||||
| /* from ftape-io.c */ | ||||
| EXPORT_SYMBOL(ftape_reset_drive); | ||||
| EXPORT_SYMBOL(ftape_command); | ||||
| EXPORT_SYMBOL(ftape_parameter); | ||||
| EXPORT_SYMBOL(ftape_ready_wait); | ||||
| EXPORT_SYMBOL(ftape_report_operation); | ||||
| EXPORT_SYMBOL(ftape_report_error); | ||||
| /* from ftape-read.c */ | ||||
| EXPORT_SYMBOL(ftape_read_segment_fraction); | ||||
| EXPORT_SYMBOL(ftape_zap_read_buffers); | ||||
| EXPORT_SYMBOL(ftape_read_header_segment); | ||||
| EXPORT_SYMBOL(ftape_decode_header_segment); | ||||
| /* from ftape-write.c */ | ||||
| EXPORT_SYMBOL(ftape_write_segment); | ||||
| EXPORT_SYMBOL(ftape_start_writing); | ||||
| EXPORT_SYMBOL(ftape_loop_until_writes_done); | ||||
| /* from ftape-buffer.h */ | ||||
| EXPORT_SYMBOL(ftape_set_nr_buffers); | ||||
| /* from ftape-format.h */ | ||||
| EXPORT_SYMBOL(ftape_format_track); | ||||
| EXPORT_SYMBOL(ftape_format_status); | ||||
| EXPORT_SYMBOL(ftape_verify_segment); | ||||
| /* from tracing.c */ | ||||
| #ifndef CONFIG_FT_NO_TRACE_AT_ALL | ||||
| EXPORT_SYMBOL(ftape_tracing); | ||||
| EXPORT_SYMBOL(ftape_function_nest_level); | ||||
| EXPORT_SYMBOL(ftape_trace_call); | ||||
| EXPORT_SYMBOL(ftape_trace_exit); | ||||
| EXPORT_SYMBOL(ftape_trace_log); | ||||
| #endif | ||||
|  | ||||
| @@ -1,36 +0,0 @@ | ||||
| # | ||||
| #       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 | ||||
| # | ||||
|  | ||||
|  | ||||
| # ZFT_OBSOLETE - enable the MTIOC_ZFTAPE_GETBLKSZ ioctl. You should | ||||
| #                leave this enabled for compatibility with taper. | ||||
|  | ||||
| obj-$(CONFIG_ZFTAPE) += zftape.o | ||||
|  | ||||
| zftape-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 zftape_syms.o | ||||
|  | ||||
| EXTRA_CFLAGS := -DZFT_OBSOLETE | ||||
| @@ -1,149 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1995-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-buffers.c,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:18:59 $ | ||||
|  * | ||||
|  *      This file contains the dynamic buffer allocation routines  | ||||
|  *      of zftape | ||||
|  */ | ||||
|  | ||||
| #include <linux/errno.h> | ||||
| #include <linux/mm.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/delay.h> | ||||
|  | ||||
| #include <linux/zftape.h> | ||||
|  | ||||
| #include <linux/vmalloc.h> | ||||
|  | ||||
| #include "../zftape/zftape-init.h" | ||||
| #include "../zftape/zftape-eof.h" | ||||
| #include "../zftape/zftape-ctl.h" | ||||
| #include "../zftape/zftape-write.h" | ||||
| #include "../zftape/zftape-read.h" | ||||
| #include "../zftape/zftape-rw.h" | ||||
| #include "../zftape/zftape-vtbl.h" | ||||
|  | ||||
| /*  global variables | ||||
|  */ | ||||
|  | ||||
| /*  local varibales | ||||
|  */ | ||||
| static unsigned int used_memory; | ||||
| static unsigned int peak_memory; | ||||
|  | ||||
| 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, %zd bytes", *(void **)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, %zd bytes", | ||||
| 		      *(void **)old, size); | ||||
| 		*(void **)old = NULL; | ||||
| 	} | ||||
| 	TRACE_EXIT; | ||||
| } | ||||
|  | ||||
| void *zft_kmalloc(size_t size) | ||||
| { | ||||
| 	void *new; | ||||
|  | ||||
| 	while ((new = kmalloc(size, GFP_KERNEL)) == NULL) { | ||||
| 		msleep_interruptible(100); | ||||
| 	} | ||||
| 	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; | ||||
| } | ||||
| @@ -1,55 +0,0 @@ | ||||
| #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 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 | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,58 +0,0 @@ | ||||
| #ifndef _ZFTAPE_CTL_H | ||||
| #define _ZFTAPE_CTL_H | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 1996, 1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version.  | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-ctl.h,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:19:02 $ | ||||
|  * | ||||
|  *      This file contains the non-standard IOCTL related definitions | ||||
|  *      for the QIC-40/80 floppy-tape driver for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/ioctl.h> | ||||
| #include <linux/mtio.h> | ||||
|  | ||||
| #include "../zftape/zftape-rw.h" | ||||
|  | ||||
| #ifdef CONFIG_ZFTAPE_MODULE | ||||
| #define ftape_status (*zft_status) | ||||
| #endif | ||||
|  | ||||
| extern int zft_offline; | ||||
| extern int zft_mt_compression; | ||||
| extern int zft_write_protected; | ||||
| extern int zft_header_read; | ||||
| extern unsigned int zft_unit; | ||||
| extern int zft_resid; | ||||
|  | ||||
| extern void zft_reset_position(zft_position *pos); | ||||
| extern int  zft_check_write_access(zft_position *pos); | ||||
| extern int  zft_def_idle_state(void); | ||||
|  | ||||
| /*  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 __user *arg); | ||||
| #endif | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,199 +0,0 @@ | ||||
| /* | ||||
|  *   I use these routines just to decide when I have to fake a  | ||||
|  *   volume-table to preserve compatibility to original ftape. | ||||
|  */ | ||||
| /* | ||||
|  *      Copyright (C) 1994-1995 Bas Laarhoven. | ||||
|  *       | ||||
|  *      Modified for zftape 1996, 1997 Claus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-eof.c,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:19:02 $ | ||||
|  * | ||||
|  *      This file contains the eof mark handling code | ||||
|  *      for the QIC-40/80 floppy-tape driver for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/string.h> | ||||
| #include <linux/errno.h> | ||||
|  | ||||
| #include <linux/zftape.h> | ||||
|  | ||||
| #include "../zftape/zftape-init.h" | ||||
| #include "../zftape/zftape-rw.h" | ||||
| #include "../zftape/zftape-eof.h" | ||||
|  | ||||
| /*      Global vars. | ||||
|  */ | ||||
|  | ||||
| /* a copy of the failed sector log from the header segment. | ||||
|  */ | ||||
| eof_mark_union *zft_eof_map; | ||||
|  | ||||
| /* number of eof marks (entries in bad sector log) on tape. | ||||
|  */ | ||||
| int zft_nr_eof_marks = -1; | ||||
|  | ||||
|  | ||||
| /*      Local vars. | ||||
|  */ | ||||
|  | ||||
| static char linux_tape_label[] = "Linux raw format V"; | ||||
| enum {  | ||||
| 	min_fmt_version = 1, max_fmt_version = 2  | ||||
| }; | ||||
| static unsigned ftape_fmt_version = 0; | ||||
|  | ||||
|  | ||||
| /* Ftape (mis)uses the bad sector log to record end-of-file marks. | ||||
|  * Initially (when the tape is erased) all entries in the bad sector | ||||
|  * log are added to the tape's bad sector map. The bad sector log then | ||||
|  * is cleared. | ||||
|  * | ||||
|  * The bad sector log normally contains entries of the form:  | ||||
|  * even 16-bit word: segment number of bad sector  | ||||
|  * odd 16-bit word: encoded date | ||||
|  * There can be a total of 448 entries (1792 bytes). | ||||
|  * | ||||
|  * My guess is that no program is using this bad sector log (the * | ||||
|  * format seems useless as there is no indication of the bad sector | ||||
|  * itself, only the segment) However, if any program does use the bad | ||||
|  * sector log, the format used by ftape will let the program think | ||||
|  * there are some bad sectors and no harm is done. | ||||
|  *   | ||||
|  * The eof mark entries that ftape stores in the bad sector log: even | ||||
|  * 16-bit word: segment number of eof mark odd 16-bit word: sector | ||||
|  * number of eof mark [1..32] | ||||
|  *   | ||||
|  * The zft_eof_map as maintained is a sorted list of eof mark entries. | ||||
|  * | ||||
|  * | ||||
|  * The tape name field in the header segments is used to store a linux | ||||
|  * tape identification string and a version number.  This way the tape | ||||
|  * can be recognized as a Linux raw format tape when using tools under | ||||
|  * other OS's. | ||||
|  * | ||||
|  * 'Wide' QIC tapes (format code 4) don't have a failed sector list | ||||
|  * anymore. That space is used for the (longer) bad sector map that | ||||
|  * now is a variable length list too.  We now store our end-of-file | ||||
|  * marker list after the bad-sector-map on tape. The list is delimited | ||||
|  * by a (__u32) 0 entry. | ||||
|  */ | ||||
|  | ||||
| int zft_ftape_validate_label(char *label) | ||||
| { | ||||
| 	static char tmp_label[45]; | ||||
| 	int result = 0; | ||||
| 	TRACE_FUN(ft_t_any); | ||||
| 	 | ||||
| 	memcpy(tmp_label, label, FT_LABEL_SZ); | ||||
| 	tmp_label[FT_LABEL_SZ] = '\0'; | ||||
| 	TRACE(ft_t_noise, "tape  label = `%s'", tmp_label); | ||||
| 	ftape_fmt_version = 0; | ||||
| 	if (memcmp(label, linux_tape_label, strlen(linux_tape_label)) == 0) { | ||||
| 		int pos = strlen(linux_tape_label); | ||||
| 		while (label[pos] >= '0' && label[pos] <= '9') { | ||||
| 			ftape_fmt_version *= 10; | ||||
| 			ftape_fmt_version = label[ pos++] - '0'; | ||||
| 		} | ||||
| 		result = (ftape_fmt_version >= min_fmt_version && | ||||
| 			  ftape_fmt_version <= max_fmt_version); | ||||
| 	} | ||||
| 	TRACE(ft_t_noise, "format version = %d", ftape_fmt_version); | ||||
| 	TRACE_EXIT result; | ||||
| } | ||||
|  | ||||
| static __u8 * find_end_of_eof_list(__u8 * ptr, __u8 * limit) | ||||
| { | ||||
| 	while (ptr + 3 < limit) { | ||||
|  | ||||
| 		if (get_unaligned((__u32*)ptr)) { | ||||
| 			ptr += sizeof(__u32); | ||||
| 		} else { | ||||
| 			return ptr; | ||||
| 		} | ||||
| 	} | ||||
| 	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; | ||||
| } | ||||
| @@ -1,52 +0,0 @@ | ||||
| #ifndef _ZFTAPE_EOF_H | ||||
| #define _ZFTAPE_EOF_H | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 1994-1995 Bas Laarhoven. | ||||
|  * adaptaed for zftape 1996, 1997 by Claus Heine | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-eof.h,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:19:03 $ | ||||
|  * | ||||
|  *      Definitions and declarations for the end of file markers | ||||
|  *      for the QIC-40/80 floppy-tape driver for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/ftape-header-segment.h> | ||||
| #include "../zftape/zftape-buffers.h" | ||||
| /*  failed sector log size (only used if format code != 4). | ||||
|  */ | ||||
|  | ||||
| typedef union { | ||||
| 	ft_fsl_entry mark; | ||||
| 	__u32 entry; | ||||
| } eof_mark_union; | ||||
|   | ||||
| /*      ftape-eof.c defined global vars. | ||||
|  */ | ||||
| extern int zft_nr_eof_marks; | ||||
| extern eof_mark_union *zft_eof_map; | ||||
|  | ||||
| /*      ftape-eof.c defined global functions. | ||||
|  */ | ||||
| extern void zft_ftape_extract_file_marks(__u8* address); | ||||
| extern int  zft_ftape_validate_label(char* label); | ||||
| extern void zft_clear_ftape_file_marks(void); | ||||
|  | ||||
| #endif | ||||
| @@ -1,377 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1996, 1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  *      This file contains the code that registers the zftape frontend  | ||||
|  *      to the ftape floppy tape driver for Linux | ||||
|  */ | ||||
|  | ||||
| #include <linux/module.h> | ||||
| #include <linux/errno.h> | ||||
| #include <linux/fs.h> | ||||
| #include <linux/kernel.h> | ||||
| #include <linux/signal.h> | ||||
| #include <linux/major.h> | ||||
| #include <linux/slab.h> | ||||
| #ifdef CONFIG_KMOD | ||||
| #include <linux/kmod.h> | ||||
| #endif | ||||
| #include <linux/fcntl.h> | ||||
| #include <linux/smp_lock.h> | ||||
|  | ||||
| #include <linux/zftape.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/device.h> | ||||
|  | ||||
| #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" | ||||
|  | ||||
| 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"); | ||||
| MODULE_LICENSE("GPL"); | ||||
|  | ||||
| /*      Global vars. | ||||
|  */ | ||||
| struct zft_cmpr_ops *zft_cmpr_ops = NULL; | ||||
| const ftape_info *zft_status; | ||||
|  | ||||
| /*      Local vars. | ||||
|  */ | ||||
| static unsigned long busy_flag; | ||||
|  | ||||
| static sigset_t 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); | ||||
| static int zft_close(struct inode *ino, struct file *filep); | ||||
| static int  zft_ioctl(struct inode *ino, struct file *filep, | ||||
| 		      unsigned int command, unsigned long arg); | ||||
| static int  zft_mmap(struct file *filep, struct vm_area_struct *vma); | ||||
| static ssize_t zft_read (struct file *fp, char __user *buff, | ||||
| 			 size_t req_len, loff_t *ppos); | ||||
| static ssize_t zft_write(struct file *fp, const char __user *buff, | ||||
| 			 size_t req_len, loff_t *ppos); | ||||
|  | ||||
| static const struct file_operations zft_cdev = | ||||
| { | ||||
| 	.owner		= THIS_MODULE, | ||||
| 	.read		= zft_read, | ||||
| 	.write		= zft_write, | ||||
| 	.ioctl		= zft_ioctl, | ||||
| 	.mmap		= zft_mmap, | ||||
| 	.open		= zft_open, | ||||
| 	.release	= zft_close, | ||||
| }; | ||||
|  | ||||
| static struct class *zft_class; | ||||
|  | ||||
| /*      Open floppy tape device | ||||
|  */ | ||||
| static int zft_open(struct inode *ino, struct file *filep) | ||||
| { | ||||
| 	int result; | ||||
| 	TRACE_FUN(ft_t_flow); | ||||
|  | ||||
| 	nonseekable_open(ino, filep); | ||||
| 	TRACE(ft_t_flow, "called for minor %d", iminor(ino)); | ||||
| 	if ( test_and_set_bit(0,&busy_flag) ) { | ||||
| 		TRACE_ABORT(-EBUSY, ft_t_warn, "failed: already busy"); | ||||
| 	} | ||||
| 	if ((iminor(ino) & ~(ZFT_MINOR_OP_MASK | FTAPE_NO_REWIND)) | ||||
| 	     >  | ||||
| 	    FTAPE_SEL_D) { | ||||
| 		clear_bit(0,&busy_flag); | ||||
| 		TRACE_ABORT(-ENXIO, ft_t_err, "failed: invalid unit nr"); | ||||
| 	} | ||||
| 	orig_sigmask = current->blocked; | ||||
| 	sigfillset(¤t->blocked); | ||||
| 	result = _zft_open(iminor(ino), filep->f_flags & O_ACCMODE); | ||||
| 	if (result < 0) { | ||||
| 		current->blocked = orig_sigmask; /* restore mask */ | ||||
| 		clear_bit(0,&busy_flag); | ||||
| 		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; | ||||
| 		sigaddsetmask (¤t->blocked, _DO_BLOCK); | ||||
| 		TRACE_EXIT 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /*      Close floppy tape device | ||||
|  */ | ||||
| static int zft_close(struct inode *ino, struct file *filep) | ||||
| { | ||||
| 	int result; | ||||
| 	TRACE_FUN(ft_t_flow); | ||||
|  | ||||
| 	if ( !test_bit(0,&busy_flag) || iminor(ino) != zft_unit) { | ||||
| 		TRACE(ft_t_err, "failed: not busy or wrong unit"); | ||||
| 		TRACE_EXIT 0; | ||||
| 	} | ||||
| 	sigfillset(¤t->blocked); | ||||
| 	result = _zft_close(); | ||||
| 	if (result < 0) { | ||||
| 		TRACE(ft_t_err, "_zft_close failed"); | ||||
| 	} | ||||
| 	current->blocked = orig_sigmask; /* restore before open state */ | ||||
| 	clear_bit(0,&busy_flag); | ||||
| 	TRACE_EXIT 0; | ||||
| } | ||||
|  | ||||
| /*      Ioctl for floppy tape device | ||||
|  */ | ||||
| static int zft_ioctl(struct inode *ino, struct file *filep, | ||||
| 		     unsigned int command, unsigned long arg) | ||||
| { | ||||
| 	int result = -EIO; | ||||
| 	sigset_t old_sigmask; | ||||
| 	TRACE_FUN(ft_t_flow); | ||||
|  | ||||
| 	if ( !test_bit(0,&busy_flag) || iminor(ino) != zft_unit || ft_failure) { | ||||
| 		TRACE_ABORT(-EIO, ft_t_err, | ||||
| 			    "failed: not busy, failure or wrong unit"); | ||||
| 	} | ||||
| 	old_sigmask = current->blocked; /* save mask */ | ||||
| 	sigfillset(¤t->blocked); | ||||
| 	/* This will work as long as sizeof(void *) == sizeof(long) */ | ||||
| 	result = _zft_ioctl(command, (void __user *) arg); | ||||
| 	current->blocked = old_sigmask; /* restore mask */ | ||||
| 	TRACE_EXIT result; | ||||
| } | ||||
|  | ||||
| /*      Ioctl for floppy tape device | ||||
|  */ | ||||
| static int  zft_mmap(struct file *filep, struct vm_area_struct *vma) | ||||
| { | ||||
| 	int result = -EIO; | ||||
| 	sigset_t old_sigmask; | ||||
| 	TRACE_FUN(ft_t_flow); | ||||
|  | ||||
| 	if ( !test_bit(0,&busy_flag) ||  | ||||
| 	    iminor(filep->f_dentry->d_inode) != zft_unit ||  | ||||
| 	    ft_failure) | ||||
| 	{ | ||||
| 		TRACE_ABORT(-EIO, ft_t_err, | ||||
| 			    "failed: not busy, failure or wrong unit"); | ||||
| 	} | ||||
| 	old_sigmask = current->blocked; /* save mask */ | ||||
| 	sigfillset(¤t->blocked); | ||||
| 	if ((result = ftape_mmap(vma)) >= 0) { | ||||
| #ifndef MSYNC_BUG_WAS_FIXED | ||||
| 		static struct vm_operations_struct dummy = { NULL, }; | ||||
| 		vma->vm_ops = &dummy; | ||||
| #endif | ||||
| 	} | ||||
| 	current->blocked = old_sigmask; /* restore mask */ | ||||
| 	TRACE_EXIT result; | ||||
| } | ||||
|  | ||||
| /*      Read from floppy tape device | ||||
|  */ | ||||
| static ssize_t zft_read(struct file *fp, char __user *buff, | ||||
| 			size_t req_len, loff_t *ppos) | ||||
| { | ||||
| 	int result = -EIO; | ||||
| 	sigset_t old_sigmask; | ||||
| 	struct inode *ino = fp->f_dentry->d_inode; | ||||
| 	TRACE_FUN(ft_t_flow); | ||||
|  | ||||
| 	TRACE(ft_t_data_flow, "called with count: %ld", (unsigned long)req_len); | ||||
| 	if (!test_bit(0,&busy_flag)  || iminor(ino) != zft_unit || ft_failure) { | ||||
| 		TRACE_ABORT(-EIO, ft_t_err, | ||||
| 			    "failed: not busy, failure or wrong unit"); | ||||
| 	} | ||||
| 	old_sigmask = current->blocked; /* save mask */ | ||||
| 	sigfillset(¤t->blocked); | ||||
| 	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 | ||||
|  */ | ||||
| static ssize_t zft_write(struct file *fp, const char __user *buff, | ||||
| 			 size_t req_len, loff_t *ppos) | ||||
| { | ||||
| 	int result = -EIO; | ||||
| 	sigset_t old_sigmask; | ||||
| 	struct inode *ino = fp->f_dentry->d_inode; | ||||
| 	TRACE_FUN(ft_t_flow); | ||||
|  | ||||
| 	TRACE(ft_t_flow, "called with count: %ld", (unsigned long)req_len); | ||||
| 	if (!test_bit(0,&busy_flag) || iminor(ino) != zft_unit || ft_failure) { | ||||
| 		TRACE_ABORT(-EIO, ft_t_err, | ||||
| 			    "failed: not busy, failure or wrong unit"); | ||||
| 	} | ||||
| 	old_sigmask = current->blocked; /* save mask */ | ||||
| 	sigfillset(¤t->blocked); | ||||
| 	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; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /*  lock the zft-compressor() module. | ||||
|  */ | ||||
| int zft_cmpr_lock(int try_to_load) | ||||
| { | ||||
| 	if (zft_cmpr_ops == NULL) { | ||||
| #ifdef CONFIG_KMOD | ||||
| 		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 | ||||
|  */ | ||||
| int __init zft_init(void) | ||||
| { | ||||
| 	int i; | ||||
| 	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"); | ||||
|         } | ||||
| #else /* !MODULE */ | ||||
| 	/* print a short no-nonsense boot message */ | ||||
| 	printk(KERN_INFO ZFTAPE_VERSION "\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),); | ||||
|  | ||||
| 	zft_class = class_create(THIS_MODULE, "zft"); | ||||
| 	for (i = 0; i < 4; i++) { | ||||
| 		class_device_create(zft_class, NULL, MKDEV(QIC117_TAPE_MAJOR, i), NULL, "qft%i", i); | ||||
| 		class_device_create(zft_class, NULL, MKDEV(QIC117_TAPE_MAJOR, i + 4), NULL, "nqft%i", i); | ||||
| 		class_device_create(zft_class, NULL, MKDEV(QIC117_TAPE_MAJOR, i + 16), NULL, "zqft%i", i); | ||||
| 		class_device_create(zft_class, NULL, MKDEV(QIC117_TAPE_MAJOR, i + 20), NULL, "nzqft%i", i); | ||||
| 		class_device_create(zft_class, NULL, MKDEV(QIC117_TAPE_MAJOR, i + 32), NULL, "rawqft%i", i); | ||||
| 		class_device_create(zft_class, NULL, MKDEV(QIC117_TAPE_MAJOR, i + 36), NULL, "nrawrawqft%i", i); | ||||
| 	} | ||||
|  | ||||
| #ifdef CONFIG_ZFT_COMPRESSOR | ||||
| 	(void)zft_compressor_init(); | ||||
| #endif | ||||
| 	zft_status = ftape_get_status(); /*  fetch global data of ftape  | ||||
| 					  *  hardware driver  | ||||
| 					  */ | ||||
| 	TRACE_EXIT 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Called by modules package when removing the driver  | ||||
|  */ | ||||
| static void zft_exit(void) | ||||
| { | ||||
| 	int i; | ||||
| 	TRACE_FUN(ft_t_flow); | ||||
|  | ||||
| 	if (unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) { | ||||
| 		TRACE(ft_t_warn, "failed"); | ||||
| 	} else { | ||||
| 		TRACE(ft_t_info, "successful"); | ||||
| 	} | ||||
|         for (i = 0; i < 4; i++) { | ||||
| 		class_device_destroy(zft_class, MKDEV(QIC117_TAPE_MAJOR, i)); | ||||
| 		class_device_destroy(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 4)); | ||||
| 		class_device_destroy(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 16)); | ||||
| 		class_device_destroy(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 20)); | ||||
| 		class_device_destroy(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 32)); | ||||
| 		class_device_destroy(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 36)); | ||||
| 	} | ||||
| 	class_destroy(zft_class); | ||||
| 	zft_uninit_mem(); /* release remaining memory, if any */ | ||||
|         printk(KERN_INFO "zftape successfully unloaded.\n"); | ||||
| 	TRACE_EXIT; | ||||
| } | ||||
|  | ||||
| module_init(zft_init); | ||||
| module_exit(zft_exit); | ||||
| @@ -1,77 +0,0 @@ | ||||
| #ifndef _ZFTAPE_INIT_H | ||||
| #define _ZFTAPE_INIT_H | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 1996, 1997 Claus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-init.h,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:19:05 $ | ||||
|  * | ||||
|  * This file contains definitions and macro for the vfs  | ||||
|  * interface defined by zftape | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <linux/ftape-header-segment.h> | ||||
|  | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
| #include "../lowlevel/ftape-ctl.h" | ||||
| #include "../lowlevel/ftape-read.h" | ||||
| #include "../lowlevel/ftape-write.h" | ||||
| #include "../lowlevel/ftape-bsm.h" | ||||
| #include "../lowlevel/ftape-io.h" | ||||
| #include "../lowlevel/ftape-buffer.h" | ||||
| #include "../lowlevel/ftape-format.h" | ||||
|  | ||||
| #include "../zftape/zftape-rw.h" | ||||
|  | ||||
| #ifdef MODULE | ||||
| #define ftape_status (*zft_status) | ||||
| #endif | ||||
|  | ||||
| extern const  ftape_info *zft_status; /* needed for zftape-vtbl.h */ | ||||
|  | ||||
| #include "../zftape/zftape-vtbl.h" | ||||
|  | ||||
| struct zft_cmpr_ops { | ||||
| 	int (*write)(int *write_cnt, | ||||
| 		     __u8 *dst_buf, const int seg_sz, | ||||
| 		     const __u8 __user *src_buf, const int req_len,  | ||||
| 		     const zft_position *pos, const zft_volinfo *volume); | ||||
| 	int (*read)(int *read_cnt, | ||||
| 		    __u8 __user *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 int                  zft_cmpr_lock(int try_to_load); | ||||
|  | ||||
| #endif | ||||
|  | ||||
|  | ||||
| @@ -1,377 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1996, 1997 Claus-Justus Heine | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-read.c,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:19:06 $ | ||||
|  * | ||||
|  *      This file contains the high level reading code | ||||
|  *      for the QIC-117 floppy-tape driver for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/errno.h> | ||||
| #include <linux/mm.h> | ||||
|  | ||||
| #include <linux/zftape.h> | ||||
|  | ||||
| #include <asm/uaccess.h> | ||||
|  | ||||
| #include "../zftape/zftape-init.h" | ||||
| #include "../zftape/zftape-eof.h" | ||||
| #include "../zftape/zftape-ctl.h" | ||||
| #include "../zftape/zftape-write.h" | ||||
| #include "../zftape/zftape-read.h" | ||||
| #include "../zftape/zftape-rw.h" | ||||
| #include "../zftape/zftape-vtbl.h" | ||||
|  | ||||
| /*      Global vars. | ||||
|  */ | ||||
| int zft_just_before_eof; | ||||
|  | ||||
| /*      Local vars. | ||||
|  */ | ||||
| static int buf_len_rd; | ||||
|  | ||||
| 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  __user *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 (copy_to_user(dst_buf,  | ||||
| 			 src_buf + pos->seg_byte_pos, *read_cnt) != 0) { | ||||
| 		TRACE_EXIT -EFAULT; | ||||
| 	} | ||||
| 	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; | ||||
| 	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 __user *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 __user *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; | ||||
| } | ||||
| @@ -1,53 +0,0 @@ | ||||
| #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 __user *buff, int req_len); | ||||
|  | ||||
| #endif /* _ZFTAPE_READ_H */ | ||||
| @@ -1,375 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1996, 1997 Claus-Justus Heine | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-rw.c,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:19:08 $ | ||||
|  * | ||||
|  *      This file contains some common code for the r/w code for | ||||
|  *      zftape. | ||||
|  */ | ||||
|  | ||||
| #include <linux/errno.h> | ||||
| #include <linux/mm.h> | ||||
|  | ||||
| #include <linux/zftape.h> | ||||
| #include "../zftape/zftape-init.h" | ||||
| #include "../zftape/zftape-eof.h" | ||||
| #include "../zftape/zftape-ctl.h" | ||||
| #include "../zftape/zftape-write.h" | ||||
| #include "../zftape/zftape-read.h" | ||||
| #include "../zftape/zftape-rw.h" | ||||
| #include "../zftape/zftape-vtbl.h" | ||||
|  | ||||
| /*      Global vars. | ||||
|  */ | ||||
|  | ||||
| __u8 *zft_deblock_buf; | ||||
| __u8 *zft_hseg_buf; | ||||
| int zft_deblock_segment = -1; | ||||
| zft_status_enum zft_io_state = zft_idle; | ||||
| int zft_header_changed; | ||||
| int zft_qic113; /* conform to old specs. and old zftape */ | ||||
| int zft_use_compression; | ||||
| 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; | ||||
|  | ||||
| unsigned int zft_written_segments; | ||||
| int zft_label_changed; | ||||
|  | ||||
| /*      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; | ||||
| static __s64 tape_pos; | ||||
|  | ||||
| __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; | ||||
| } | ||||
| @@ -1,101 +0,0 @@ | ||||
| #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_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 */ | ||||
|  | ||||
| @@ -1,757 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (c) 1995-1997 Claus-Justus Heine  | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or | ||||
|  modify it under the terms of the GNU General Public License as | ||||
|  published by the Free Software Foundation; either version 2, or (at | ||||
|  your option) any later version. | ||||
|   | ||||
|  This program is distributed in the hope that it will be useful, but | ||||
|  WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  General Public License for more details. | ||||
|   | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, | ||||
|  USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.c,v $ | ||||
|  * $Revision: 1.7.6.1 $ | ||||
|  * $Date: 1997/11/24 13:48:31 $ | ||||
|  * | ||||
|  *      This file defines a volume table as defined in various QIC | ||||
|  *      standards. | ||||
|  *  | ||||
|  *      This is a minimal implementation, just allowing ordinary DOS | ||||
|  *      :( prgrams to identify the cartridge as used. | ||||
|  */ | ||||
|  | ||||
| #include <linux/errno.h> | ||||
| #include <linux/mm.h> | ||||
| #include <linux/slab.h> | ||||
|  | ||||
| #include <linux/zftape.h> | ||||
| #include "../zftape/zftape-init.h" | ||||
| #include "../zftape/zftape-eof.h" | ||||
| #include "../zftape/zftape-ctl.h" | ||||
| #include "../zftape/zftape-write.h" | ||||
| #include "../zftape/zftape-read.h" | ||||
| #include "../zftape/zftape-rw.h" | ||||
| #include "../zftape/zftape-vtbl.h" | ||||
|  | ||||
| #define ZFT_CMAP_HACK /* leave this defined to hide the compression map */ | ||||
|  | ||||
| /* | ||||
|  *  global variables  | ||||
|  */ | ||||
| int zft_qic_mode   = 1; /* use the vtbl */ | ||||
| int zft_old_ftape; /* prevents old ftaped tapes to be overwritten */ | ||||
| int zft_volume_table_changed; /* 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; | ||||
| static zft_volinfo  eot_vtbl; | ||||
| static zft_volinfo *cur_vtbl; | ||||
|  | ||||
| static 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 compatibility 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. | ||||
|  */ | ||||
| static 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 actually 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; | ||||
| } | ||||
| @@ -1,227 +0,0 @@ | ||||
| #ifndef _ZFTAPE_VTBL_H | ||||
| #define _ZFTAPE_VTBL_H | ||||
|  | ||||
| /* | ||||
|  *      Copyright (c) 1995-1997  Claus-Justus Heine | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or | ||||
|  modify it under the terms of the GNU General Public License as | ||||
|  published by the Free Software Foundation; either version 2, or (at | ||||
|  your option) any later version. | ||||
|   | ||||
|  This program is distributed in the hope that it will be useful, but | ||||
|  WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||
|  General Public License for more details. | ||||
|   | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, | ||||
|  USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.h,v $ | ||||
|  * $Revision: 1.3 $ | ||||
|  * $Date: 1997/10/28 14:30:09 $ | ||||
|  * | ||||
|  *      This file defines a volume table as defined in the QIC-80 | ||||
|  *      development standards. | ||||
|  */ | ||||
|  | ||||
| #include <linux/list.h> | ||||
|  | ||||
| #include "../lowlevel/ftape-tracing.h" | ||||
|  | ||||
| #include "../zftape/zftape-eof.h" | ||||
| #include "../zftape/zftape-ctl.h" | ||||
| #include "../zftape/zftape-rw.h" | ||||
|  | ||||
| #define VTBL_SIZE 128 /* bytes */ | ||||
|  | ||||
| /* The following are offsets in the vtbl.  */ | ||||
| #define VTBL_SIG   0 | ||||
| #define VTBL_START 4 | ||||
| #define VTBL_END   6 | ||||
| #define VTBL_DESC  8 | ||||
| #define VTBL_DATE  52 | ||||
| #define VTBL_FLAGS 56 | ||||
| #define VTBL_FL_VENDOR_SPECIFIC (1<<0) | ||||
| #define VTBL_FL_MUTLI_CARTRIDGE (1<<1) | ||||
| #define VTBL_FL_NOT_VERIFIED    (1<<2) | ||||
| #define VTBL_FL_REDIR_INHIBIT   (1<<3) | ||||
| #define VTBL_FL_SEG_SPANNING    (1<<4) | ||||
| #define VTBL_FL_DIRECTORY_LAST  (1<<5) | ||||
| #define VTBL_FL_RESERVED_6      (1<<6) | ||||
| #define VTBL_FL_RESERVED_7      (1<<7) | ||||
| #define VTBL_M_NO  57 | ||||
| #define VTBL_EXT   58 | ||||
| #define EXT_ZFTAPE_SIG     0 | ||||
| #define EXT_ZFTAPE_BLKSZ  10 | ||||
| #define EXT_ZFTAPE_CMAP   12 | ||||
| #define EXT_ZFTAPE_QIC113 13 | ||||
| #define VTBL_PWD   84 | ||||
| #define VTBL_DIR_SIZE 92 | ||||
| #define VTBL_DATA_SIZE 96 | ||||
| #define VTBL_OS_VERSION 104 | ||||
| #define VTBL_SRC_DRIVE  106 | ||||
| #define VTBL_DEV        122 | ||||
| #define VTBL_RESERVED_1 123 | ||||
| #define VTBL_CMPR       124 | ||||
| #define VTBL_CMPR_UNREG 0x3f | ||||
| #define VTBL_CMPR_USED  0x80 | ||||
| #define VTBL_FMT        125 | ||||
| #define VTBL_RESERVED_2 126 | ||||
| #define VTBL_RESERVED_3 127 | ||||
| /* compatibility 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];							\ | ||||
| 	strlcpy(tmp, desc, sizeof(tmp));				\ | ||||
| 	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 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); | ||||
|  | ||||
| static inline int   zft_tape_at_eod         (const zft_position *pos); | ||||
| static inline int   zft_tape_at_lbot        (const zft_position *pos); | ||||
| static inline void  zft_position_before_eof (zft_position *pos,  | ||||
| 					     const zft_volinfo *volume); | ||||
| static 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 handle fsfm/bsfm -- we | ||||
|  * need to position before the eof mark.  NOTE: zft_tape_pos is not | ||||
|  * changed  | ||||
|  */ | ||||
| static 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? | ||||
|  */ | ||||
| static 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; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static 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)  | ||||
|  */ | ||||
| static 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 */ | ||||
| @@ -1,483 +0,0 @@ | ||||
| /* | ||||
|  *      Copyright (C) 1996, 1997 Claus Heine | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-write.c,v $ | ||||
|  * $Revision: 1.3 $ | ||||
|  * $Date: 1997/11/06 00:50:29 $ | ||||
|  * | ||||
|  *      This file contains the writing code | ||||
|  *      for the QIC-117 floppy-tape driver for Linux. | ||||
|  */ | ||||
|  | ||||
| #include <linux/errno.h> | ||||
| #include <linux/mm.h> | ||||
|  | ||||
| #include <linux/zftape.h> | ||||
|  | ||||
| #include <asm/uaccess.h> | ||||
|  | ||||
| #include "../zftape/zftape-init.h" | ||||
| #include "../zftape/zftape-eof.h" | ||||
| #include "../zftape/zftape-ctl.h" | ||||
| #include "../zftape/zftape-write.h" | ||||
| #include "../zftape/zftape-read.h" | ||||
| #include "../zftape/zftape-rw.h" | ||||
| #include "../zftape/zftape-vtbl.h" | ||||
|  | ||||
| /*      Global vars. | ||||
|  */ | ||||
|  | ||||
| /*      Local vars. | ||||
|  */ | ||||
| static int last_write_failed; | ||||
| static int need_flush; | ||||
|  | ||||
| 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 __user *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 (copy_from_user(dst_buf + pos->seg_byte_pos, src_buf, *cnt) != 0) { | ||||
| 		TRACE_EXIT -EFAULT; | ||||
| 	} | ||||
| 	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 necessary */ | ||||
| 	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 __user *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 __user *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 */ | ||||
| } | ||||
| @@ -1,38 +0,0 @@ | ||||
| #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 __user *buff, int req_len); | ||||
| #endif /* _ZFTAPE_WRITE_H */ | ||||
| @@ -1,43 +0,0 @@ | ||||
| /* | ||||
|  *      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 symbols that the zftape frontend to  | ||||
|  *      the ftape floppy tape driver exports  | ||||
|  */		  | ||||
|  | ||||
| #include <linux/module.h> | ||||
|  | ||||
| #include <linux/zftape.h> | ||||
|  | ||||
| #include "../zftape/zftape-init.h" | ||||
| #include "../zftape/zftape-read.h" | ||||
| #include "../zftape/zftape-buffers.h" | ||||
| #include "../zftape/zftape-ctl.h" | ||||
|  | ||||
| /* zftape-init.c */ | ||||
| EXPORT_SYMBOL(zft_cmpr_register); | ||||
| /* zftape-read.c */ | ||||
| EXPORT_SYMBOL(zft_fetch_segment_fraction); | ||||
| /* zftape-buffers.c */ | ||||
| EXPORT_SYMBOL(zft_vmalloc_once); | ||||
| EXPORT_SYMBOL(zft_vmalloc_always); | ||||
| EXPORT_SYMBOL(zft_vfree); | ||||
| @@ -60,8 +60,6 @@ header-y += fadvise.h | ||||
| header-y += fd.h | ||||
| header-y += fdreg.h | ||||
| header-y += fib_rules.h | ||||
| header-y += ftape-header-segment.h | ||||
| header-y += ftape-vendors.h | ||||
| header-y += fuse.h | ||||
| header-y += futex.h | ||||
| header-y += genetlink.h | ||||
| @@ -206,7 +204,6 @@ unifdef-y += fcntl.h | ||||
| unifdef-y += filter.h | ||||
| unifdef-y += flat.h | ||||
| unifdef-y += fs.h | ||||
| unifdef-y += ftape.h | ||||
| unifdef-y += gameport.h | ||||
| unifdef-y += generic_serial.h | ||||
| unifdef-y += genhd.h | ||||
| @@ -341,6 +338,5 @@ unifdef-y += wait.h | ||||
| unifdef-y += wanrouter.h | ||||
| unifdef-y += watchdog.h | ||||
| unifdef-y += xfrm.h | ||||
| unifdef-y += zftape.h | ||||
|  | ||||
| objhdr-y += version.h | ||||
|   | ||||
| @@ -1,122 +0,0 @@ | ||||
| #ifndef _FTAPE_HEADER_SEGMENT_H | ||||
| #define _FTAPE_HEADER_SEGMENT_H | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 1996-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/include/linux/ftape-header-segment.h,v $ | ||||
|  * $Revision: 1.2 $ | ||||
|  * $Date: 1997/10/05 19:19:28 $ | ||||
|  * | ||||
|  *      This file defines some offsets into the header segment of a | ||||
|  *      floppy tape cartridge.  For use with the QIC-40/80/3010/3020 | ||||
|  *      floppy-tape driver "ftape" for Linux. | ||||
|  */ | ||||
|  | ||||
| #define FT_SIGNATURE   0  /* must be 0xaa55aa55 */ | ||||
| #define FT_FMT_CODE    4 | ||||
| #define FT_REV_LEVEL   5  /* only for QIC-80 since. Rev. L (== 0x0c)         */ | ||||
| #define FT_HSEG_1      6  /* first header segment, except for format code  6 */ | ||||
| #define FT_HSEG_2      8  /* second header segment, except for format code 6 */ | ||||
| #define FT_FRST_SEG   10  /* first data segment, except for format code 6    */ | ||||
| #define FT_LAST_SEG   12  /* last data segment, except for format code 6     */ | ||||
| #define FT_FMT_DATE   14  /* date and time of most recent format, see below  */ | ||||
| #define FT_WR_DATE    18  /* date and time of most recent write or format    */ | ||||
| #define FT_SPT        24  /* segments per track                              */ | ||||
| #define FT_TPC        26  /* tracks per cartridge                            */ | ||||
| #define FT_FHM        27  /* floppy drive head (maximum of it)               */ | ||||
| #define FT_FTM        28  /* floppy track max.                               */ | ||||
| #define FT_FSM        29  /* floppy sector max. (128)                        */ | ||||
| #define FT_LABEL      30  /* floppy tape label                               */ | ||||
| #define FT_LABEL_DATE 74  /* date and time the tape label was written        */ | ||||
| #define FT_LABEL_SZ   (FT_LABEL_DATE - FT_LABEL) | ||||
| #define FT_CMAP_START 78  /* starting segment of compression map             */ | ||||
| #define FT_FMT_ERROR 128  /* must be set to 0xff if remainder gets lost during | ||||
| 			   * tape format | ||||
| 			   */ | ||||
| #define FT_SEG_CNT   130  /* number of seg. written, formatted or verified | ||||
| 			   * through lifetime of tape (why not read?) | ||||
| 			   */ | ||||
| #define FT_INIT_DATE 138  /* date and time of initial tape format    */ | ||||
| #define FT_FMT_CNT   142  /* number of times tape has been formatted */ | ||||
| #define FT_FSL_CNT   144  /* number of segments in failed sector log */ | ||||
| #define FT_MK_CODE   146  /* id string of tape manufacturer          */ | ||||
| #define FT_LOT_CODE  190  /* tape manufacturer lot code              */ | ||||
| #define FT_6_HSEG_1  234  /* first header segment for format code  6 */ | ||||
| #define FT_6_HSEG_2  238  /* second header segment for format code 6 */ | ||||
| #define FT_6_FRST_SEG 242 /* first data segment for format code 6    */ | ||||
| #define FT_6_LAST_SEG 246 /* last data segment for format code 6     */ | ||||
|  | ||||
| #define FT_FSL        256 | ||||
| #define FT_HEADER_END 256 /* space beyond this point: | ||||
| 			   * format codes 2, 3 and 5:  | ||||
| 			   * -  failed sector log until byte 2047 | ||||
| 			   * -  bad sector map in the reamining part of segment | ||||
| 			   * format codes 4 and 6: | ||||
| 			   * -  bad sector map  starts hear | ||||
| 			   */ | ||||
|  | ||||
|  | ||||
| /*  value to be stored at the FT_SIGNATURE offset  | ||||
|  */ | ||||
| #define FT_HSEG_MAGIC 0xaa55aa55 | ||||
| #define FT_D2G_MAGIC  0x82288228 /* Ditto 2GB */ | ||||
|  | ||||
| /* data and time encoding: */ | ||||
| #define FT_YEAR_SHIFT 25 | ||||
| #define FT_YEAR_MASK  0xfe000000 | ||||
| #define FT_YEAR_0     1970 | ||||
| #define FT_YEAR_MAX   127 | ||||
| #define FT_YEAR(year) ((((year)-FT_YEAR_0)<<FT_YEAR_SHIFT)&FT_YEAR_MASK) | ||||
|  | ||||
| #define FT_TIME_SHIFT   0 | ||||
| #define FT_TIME_MASK    0x01FFFFFF | ||||
| #define FT_TIME_MAX     0x01ea6dff /* last second of a year */ | ||||
| #define FT_TIME(mo,d,h,m,s) \ | ||||
| 	((((s)+60*((m)+60*((h)+24*((d)+31*(mo))))) & FT_TIME_MASK)) | ||||
|  | ||||
| #define FT_TIME_STAMP(y,mo,d,h,m,s) (FT_YEAR(y) | FT_TIME(mo,d,h,m,s)) | ||||
|  | ||||
| /* values for the format code field */ | ||||
| typedef enum { | ||||
| 	fmt_normal = 2, /*  QIC-80 post Rev. B 205Ft or 307Ft tape    */ | ||||
| 	fmt_1100ft = 3, /*  QIC-80 post Rev. B 1100Ft tape            */ | ||||
| 	fmt_var    = 4, /*  QIC-80 post Rev. B variabel length format */ | ||||
| 	fmt_425ft  = 5, /*  QIC-80 post Rev. B 425Ft tape             */ | ||||
| 	fmt_big    = 6  /*  QIC-3010/3020 variable length tape with more  | ||||
| 			 *  than 2^16 segments per tape | ||||
| 			 */ | ||||
| } ft_format_type; | ||||
|  | ||||
| /* definitions for the failed sector log */ | ||||
| #define FT_FSL_SIZE        (2 * FT_SECTOR_SIZE - FT_HEADER_END) | ||||
| #define FT_FSL_MAX_ENTRIES (FT_FSL_SIZE/sizeof(__u32)) | ||||
|  | ||||
| typedef struct ft_fsl_entry { | ||||
| 	__u16 segment; | ||||
| 	__u16 date; | ||||
| } __attribute__ ((packed)) ft_fsl_entry; | ||||
|  | ||||
|  | ||||
| /*  date encoding for the failed sector log  | ||||
|  *  month: 1..12, day: 1..31, year: 1970..2097 | ||||
|  */ | ||||
| #define FT_FSL_TIME_STAMP(y,m,d) \ | ||||
| 	(((((y) - FT_YEAR_0)<<9)&0xfe00) | (((m)<<5)&0x01e0) | ((d)&0x001f)) | ||||
|  | ||||
| #endif /* _FTAPE_HEADER_SEGMENT_H */ | ||||
| @@ -1,137 +0,0 @@ | ||||
| #ifndef _FTAPE_VENDORS_H | ||||
| #define _FTAPE_VENDORS_H | ||||
|  | ||||
| /* | ||||
|  *      Copyright (C) 1993-1996 Bas Laarhoven, | ||||
|  *                (C) 1996-1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/include/linux/ftape-vendors.h,v $ | ||||
|  * $Revision: 1.6 $ | ||||
|  * $Date: 1997/10/09 15:38:11 $ | ||||
|  * | ||||
|  *      This file contains the supported drive types with their | ||||
|  *      QIC-117 spec. vendor code and drive dependent configuration | ||||
|  *      information. | ||||
|  */ | ||||
|  | ||||
| typedef enum { | ||||
| 	unknown_wake_up = 0, | ||||
| 	no_wake_up, | ||||
| 	wake_up_colorado, | ||||
| 	wake_up_mountain, | ||||
| 	wake_up_insight, | ||||
| } wake_up_types; | ||||
|  | ||||
| typedef struct { | ||||
| 	wake_up_types wake_up;	/* see wake_up_types */ | ||||
| 	char *name;		/* Text describing the drive */ | ||||
| } wakeup_method; | ||||
|  | ||||
| /*  Note: order of entries in WAKEUP_METHODS must be so that a variable | ||||
|  *        of type wake_up_types can be used as an index in the array. | ||||
|  */ | ||||
| #define WAKEUP_METHODS { \ | ||||
|   { unknown_wake_up,    "Unknown" }, \ | ||||
|   { no_wake_up,         "None" }, \ | ||||
|   { wake_up_colorado,   "Colorado" }, \ | ||||
|   { wake_up_mountain,   "Mountain" }, \ | ||||
|   { wake_up_insight,    "Motor-on" }, \ | ||||
| } | ||||
|  | ||||
| typedef struct { | ||||
| 	unsigned int vendor_id;	/* vendor id from drive */ | ||||
| 	int speed;		/* maximum tape transport speed (ips) */ | ||||
| 	wake_up_types wake_up;	/* see wake_up_types */ | ||||
| 	char *name;		/* Text describing the drive */ | ||||
| } vendor_struct; | ||||
|  | ||||
| #define UNKNOWN_VENDOR (-1) | ||||
|  | ||||
| #define QIC117_VENDORS {						    \ | ||||
| /* see _vendor_struct */						    \ | ||||
|   { 0x00000,  82, wake_up_colorado,  "Colorado DJ-10 (old)" },		    \ | ||||
|   { 0x00047,  90, wake_up_colorado,  "Colorado DJ-10/DJ-20" },		    \ | ||||
|   { 0x011c2,  84, wake_up_colorado,  "Colorado 700" },			    \ | ||||
|   { 0x011c3,  90, wake_up_colorado,  "Colorado 1400" },			    \ | ||||
|   { 0x011c4,  84, wake_up_colorado,  "Colorado DJ-10/DJ-20 (new)" },	    \ | ||||
|   { 0x011c5,  84, wake_up_colorado,  "HP Colorado T1000" },		    \ | ||||
|   { 0x011c6,  90, wake_up_colorado,  "HP Colorado T3000" },		    \ | ||||
|   { 0x00005,  45, wake_up_mountain,  "Archive 5580i" },			    \ | ||||
|   { 0x10005,  50, wake_up_insight,   "Insight 80Mb, Irwin 80SX" },	    \ | ||||
|   { 0x00140,  74, wake_up_mountain,  "Archive S.Hornet [Identity/Escom]" }, \ | ||||
|   { 0x00146,  72, wake_up_mountain,  "Archive 31250Q [Escom]" },	    \ | ||||
|   { 0x0014a, 100, wake_up_mountain,  "Archive XL9250i [Conner/Escom]" },    \ | ||||
|   { 0x0014c,  98, wake_up_mountain,  "Conner C250MQT" },		    \ | ||||
|   { 0x0014e,  80, wake_up_mountain,  "Conner C250MQ" },			    \ | ||||
|   { 0x00150,  80, wake_up_mountain,  "Conner TSM420R/TST800R" },	    \ | ||||
|   { 0x00152,  80, wake_up_mountain,  "Conner TSM850R" },		    \ | ||||
|   { 0x00156,  80, wake_up_mountain,  "Conner TSM850R/1700R/TST3200R" },	    \ | ||||
|   { 0x00180,   0, wake_up_mountain,  "Summit SE 150" },			    \ | ||||
|   { 0x00181,  85, wake_up_mountain,  "Summit SE 250, Mountain FS8000" },    \ | ||||
|   { 0x001c1,  82, no_wake_up,        "Wangtek 3040F" },			    \ | ||||
|   { 0x001c8,  64, no_wake_up,        "Wangtek 3080F" },			    \ | ||||
|   { 0x001c8,  64, wake_up_colorado,  "Wangtek 3080F" },			    \ | ||||
|   { 0x001ca,  67, no_wake_up,        "Wangtek 3080F (new)" },		    \ | ||||
|   { 0x001cc,  77, wake_up_colorado,  "Wangtek 3200 / Teac 700" },	    \ | ||||
|   { 0x001cd,  75, wake_up_colorado,  "Reveal TB1400" },			    \ | ||||
|   { 0x00380,  85, wake_up_colorado,  "Exabyte Eagle-96" },		    \ | ||||
|   { 0x00381,  85, wake_up_colorado,  "Exabyte Eagle TR-3" },		    \ | ||||
|   { 0x00382,  85, wake_up_colorado,  "Exabyte Eagle TR-3" },		    \ | ||||
|   { 0x003ce,  77, wake_up_colorado,  "Teac 800" },			    \ | ||||
|   { 0x003cf,   0, wake_up_colorado,  "Teac FT3010TR" },			    \ | ||||
|   { 0x08880,  64, no_wake_up,        "Iomega 250, Ditto 800" },		    \ | ||||
|   { 0x08880,  64, wake_up_colorado,  "Iomega 250, Ditto 800" },		    \ | ||||
|   { 0x08880,  64, wake_up_insight,   "Iomega 250, Ditto 800" },		    \ | ||||
|   { 0x08881,  80, wake_up_colorado,  "Iomega 700" },			    \ | ||||
|   { 0x08882,  80, wake_up_colorado,  "Iomega 3200" },			    \ | ||||
|   { 0x08883,  80, wake_up_colorado,  "Iomega DITTO 2GB" },		    \ | ||||
|   { 0x00021,  70, no_wake_up,        "AIWA CT-803" },			    \ | ||||
|   { 0x004c0,  80, no_wake_up,        "AIWA TD-S1600" },			    \ | ||||
|   { 0x00021,   0, wake_up_mountain,  "COREtape QIC80" },		    \ | ||||
|   { 0x00441,   0, wake_up_mountain,  "ComByte DoublePlay" },		    \ | ||||
|   { 0x00481, 127, wake_up_mountain,  "PERTEC MyTape 800" },		    \ | ||||
|   { 0x00483, 130, wake_up_mountain,  "PERTEC MyTape 3200" },		    \ | ||||
|   { UNKNOWN_VENDOR, 0, no_wake_up, "unknown" }				    \ | ||||
| } | ||||
|  | ||||
| #define QIC117_MAKE_CODES {			\ | ||||
|   { 0, "Unassigned" },				\ | ||||
|   { 1, "Alloy Computer Products" },		\ | ||||
|   { 2, "3M" },					\ | ||||
|   { 3, "Tandberg Data" },			\ | ||||
|   { 4, "Colorado" },				\ | ||||
|   { 5, "Archive/Conner" },			\ | ||||
|   { 6, "Mountain/Summit Memory Systems" },	\ | ||||
|   { 7, "Wangtek/Rexon/Tecmar" },		\ | ||||
|   { 8, "Sony" },				\ | ||||
|   { 9, "Cipher Data Products" },		\ | ||||
|   { 10, "Irwin Magnetic Systems" },		\ | ||||
|   { 11, "Braemar" },				\ | ||||
|   { 12, "Verbatim" },				\ | ||||
|   { 13, "Core International" },			\ | ||||
|   { 14, "Exabyte" },				\ | ||||
|   { 15, "Teac" },				\ | ||||
|   { 16, "Gigatek" },				\ | ||||
|   { 17, "ComByte" },				\ | ||||
|   { 18, "PERTEC Memories" },			\ | ||||
|   { 19, "Aiwa" },				\ | ||||
|   { 71, "Colorado" },				\ | ||||
|   { 546, "Iomega Inc" },			\ | ||||
| } | ||||
|  | ||||
| #endif /* _FTAPE_VENDORS_H */ | ||||
| @@ -1,201 +0,0 @@ | ||||
| #ifndef _FTAPE_H | ||||
| #define _FTAPE_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/include/linux/ftape.h,v $ | ||||
|  * $Revision: 1.17.6.4 $ | ||||
|  * $Date: 1997/11/25 01:52:54 $ | ||||
|  * | ||||
|  *      This file contains global definitions, typedefs and macro's | ||||
|  *      for the QIC-40/80/3010/3020 floppy-tape driver for Linux. | ||||
|  */ | ||||
|  | ||||
| #define FTAPE_VERSION "ftape v3.04d 25/11/97" | ||||
|  | ||||
| #ifdef __KERNEL__ | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/mm.h> | ||||
| #endif | ||||
| #include <linux/types.h> | ||||
| #include <linux/mtio.h> | ||||
|  | ||||
| #define FT_SECTOR(x)		(x+1)	/* sector offset into real sector */ | ||||
| #define FT_SECTOR_SIZE		1024 | ||||
| #define FT_SECTORS_PER_SEGMENT	  32 | ||||
| #define FT_ECC_SECTORS		   3 | ||||
| #define FT_SEGMENT_SIZE		((FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS) * FT_SECTOR_SIZE) | ||||
| #define FT_BUFF_SIZE    (FT_SECTORS_PER_SEGMENT * FT_SECTOR_SIZE) | ||||
|  | ||||
| /* | ||||
|  *   bits of the minor device number that define drive selection | ||||
|  *   methods. Could be used one day to access multiple tape | ||||
|  *   drives on the same controller. | ||||
|  */ | ||||
| #define FTAPE_SEL_A     0 | ||||
| #define FTAPE_SEL_B     1 | ||||
| #define FTAPE_SEL_C     2 | ||||
| #define FTAPE_SEL_D     3 | ||||
| #define FTAPE_SEL_MASK     3 | ||||
| #define FTAPE_SEL(unit) ((unit) & FTAPE_SEL_MASK) | ||||
| #define FTAPE_NO_REWIND 4	/* mask for minor nr */ | ||||
|  | ||||
| /* 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." | ||||
| #endif | ||||
| #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 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 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 CONFIG_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 defined(CONFIG_FT_ALT_FDC)  /* 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 NR_ITEMS(x)     (int)(sizeof(x)/ sizeof(*x)) | ||||
|  | ||||
| #endif  /* __KERNEL__ */ | ||||
|  | ||||
| #endif | ||||
| @@ -1,87 +0,0 @@ | ||||
| #ifndef _ZFTAPE_H | ||||
| #define _ZFTAPE_H | ||||
|  | ||||
| /* | ||||
|  * Copyright (C) 1996, 1997 Claus-Justus Heine. | ||||
|  | ||||
|  This program is free software; you can redistribute it and/or modify | ||||
|  it under the terms of the GNU General Public License as published by | ||||
|  the Free Software Foundation; either version 2, or (at your option) | ||||
|  any later version. | ||||
|  | ||||
|  This program is distributed in the hope that it will be useful, | ||||
|  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  GNU General Public License for more details. | ||||
|  | ||||
|  You should have received a copy of the GNU General Public License | ||||
|  along with this program; see the file COPYING.  If not, write to | ||||
|  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||||
|  | ||||
|  * | ||||
|  * $Source: /homes/cvs/ftape-stacked/include/linux/zftape.h,v $ | ||||
|  * $Revision: 1.12 $ | ||||
|  * $Date: 1997/10/21 11:02:37 $ | ||||
|  * | ||||
|  *      Special ioctl and other global info for the zftape VFS | ||||
|  *      interface for the QIC-40/80/3010/3020 floppy-tape driver for | ||||
|  *      Linux. | ||||
|  */ | ||||
|  | ||||
| #define ZFTAPE_VERSION  "zftape for " FTAPE_VERSION | ||||
|  | ||||
| #include <linux/ftape.h> | ||||
|  | ||||
| #define ZFTAPE_LABEL       "Ftape - The Linux Floppy Tape Project!" | ||||
|  | ||||
| /* Bits of the minor device number that control the operation mode */ | ||||
| #define ZFT_Q80_MODE		(1 << 3) | ||||
| #define ZFT_ZIP_MODE		(1 << 4) | ||||
| #define ZFT_RAW_MODE		(1 << 5) | ||||
| #define ZFT_MINOR_OP_MASK	(ZFT_Q80_MODE	| 	\ | ||||
| 				 ZFT_ZIP_MODE	| 	\ | ||||
| 				 ZFT_RAW_MODE) | ||||
| #define ZFT_MINOR_MASK		(FTAPE_SEL_MASK		|	\ | ||||
| 				 ZFT_MINOR_OP_MASK	|	\ | ||||
| 				 FTAPE_NO_REWIND) | ||||
|  | ||||
| #ifdef ZFT_OBSOLETE | ||||
| struct mtblksz { | ||||
| 	unsigned int mt_blksz; | ||||
| }; | ||||
| #define MTIOC_ZFTAPE_GETBLKSZ _IOR('m', 104, struct mtblksz) | ||||
| #endif | ||||
|  | ||||
| #ifdef __KERNEL__ | ||||
|  | ||||
| extern int zft_init(void); | ||||
|  | ||||
| static 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)); | ||||
| 	}  | ||||
| } | ||||
|  | ||||
| static 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 | ||||
		Reference in New Issue
	
	Block a user