Backdated import of mt-st version 0.7

This is an import of the mt-st upstream release 0.7 as it appeared in
the Debian archives.
This commit is contained in:
Kai Mäkisara
2001-11-21 23:14:29 +01:00
committed by Iustin Pop
commit fcb4fbe0d1
12 changed files with 3375 additions and 0 deletions

351
COPYING Normal file
View File

@@ -0,0 +1,351 @@
NOTE! This copyright does *not* cover user programs that use kernel
services by normal system calls - this is merely considered normal use
of the kernel, and does *not* fall under the heading of "derived work".
Also note that the GPL below is copyrighted by the Free Software
Foundation, but the instance of code that it refers to (the linux
kernel) is copyrighted by me and others who actually wrote it.
Linus Torvalds
----------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

25
Makefile Normal file
View File

@@ -0,0 +1,25 @@
CFLAGS= -Wall -O2
SBINDIR= /sbin
BINDIR= /bin
MANDIR= /usr/man
all: mt stinit
mt: mt.c
$(CC) $(CFLAGS) -o mt mt.c
stinit: stinit.c
$(CC) $(CFLAGS) -o stinit stinit.c
install: mt stinit
install -s mt $(BINDIR)
install -c -m 444 mt.1 $(MANDIR)/man1
install -s stinit $(SBINDIR)
install -c -m 444 stinit.8 $(MANDIR)/man8
dist: clean
(mydir=`basename \`pwd\``;\
cd .. && tar cvvf - $$mydir | gzip -9 > $${mydir}.tar.gz)
clean:
rm -f *~ \#*\# *.o mt stinit

83
README Normal file
View File

@@ -0,0 +1,83 @@
This directory contains two programs; mt and stinit. Mt is basically
a "standard" mt with additional commands to send the ioctls specific
to the Linux SCSI tape driver. The source supports all ioctls up to
kernel version 2.4.0 but it can also be compiled in kernels >= 2.0.x
(and hopefully with 1.2.x). Although this mt program is tailored for
SCSI tapes, it can also be used with other Linux tape drivers using the
same ioctls (some of the commands may not work with all drivers).
Stinit is a program to initialize the tape drive characteristics. The
current version should be considered alpha. See README.stinit for more
information.
The files:
README - This file.
README.stinit - Information about the stinit program
COPYING - The GNU Public License
Makefile - Makefile for programs
mt.c - The mt source
mt.1 - The man page for mt
mtio.h - The tape command definitions
qic117.h - Needed by mtio.h
stinit.c - The stinit source
stinit.8 - The man page for stinit
stinit.def.examples - example configurations for different devices
Installation:
- review the makefile
- make
- make install
Changes in version 0.7:
- add command eject for compatibility with GNU mt (synonym for offline
and rewoffl)
- the load and ersae commands accept an argument
- add CLN (cleaning request) to status
- add command stsetcln to set the cleaning request recognition options
- add the flag no-wait to the settable/clearable options
- some new density codes added
Changes in version 0.6:
- uses local mtio.h to include support for the most recent driver
features even when compiled on a system having old mtio.h
- on-line and write-protect are checked after some errors and a
message is printed if the probable error reason is found
- the tape is opened with flag O_NONBLOCK for commands that are
useful even when the device is not ready (no tape)
- some new density codes added for printout
- OnStream drives using the osst driver recognised
- the obsolete command 'datcompression' is removed
- new option --version in mt
Changes in version 0.5b:
- corrected the bug that caused the command argument to be ignored if
option -f was used
- added #include <errno.h> to stinit.c to enable compilation with glibc
- density 0x45 (TR-4) added to known density list
Changes in version 0.5:
- utility stinit added to package
- command asf added
- command datcompression not compiled in default configuration
- support added for setting timeouts
- bugs in argument parsing corrected
- help prints all commands
- binaries not distributed any more
- mt code cleaned
- GNU Public License used for both programs
Changes in version 0.4:
- support for the ioctls for partitioned tapes
- compiles also with 1.2.13
- the driver options can be specified also with keywords
- floppy tape type is shown
- (not working) support for other operating systems removed
Changes in version 0.3:
- support for new ioctls
- accepts hexadecimal numbers with prefix 0x
- the datcompression command improved (although it is being overrided
by the command compression using a new ioctl)
- bus fixes
November 8, 2001 Kai Makisara (email Kai.Makisara@metla.fi)

51
README.stinit Normal file
View File

@@ -0,0 +1,51 @@
The program stinit is meant for initializing of SCSI tape drive modes
at system startup, when the tape driver module is loaded, or when new
tape drivers are initialized using
echo "scsi add-single-device" x y z v >/proc/scsi/scsi
The parameters used in initialization of a tape drive are fetched from
a text file. The parameter file is indexed by the inquiry data
returned by the drive, i.e., the parameters are defined by the drive
manufacturer, model, etc. This means that the initialization for a
drive does not depend on its hardware address. A similar method is
used by most Unices either within the kernel or outside the kernel.
The contents of the configuration file and the command line parameters
are defined in the man page stinit.8. A sample configuration file
stinit.def.examples is included in this distribution. It can be used as
example when writing descriptions for the tape drives in a
system. NOTE that the examples by no means specify what are the
"correct" parameters for different types of devices.
The program is configured for maximum of 32 tapes and 4 modes (the
default Linux configuration). If the kernel is configured for
different number of tape modes, the definitions MAX_TAPES and
NBR_MODES in stinit.c should be configured accordingly. (With 8 bit
minor numbers NBR_MODES * MAX_TAPES == 128.)
The files:
stinit.c - the program source code
stinit.8 - the man page
stinit.def.examples - a file containing example definitions for
imaginary tape drives
Makefile - a sample makefile for the program
README.stinit - this file
Changes in version 0.7:
- the directory scanning for tape devices is restricted to files with
certain names in some directories to avoid triggering automatic
module loading for device that don't exist (original patch from
Philippe Troin)
- support for devfs (/dev/tapes) added
- logging bug fixes
- add setting the cleaning request parameter
- add setting the no-wait (immediate) bit
Changes in version 0.6:
- fix the bug with whitespace at the beginning of lines
- use O_NONBLOCK to open the tape (anticipate kernel change)
Initial version 0.5.
November 8, 2001 Kai M<>kisara <Kai.Makisara@metla.fi>

16
mt-st-0.7.lsm Normal file
View File

@@ -0,0 +1,16 @@
Begin4
Title: mt-st
Version: 0.7
Entered-date: 2001-11-21
Description: Includes a mt-like program supporting additional
commands using ioctls specific to the Linux SCSI tape driver (up
to kernel 2.4.15), and the program stinit to define the SCSI tape
devices in system startup scripts.
Keywords: tape SCSI
Author: Kai.Makisara@metla.fi (Kai Makisara)
Maintained-by: Kai.Makisara@metla.fi (Kai Makisara)
Primary-site: ftp.ibiblio.org /pub/Linux/system/backup
35 kB mt-st-0.7.tar.gz
0.7 kB mt-st-0.7.lsm
Copying-policy: GPL
End

264
mt.1 Normal file
View File

@@ -0,0 +1,264 @@
.TH MT 1 "November 2001" \" -*- nroff -*-
.SH NAME
mt \- control magnetic tape drive operation
.SH SYNOPSIS
.B mt
[\-h] [\-f device] operation [count] [arguments...]
.SH DESCRIPTION
This manual page documents the tape control program
.BR mt .
.B mt
performs the given
.IR operation ,
which must be one of the tape operations listed below, on a tape
drive. The commands can also be listed by running the program with the
.I \-h
option. The version of mt is printed with the
.I \-v
or
.I \-\-version
option. The path of the tape device to operate on can be given with
the
.I \-f
or
.I \-t
option. If neither of those options is given, and the environment
variable
.B TAPE
is set, it is used. Otherwise, a default device defined in the file
.I /usr/include/sys/mtio.h
is used.
.PP
Some operations optionally take an argument or repeat count, which can be given
after the operation name and defaults to 1.
.PP
The available operations are listed below. Unique abbreviations are
accepted. Not all operations are available on all systems, or work on
all types of tape drives.
.IP fsf
Forward space
.I count
files.
The tape is positioned on the first block of the next file.
.IP fsfm
Forward space
.I count
files.
The tape is positioned on the last block of the previous file.
.IP bsf
Backward space
.I count
files.
The tape is positioned on the last block of the previous file.
.IP bsfm
Backward space
.I count
files.
The tape is positioned on the first block of the next file.
.IP asf
The tape is positioned at the beginning of the
.I count
file. Positioning is done by first rewinding the tape and then spacing
forward over
.I count
filemarks.
.IP fsr
Forward space
.I count
records.
.IP bsr
Backward space
.I count
records.
.IP fss
(SCSI tapes) Forward space
.I count
setmarks.
.IP bss
(SCSI tapes) Backward space
.I count
setmarks.
.IP "eod, seod"
Space to end of valid data. Used on streamer tape
drives to append data to the logical and of tape.
.IP rewind
Rewind the tape.
.IP "offline, rewoffl, eject"
Rewind the tape and, if applicable, unload the tape.
.IP retension
Rewind the tape, then wind it to the end of the reel,
then rewind it again.
.IP "weof, eof"
Write
.I count
EOF marks at current position.
.IP "wset"
(SCSI tapes) Write
.I count
setmarks at current position (only SCSI tape).
.IP erase
Erase the tape.
.IP status
Print status information about the tape unit. (If the density code is
"no translation" in the status output, this does not affect working of the
tape drive.)
.IP seek
(SCSI tapes) Seek to the
.I count
block on the tape. This operation is available on some
Tandberg and Wangtek streamers and some SCSI-2 tape drives. The block
address should be obtained from a
.I tell
call earlier.
.IP tell
(SCSI tapes) Tell the current block on tape. This operation is available on some
Tandberg and Wangtek streamers and some SCSI-2 tape drives.
.IP setpartition
(SCSI tapes) Switch to the partition determined by
.I count.
The default data partition of the tape is numbered zero. Switching
partition is available only if enabled for the device, the device
supports multiple partitions, and the tape is formatted with multiple
partitions.
.IP partseek
(SCSI tapes) The tape position is set to block
.I count
in the partition given by the argument after count. The default
partition is zero.
.IP mkpartition
(SCSI tapes) Format the tape with one (count is zero) or two partitions
(count gives the size of the second partition in megabytes). The tape
drive must be able to format partitioned tapes with
initiator-specified partition size and partition support
must be enabled for the drive.
.IP load
(SCSI tapes) Send the load command to the tape drive. The drives usually load the
tape when a new cartridge is inserted. The argument
.I count
can usually be omitted. Some HP changers load tape n if the
.I count
10000 + n is given (a special funtion in the Linux st driver).
.IP lock
(SCSI tapes) Lock the tape drive door.
.IP unlock
(SCSI tapes) Unlock the tape drive door.
.IP setblk
(SCSI tapes) Set the block size of the drive to
.I count
bytes per record.
.IP setdensity
(SCSI tapes) Set the tape density code to
.I count.
The proper codes to use with each drive should be looked up from the
drive documentation.
.IP densities
(SCSI tapes) Write explanation of some common density codes to
standard output.
.IP drvbuffer
(SCSI tapes) Set the tape drive buffer code to
.I number.
The proper value for unbuffered operation is zero and "normal" buffered
operation one. The meanings of other values can be found in the drive
documentation or, in case of a SCSI-2 drive, from the SCSI-2 standard.
.IP compression
(SCSI tapes) The compression within the drive can be switched on or
off using the MTCOMPRESSION ioctl. Note that this method is not
supported by all drives implementing compression. For instance, the
Exabyte 8 mm drives use density codes to select compression.
.IP stoptions
(SCSI tapes) Set the driver options bits for the device to the defined
values. Allowed only for the superuser. The bits can be set
either by oring the option bits from the file /usr/include/linux/mtio.h to
.I count,
or by using the following keywords (as many keywords can be used on
the same line as necessary, unambiguous abbreviations allowed):
.RS
.IP buffer-writes 15
buffered writes enabled
.IP async-writes
asynchronous writes enabled
.IP read-ahead
read-ahead for fixed block size
.IP debug
debugging (if compiled into driver)
.IP two-fms
write two filemarks when file closed
.IP fast-eod
space directly to eod (and lose file number)
.IP no-wait
don't wait until rewind, etc. complete
.IP auto-lock
automatically lock/unlock drive door
.IP def-writes
the block size and density are for writes
.IP can-bsr
drive can space backwards well
.IP no-blklimits
drive doesn't support read block limits
.IP can-partitions
drive can handle partitioned tapes
.IP scsi2logical
seek and tell use SCSI-2 logical block addresses instead of device
dependent addresses
.IP sysv
enable the System V semantics
.RE
.IP stsetoptions
(SCSI tapes) Set selected driver options bits. The methods to specify
the bits to set are given above in description of stoptions.
Allowed only for the superuser.
.IP stclearoptions
(SCSI tapes) Clear selected driver option bits. The methods to specify
the bits to clear are given above in description of stoptions.
Allowed only for the superuser.
.IP stwrthreshold
(SCSI tapes) The write threshold for the tape device is set to
.I count
kilobytes. The value must be smaller than or equal to the driver
buffer size. Allowed only for the superuser.
.IP defblksize
(SCSI tapes) Set the default block size of the device to
.I count
bytes. The value -1 disables the default block size.
The block size set by
.I setblk
overrides the default until a new tape is inserted.
Allowed only for the superuser.
.IP defdensity
(SCSI tapes) Set the default density code. The value -1 disables the
default density. The density set by
.I setdensity
overrides the default until a new tape is inserted. Allowed only for the
superuser.
.IP defdrvbuffer
(SCSI tapes) Set the default drive buffer code. The value -1 disables the
default drive buffer code. The drive buffer code set by
.I drvbuffer
overrides the default until a new tape is inserted. Allowed only for the
superuser.
.IP defcompression
(SCSI tapes) Set the default compression state. The value -1 disables the
default compression. The compression state set by
.I compression
overrides the default until a new tape is inserted. Allowed only for the
superuser.
.IP sttimeout
sets the normal timeout for the device. The value is given in
seconds. Allowed only for the superuser.
.IP stlongtimeout
sets the long timeout for the device. The value is given in
seconds. Allowed only for the superuser.
.IP stsetcln
set the cleaning request interpretation parameters.
.PP
.B mt
exits with a status of 0 if the operation succeeded, 1 if the
operation or device name given was invalid, or 2 if the operation
failed.
.SH AUTHOR
The program is written by Kai Makisara <Kai.Makisara@metla.fi>.
.SH COPYRIGHT
The program and the manual page are copyrighted by Kai Makisara, 1998-2001.
They can be distributed according to the GNU Copyleft.
.SH SEE ALSO
st(4)

701
mt.c Normal file
View File

@@ -0,0 +1,701 @@
/* This file contains the source of the 'mt' program intended for
Linux systems. The program supports the basic mt commands found
in most Unix-like systems. In addition to this the program
supports several commands designed for use with the Linux SCSI
tape drive.
Maintained by Kai M<>kisara (email Kai.Makisara@metla.fi)
Copyright by Kai M<>kisara, 1998 - 2001. The program may be distributed
according to the GNU Public License
Last Modified: Wed Nov 21 23:14:29 2001 by makisara@kai.makisara.local
*/
#ifndef lint
static char rcsid[] = "$Id: /home/makisara/src/sys/mt-st-0.7/mt.c at Wed Nov 21 23:14:29 2001 by makisara@kai.makisara.local$";
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include "mtio.h"
#ifndef DEFTAPE
#define DEFTAPE "/dev/tape" /* default tape device */
#endif /* DEFTAPE */
#define VERSION "0.7"
typedef int (* cmdfunc)(/* int, struct cmdef_tr *, int, char ** */);
typedef struct cmdef_tr {
char *cmd_name;
int cmd_code;
cmdfunc cmd_function;
int cmd_count_bits;
unsigned char cmd_fdtype;
unsigned char arg_cnt;
int error_tests;
} cmdef_tr;
#define NO_FD 0
#define FD_RDONLY 1
#define FD_RDWR 2
#define NO_ARGS 0
#define ONE_ARG 1
#define TWO_ARGS 2
#define MULTIPLE_ARGS 255
#define DO_BOOLEANS 1002
#define SET_BOOLEANS 1003
#define CLEAR_BOOLEANS 1004
#define ET_ONLINE 1
#define ET_WPROT 2
static void usage(int);
static int do_standard(int, cmdef_tr *, int, char **);
static int do_drvbuffer(int, cmdef_tr *, int, char **);
static int do_options(int, cmdef_tr *, int, char **);
static int do_tell(int, cmdef_tr *, int, char **);
static int do_partseek(int, cmdef_tr *, int, char **);
static int do_status(int, cmdef_tr *, int, char **);
static int print_densities(int, cmdef_tr *, int, char **);
static int do_asf(int, cmdef_tr *, int, char **);
static void test_error(int, cmdef_tr *);
static cmdef_tr cmds[] = {
{ "weof", MTWEOF, do_standard, 0, FD_RDWR, ONE_ARG,
ET_ONLINE | ET_WPROT },
{ "wset", MTWSM, do_standard, 0, FD_RDWR, ONE_ARG,
ET_ONLINE | ET_WPROT },
{ "eof", MTWEOF, do_standard, 0, FD_RDWR, ONE_ARG,
ET_ONLINE },
{ "fsf", MTFSF, do_standard, 0, FD_RDONLY, ONE_ARG,
ET_ONLINE },
{ "fsfm", MTFSFM, do_standard, 0, FD_RDONLY, ONE_ARG,
ET_ONLINE },
{ "bsf", MTBSF, do_standard, 0, FD_RDONLY, ONE_ARG,
ET_ONLINE },
{ "bsfm", MTBSFM, do_standard, 0, FD_RDONLY, ONE_ARG,
ET_ONLINE },
{ "fsr", MTFSR, do_standard, 0, FD_RDONLY, ONE_ARG,
ET_ONLINE },
{ "bsr", MTBSR, do_standard, 0, FD_RDONLY, ONE_ARG,
ET_ONLINE },
{ "fss", MTFSS, do_standard, 0, FD_RDONLY, ONE_ARG,
ET_ONLINE },
{ "bss", MTBSS, do_standard, 0, FD_RDONLY, ONE_ARG,
ET_ONLINE },
{ "rewind", MTREW, do_standard, 0, FD_RDONLY, NO_ARGS,
ET_ONLINE },
{ "offline", MTOFFL, do_standard, 0, FD_RDONLY, NO_ARGS,
ET_ONLINE },
{ "rewoffl", MTOFFL, do_standard, 0, FD_RDONLY, NO_ARGS,
ET_ONLINE },
{ "eject", MTOFFL, do_standard, 0, FD_RDONLY, NO_ARGS,
ET_ONLINE },
{ "retension", MTRETEN, do_standard, 0, FD_RDONLY, NO_ARGS,
ET_ONLINE },
{ "eod", MTEOM, do_standard, 0, FD_RDONLY, NO_ARGS,
ET_ONLINE },
{ "seod", MTEOM, do_standard, 0, FD_RDONLY, NO_ARGS,
ET_ONLINE },
{ "seek", MTSEEK, do_standard, 0, FD_RDONLY, ONE_ARG,
ET_ONLINE },
{ "tell", MTTELL, do_tell, 0, FD_RDONLY, NO_ARGS,
ET_ONLINE },
{ "status", MTNOP, do_status, 0, FD_RDONLY, NO_ARGS,
0 },
{ "erase", MTERASE, do_standard, 0, FD_RDWR, ONE_ARG,
ET_ONLINE },
{ "setblk", MTSETBLK, do_standard, 0, FD_RDONLY, ONE_ARG,
0 },
{ "lock", MTLOCK, do_standard, 0, FD_RDONLY, NO_ARGS,
ET_ONLINE },
{ "unlock", MTUNLOCK, do_standard, 0, FD_RDONLY, NO_ARGS,
ET_ONLINE },
{ "load", MTLOAD, do_standard, 0, FD_RDONLY, ONE_ARG,
0 },
{ "compression", MTCOMPRESSION, do_standard, 0, FD_RDONLY, ONE_ARG,
0 },
{ "setdensity", MTSETDENSITY, do_standard, 0, FD_RDONLY, ONE_ARG,
0 },
{ "drvbuffer", MTSETDRVBUFFER, do_drvbuffer, 0, FD_RDONLY, ONE_ARG,
0 },
{ "stwrthreshold", MTSETDRVBUFFER, do_drvbuffer, MT_ST_WRITE_THRESHOLD,
FD_RDONLY, ONE_ARG, 0},
{ "stoptions", DO_BOOLEANS, do_options, 0, FD_RDONLY,
MULTIPLE_ARGS, 0},
{ "stsetoptions", SET_BOOLEANS, do_options, 0, FD_RDONLY,
MULTIPLE_ARGS, 0},
{ "stclearoptions", CLEAR_BOOLEANS, do_options, 0, FD_RDONLY,
MULTIPLE_ARGS, 0},
{ "defblksize", MTSETDRVBUFFER, do_drvbuffer, MT_ST_DEF_BLKSIZE,
FD_RDONLY, ONE_ARG, 0},
{ "defdensity", MTSETDRVBUFFER, do_drvbuffer, MT_ST_DEF_DENSITY,
FD_RDONLY, ONE_ARG, 0},
{ "defdrvbuffer", MTSETDRVBUFFER, do_drvbuffer, MT_ST_DEF_DRVBUFFER,
FD_RDONLY, ONE_ARG, 0},
{ "defcompression", MTSETDRVBUFFER, do_drvbuffer, MT_ST_DEF_COMPRESSION,
FD_RDONLY, ONE_ARG, 0},
{ "stsetcln", MTSETDRVBUFFER, do_drvbuffer, MT_ST_SET_CLN,
FD_RDONLY, ONE_ARG, 0},
{ "sttimeout", MTSETDRVBUFFER, do_drvbuffer, MT_ST_SET_TIMEOUT,
FD_RDONLY, ONE_ARG, 0},
{ "stlongtimeout", MTSETDRVBUFFER, do_drvbuffer, MT_ST_SET_LONG_TIMEOUT,
FD_RDONLY, ONE_ARG, 0},
{ "densities", 0, print_densities, 0, NO_FD, NO_ARGS,
0 },
{ "setpartition", MTSETPART, do_standard, 0, FD_RDONLY, ONE_ARG,
ET_ONLINE },
{ "mkpartition", MTMKPART, do_standard, 0, FD_RDWR, ONE_ARG,
ET_ONLINE },
{ "partseek", 0, do_partseek, 0, FD_RDONLY, TWO_ARGS,
ET_ONLINE },
{ "asf", 0, do_asf, MTREW, FD_RDONLY, ONE_ARG,
ET_ONLINE },
{ NULL, 0, 0, 0 }
};
static struct densities {
int code;
char *name;
} density_tbl[] = {
{0x00, "default"},
{0x01, "NRZI (800 bpi)"},
{0x02, "PE (1600 bpi)"},
{0x03, "GCR (6250 bpi)"},
{0x04, "QIC-11"},
{0x05, "QIC-45/60 (GCR, 8000 bpi)"},
{0x06, "PE (3200 bpi)"},
{0x07, "IMFM (6400 bpi)"},
{0x08, "GCR (8000 bpi)"},
{0x09, "GCR (37871 bpi)"},
{0x0a, "MFM (6667 bpi)"},
{0x0b, "PE (1600 bpi)"},
{0x0c, "GCR (12960 bpi)"},
{0x0d, "GCR (25380 bpi)"},
{0x0f, "QIC-120 (GCR 10000 bpi)"},
{0x10, "QIC-150/250 (GCR 10000 bpi)"},
{0x11, "QIC-320/525 (GCR 16000 bpi)"},
{0x12, "QIC-1350 (RLL 51667 bpi)"},
{0x13, "DDS (61000 bpi)"},
{0x14, "EXB-8200 (RLL 43245 bpi)"},
{0x15, "EXB-8500 or QIC-1000"},
{0x16, "MFM 10000 bpi"},
{0x17, "MFM 42500 bpi"},
{0x18, "TZ86"},
{0x19, "DLT 10GB"},
{0x1a, "DLT 20GB"},
{0x1b, "DLT 35GB"},
{0x1c, "QIC-385M"},
{0x1d, "QIC-410M"},
{0x1e, "QIC-1000C"},
{0x1f, "QIC-2100C"},
{0x20, "QIC-6GB"},
{0x21, "QIC-20GB"},
{0x22, "QIC-2GB"},
{0x23, "QIC-875"},
{0x24, "DDS-2"},
{0x25, "DDS-3"},
{0x26, "DDS-4 or QIC-4GB"},
{0x27, "Exabyte Mammoth"},
{0x28, "Exabyte Mammoth-2"},
{0x29, "QIC-3080MC"},
{0x30, "AIT-1 or MLR3"},
{0x31, "AIT-2"},
{0x33, "SLR6"},
{0x34, "SLR100"},
{0x40, "DLT1 40 GB, or Ultrium"},
{0x41, "DLT 40GB"},
{0x45, "QIC-3095-MC (TR-4)"},
{0x47, "TR-5"},
{0x80, "DLT 15GB uncomp. or Ecrix"},
{0x81, "DLT 15GB compressed"},
{0x82, "DLT 20GB uncompressed"},
{0x83, "DLT 20GB compressed"},
{0x84, "DLT 35GB uncompressed"},
{0x85, "DLT 35GB compressed"},
{0x86, "DLT1 40 GB uncompressed"},
{0x87, "DLT1 40 GB compressed"},
{0x88, "DLT 40GB uncompressed"},
{0x89, "DLT 40GB compressed"},
{0x8c, "EXB-8505 compressed"},
{0x90, "EXB-8205 compressed"}
};
#define NBR_DENSITIES (sizeof(density_tbl) / sizeof(struct densities))
static struct booleans {
char *name;
unsigned long bitmask;
char *expl;
} boolean_tbl[] = {
{"buffer-writes", MT_ST_BUFFER_WRITES, "buffered writes"},
{"async-writes", MT_ST_ASYNC_WRITES, "asynchronous writes"},
{"read-ahead", MT_ST_READ_AHEAD, "read-ahead for fixed block size"},
{"debug", MT_ST_DEBUGGING, "debugging (if compiled into driver)"},
{"two-fms", MT_ST_TWO_FM, "write two filemarks when file closed"},
{"fast-eod", MT_ST_FAST_MTEOM, "space directly to eod (and lose file number)"},
{"auto-lock", MT_ST_AUTO_LOCK, "automatically lock/unlock drive door"},
{"def-writes", MT_ST_DEF_WRITES, "the block size and density are for writes"},
{"can-bsr", MT_ST_CAN_BSR, "drive can space backwards well"},
{"no-blklimits", MT_ST_NO_BLKLIMS, "drive doesn't support read block limits"},
{"can-partitions",MT_ST_CAN_PARTITIONS,"drive can handle partitioned tapes"},
{"scsi2logical", MT_ST_SCSI2LOGICAL, "logical block addresses used with SCSI-2"},
{"no-wait", MT_ST_NOWAIT, "immediate mode for rewind, etc."},
#ifdef MT_ST_SYSV
{"sysv", MT_ST_SYSV, "enable the SystemV semantics"},
#endif
{NULL, 0}};
static char *tape_name; /* The tape name for messages */
int
main(int argc, char **argv)
{
int mtfd, cmd_code, i, argn, len, oflags;
char *cmdstr;
cmdef_tr *comp, *comp2;
for (argn=1; argn < argc; argn++)
if (*argv[argn] == '-')
switch (*(argv[argn] + 1)) {
case 'f':
case 't':
argn += 1;
if (argn >= argc) {
usage(0);
exit(1);
}
tape_name = argv[argn];
break;
case 'h':
usage(1);
exit(0);
break;
case 'v':
printf("mt-st v. %s\n", VERSION);
exit(0);
break;
case '-':
if (*(argv[argn] + 1) == '-' &&
*(argv[argn] + 2) == 'v') {
printf("mt-st v. %s\n", VERSION);
exit(0);
}
/* Fall through */
default:
usage(0);
exit(1);
}
else
break;
if (tape_name == NULL && (tape_name = getenv("TAPE")) == NULL)
tape_name = DEFTAPE;
if (argn >= argc ) {
usage(0);
exit(1);
}
cmdstr = argv[argn++];
len = strlen(cmdstr);
for (comp = cmds; comp->cmd_name != NULL; comp++)
if (strncmp(cmdstr, comp->cmd_name, len) == 0)
break;
if (comp->cmd_name == NULL) {
fprintf(stderr, "mt: unknown command \"%s\"\n", cmdstr);
usage(1);
exit(1);
}
if (len != strlen(comp->cmd_name)) {
for (comp2 = comp + 1; comp2->cmd_name != NULL; comp2++)
if (strncmp(cmdstr, comp2->cmd_name, len) == 0)
break;
if (comp2->cmd_name != NULL) {
fprintf(stderr, "mt: ambiguous command \"%s\"\n", cmdstr);
usage(1);
exit(1);
}
}
if (comp->arg_cnt != MULTIPLE_ARGS && comp->arg_cnt < argc - argn) {
fprintf(stderr, "mt: too many arguments for the command '%s'.\n",
comp->cmd_name);
exit(1);
}
cmd_code = comp->cmd_code;
if (comp->cmd_fdtype != NO_FD) {
oflags = comp->cmd_fdtype == FD_RDONLY ? O_RDONLY : O_RDWR;
if ((comp->error_tests & ET_ONLINE) == 0)
oflags |= O_NONBLOCK;
if ((mtfd = open(tape_name, oflags)) < 0) {
perror(tape_name);
exit(1);
}
}
else
mtfd = (-1);
if (comp->cmd_function != NULL) {
i = comp->cmd_function(mtfd, comp, argc - argn,
(argc - argn > 0 ? argv + argn : NULL));
if (i) {
if (errno == ENOSYS)
fprintf(stderr, "mt: Command not supported by this kernel.\n");
else if (comp->error_tests != 0)
test_error(mtfd, comp);
}
}
else {
fprintf(stderr, "mt: Internal error: command without function.\n");
i = 1;
}
if (mtfd >= 0)
close(mtfd);
return i;
}
static void
usage(int explain)
{
int ind;
char line[100];
fprintf(stderr, "usage: mt [-v] [--version] [-h] [ -f device ] command [ count ]\n");
if (explain) {
for (ind=0; cmds[ind].cmd_name != NULL; ) {
if (ind == 0)
strcpy(line, "commands: ");
else
strcpy(line, " ");
for ( ; cmds[ind].cmd_name != NULL; ind++) {
strcat(line, cmds[ind].cmd_name);
if (cmds[ind+1].cmd_name != NULL)
strcat(line, ", ");
else
strcat(line, ".");
if (strlen(line) >= 70 || cmds[ind+1].cmd_name == NULL) {
fprintf(stderr, "%s\n", line);
ind++;
break;
}
}
}
}
}
/* Do a command that simply feeds an argument to the MTIOCTOP ioctl */
static int
do_standard(int mtfd, cmdef_tr *cmd, int argc, char **argv)
{
struct mtop mt_com;
mt_com.mt_op = cmd->cmd_code;
mt_com.mt_count = (argc > 0 ? strtol(*argv, NULL, 0) : 1);
mt_com.mt_count |= cmd->cmd_count_bits;
if (mt_com.mt_count < 0) {
fprintf(stderr, "mt: negative repeat count\n");
return 1;
}
if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) {
perror(tape_name);
return 2;
}
return 0;
}
/* The the drive buffering and other things with this (highly overloaded)
ioctl function. (See also do_options below.) */
static int
do_drvbuffer(int mtfd, cmdef_tr *cmd, int argc, char **argv)
{
struct mtop mt_com;
mt_com.mt_op = MTSETDRVBUFFER;
mt_com.mt_count = (argc > 0 ? strtol(*argv, NULL, 0) : 1);
if ((cmd->cmd_count_bits & MT_ST_OPTIONS) == MT_ST_DEF_OPTIONS)
mt_com.mt_count &= 0xfffff;
#ifdef MT_ST_TIMEOUTS
else if ((cmd->cmd_count_bits & MT_ST_OPTIONS) == MT_ST_TIMEOUTS)
mt_com.mt_count &= 0x7ffffff;
#endif
else
mt_com.mt_count &= 0xfffffff;
mt_com.mt_count |= cmd->cmd_count_bits;
if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) {
perror(tape_name);
return 2;
}
return 0;
}
/* Set the tape driver options */
static int
do_options(int mtfd, cmdef_tr *cmd, int argc, char **argv)
{
int i, an, len;
struct mtop mt_com;
mt_com.mt_op = MTSETDRVBUFFER;
if (argc == 0)
mt_com.mt_count = 0;
else if (isdigit(**argv))
mt_com.mt_count = strtol(*argv, NULL, 0) & ~MT_ST_OPTIONS;
else
for (an = 0, mt_com.mt_count = 0; an < argc; an++) {
len = strlen(argv[an]);
for (i=0; boolean_tbl[i].name != NULL; i++)
if (!strncmp(boolean_tbl[i].name, argv[an], len)) {
mt_com.mt_count |= boolean_tbl[i].bitmask;
break;
}
if (boolean_tbl[i].name == NULL) {
fprintf(stderr, "Illegal property name '%s'.\n", argv[an]);
fprintf(stderr, "The implemented property names are:\n");
for (i=0; boolean_tbl[i].name != NULL; i++)
fprintf(stderr, " %9s -> %s\n", boolean_tbl[i].name,
boolean_tbl[i].expl);
return 1;
}
if (len != strlen(boolean_tbl[i].name))
for (i++ ; boolean_tbl[i].name != NULL; i++)
if (!strncmp(boolean_tbl[i].name, argv[an], len)) {
fprintf(stderr, "Property name '%s' ambiguous.\n",
argv[an]);
return 1;
}
}
switch (cmd->cmd_code) {
case DO_BOOLEANS:
mt_com.mt_count |= MT_ST_BOOLEANS;
break;
case SET_BOOLEANS:
mt_com.mt_count |= MT_ST_SETBOOLEANS;
break;
case CLEAR_BOOLEANS:
mt_com.mt_count |= MT_ST_CLEARBOOLEANS;
break;
}
if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) {
perror(tape_name);
return 2;
}
return 0;
}
/* Tell where the tape is */
static int
do_tell(int mtfd, cmdef_tr *cmd, int argc, char **argv)
{
struct mtpos mt_pos;
if (ioctl(mtfd, MTIOCPOS, (char *)&mt_pos) < 0) {
perror(tape_name);
return 2;
}
printf("At block %ld.\n", mt_pos.mt_blkno);
return 0;
}
/* Position the tape to a specific location within a specified partition */
static int
do_partseek(int mtfd, cmdef_tr *cmd, int argc, char **argv)
{
struct mtop mt_com;
mt_com.mt_op = MTSETPART;
mt_com.mt_count = (argc > 0 ? strtol(*argv, NULL, 0) : 0);
if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) {
perror(tape_name);
return 2;
}
mt_com.mt_op = MTSEEK;
mt_com.mt_count = (argc > 1 ? strtol(argv[1], NULL, 0) : 0);
if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) {
perror(tape_name);
return 2;
}
return 0;
}
/* Position to start of file n. This might be implemented more intelligently
some day. */
static int
do_asf(int mtfd, cmdef_tr *cmd, int argc, char **argv)
{
struct mtop mt_com;
mt_com.mt_op = MTREW;
mt_com.mt_count = 1;
if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) {
perror(tape_name);
return 2;
}
mt_com.mt_count = (argc > 0 ? strtol(*argv, NULL, 0) : 0);
if (mt_com.mt_count > 0) {
mt_com.mt_op = MTFSF;
if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) {
perror(tape_name);
return 2;
}
}
return 0;
}
/*** Decipher the status ***/
static int
do_status(int mtfd, cmdef_tr *cmd, int argc, char **argv)
{
struct mtget status;
int dens, i;
char *type, *density;
if (ioctl(mtfd, MTIOCGET, (char *)&status) < 0) {
perror(tape_name);
return 2;
}
if (status.mt_type == MT_ISSCSI1)
type = "SCSI 1";
else if (status.mt_type == MT_ISSCSI2)
type = "SCSI 2";
else if (status.mt_type == MT_ISONSTREAM_SC)
type = "OnStream SC-, DI-, DP-, or USB";
else
type = NULL;
if (type == NULL) {
if (status.mt_type & 0x800000)
printf ("qic-117 drive type = 0x%05lx\n", status.mt_type & 0x1ffff);
else if (status.mt_type == 0)
printf("IDE-Tape (type code 0) ?\n");
else
printf("Unknown tape drive type (type code %ld)\n", status.mt_type);
printf("File number=%d, block number=%d.\n",
status.mt_fileno, status.mt_blkno);
printf("mt_resid: %ld, mt_erreg: 0x%lx\n",
status.mt_resid, status.mt_erreg);
printf("mt_dsreg: 0x%lx, mt_gstat: 0x%lx\n",
status.mt_dsreg, status.mt_gstat);
}
else {
printf("%s tape drive:\n", type);
if (status.mt_type == MT_ISSCSI2)
printf("File number=%d, block number=%d, partition=%ld.\n",
status.mt_fileno, status.mt_blkno, (status.mt_resid & 0xff));
else
printf("File number=%d, block number=%d.\n",
status.mt_fileno, status.mt_blkno);
if (status.mt_type == MT_ISSCSI1 ||
status.mt_type == MT_ISSCSI2 ||
status.mt_type == MT_ISONSTREAM_SC) {
dens = (status.mt_dsreg & MT_ST_DENSITY_MASK) >> MT_ST_DENSITY_SHIFT;
density = "no translation";
for (i=0; i < NBR_DENSITIES; i++)
if (density_tbl[i].code == dens) {
density = density_tbl[i].name;
break;
}
printf("Tape block size %ld bytes. Density code 0x%x (%s).\n",
((status.mt_dsreg & MT_ST_BLKSIZE_MASK) >> MT_ST_BLKSIZE_SHIFT),
dens, density);
printf("Soft error count since last status=%ld\n",
(status.mt_erreg & MT_ST_SOFTERR_MASK) >> MT_ST_SOFTERR_SHIFT);
}
}
printf("General status bits on (%lx):\n", status.mt_gstat);
if (GMT_EOF(status.mt_gstat))
printf(" EOF");
if (GMT_BOT(status.mt_gstat))
printf(" BOT");
if (GMT_EOT(status.mt_gstat))
printf(" EOT");
if (GMT_SM(status.mt_gstat))
printf(" SM");
if (GMT_EOD(status.mt_gstat))
printf(" EOD");
if (GMT_WR_PROT(status.mt_gstat))
printf(" WR_PROT");
if (GMT_ONLINE(status.mt_gstat))
printf(" ONLINE");
if (GMT_D_6250(status.mt_gstat))
printf(" D_6250");
if (GMT_D_1600(status.mt_gstat))
printf(" D_1600");
if (GMT_D_800(status.mt_gstat))
printf(" D_800");
if (GMT_DR_OPEN(status.mt_gstat))
printf(" DR_OPEN");
if (GMT_IM_REP_EN(status.mt_gstat))
printf(" IM_REP_EN");
if (GMT_CLN(status.mt_gstat))
printf(" CLN");
printf("\n");
return 0;
}
/* Print a list of possible density codes */
static int
print_densities(int fd, cmdef_tr *cmd, int argc, char **argv)
{
int i, offset;
printf("Some SCSI tape density codes:\ncode explanation code explanation\n");
offset = (NBR_DENSITIES + 1) / 2;
for (i=0; i < offset; i++) {
printf("0x%02x %-28s", density_tbl[i].code, density_tbl[i].name);
if (i + offset < NBR_DENSITIES)
printf(" 0x%02x %s\n", density_tbl[i + offset].code, density_tbl[i + offset].name);
else
printf("\n");
}
return 0;
}
/* Try to find out why the command failed */
static void
test_error(int mtfd, cmdef_tr *cmd)
{
struct mtget status;
if (ioctl(mtfd, MTIOCGET, (char *)&status) < 0)
return;
if (status.mt_type != MT_ISSCSI1 && status.mt_type != MT_ISSCSI2)
return;
if ((cmd->error_tests & ET_ONLINE) && !GMT_ONLINE(status.mt_gstat))
fprintf(stderr, "mt: The device is offline (not powered on, no tape ?).\n");
if ((cmd->error_tests & ET_WPROT) && !GMT_WR_PROT(status.mt_gstat))
fprintf(stderr, "mt: The tape is write-protected.\n");
}

379
mtio.h Normal file
View File

@@ -0,0 +1,379 @@
/*
* linux/mtio.h header file for Linux. Written by H. Bergman
*
* Modified for special ioctls provided by zftape in September 1997
* by C.-J. Heine.
*/
#ifndef _LINUX_MTIO_H
#define _LINUX_MTIO_H
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/qic117.h>
/*
* Structures and definitions for mag tape io control commands
*/
/* structure for MTIOCTOP - mag tape op command */
struct mtop {
short mt_op; /* operations defined below */
int mt_count; /* how many of them */
};
/* Magnetic Tape operations [Not all operations supported by all drivers]: */
#define MTRESET 0 /* +reset drive in case of problems */
#define MTFSF 1 /* forward space over FileMark,
* position at first record of next file
*/
#define MTBSF 2 /* backward space FileMark (position before FM) */
#define MTFSR 3 /* forward space record */
#define MTBSR 4 /* backward space record */
#define MTWEOF 5 /* write an end-of-file record (mark) */
#define MTREW 6 /* rewind */
#define MTOFFL 7 /* rewind and put the drive offline (eject?) */
#define MTNOP 8 /* no op, set status only (read with MTIOCGET) */
#define MTRETEN 9 /* retension tape */
#define MTBSFM 10 /* +backward space FileMark, position at FM */
#define MTFSFM 11 /* +forward space FileMark, position at FM */
#define MTEOM 12 /* goto end of recorded media (for appending files).
* MTEOM positions after the last FM, ready for
* appending another file.
*/
#define MTERASE 13 /* erase tape -- be careful! */
#define MTRAS1 14 /* run self test 1 (nondestructive) */
#define MTRAS2 15 /* run self test 2 (destructive) */
#define MTRAS3 16 /* reserved for self test 3 */
#define MTSETBLK 20 /* set block length (SCSI) */
#define MTSETDENSITY 21 /* set tape density (SCSI) */
#define MTSEEK 22 /* seek to block (Tandberg, etc.) */
#define MTTELL 23 /* tell block (Tandberg, etc.) */
#define MTSETDRVBUFFER 24 /* set the drive buffering according to SCSI-2 */
/* ordinary buffered operation with code 1 */
#define MTFSS 25 /* space forward over setmarks */
#define MTBSS 26 /* space backward over setmarks */
#define MTWSM 27 /* write setmarks */
#define MTLOCK 28 /* lock the drive door */
#define MTUNLOCK 29 /* unlock the drive door */
#define MTLOAD 30 /* execute the SCSI load command */
#define MTUNLOAD 31 /* execute the SCSI unload command */
#define MTCOMPRESSION 32/* control compression with SCSI mode page 15 */
#define MTSETPART 33 /* Change the active tape partition */
#define MTMKPART 34 /* Format the tape with one or two partitions */
/* structure for MTIOCGET - mag tape get status command */
struct mtget {
long mt_type; /* type of magtape device */
long mt_resid; /* residual count: (not sure)
* number of bytes ignored, or
* number of files not skipped, or
* number of records not skipped.
*/
/* the following registers are device dependent */
long mt_dsreg; /* status register */
long mt_gstat; /* generic (device independent) status */
long mt_erreg; /* error register */
/* The next two fields are not always used */
__kernel_daddr_t mt_fileno; /* number of current file on tape */
__kernel_daddr_t mt_blkno; /* current block number */
};
/*
* Constants for mt_type. Not all of these are supported,
* and these are not all of the ones that are supported.
*/
#define MT_ISUNKNOWN 0x01
#define MT_ISQIC02 0x02 /* Generic QIC-02 tape streamer */
#define MT_ISWT5150 0x03 /* Wangtek 5150EQ, QIC-150, QIC-02 */
#define MT_ISARCHIVE_5945L2 0x04 /* Archive 5945L-2, QIC-24, QIC-02? */
#define MT_ISCMSJ500 0x05 /* CMS Jumbo 500 (QIC-02?) */
#define MT_ISTDC3610 0x06 /* Tandberg 6310, QIC-24 */
#define MT_ISARCHIVE_VP60I 0x07 /* Archive VP60i, QIC-02 */
#define MT_ISARCHIVE_2150L 0x08 /* Archive Viper 2150L */
#define MT_ISARCHIVE_2060L 0x09 /* Archive Viper 2060L */
#define MT_ISARCHIVESC499 0x0A /* Archive SC-499 QIC-36 controller */
#define MT_ISQIC02_ALL_FEATURES 0x0F /* Generic QIC-02 with all features */
#define MT_ISWT5099EEN24 0x11 /* Wangtek 5099-een24, 60MB, QIC-24 */
#define MT_ISTEAC_MT2ST 0x12 /* Teac MT-2ST 155mb drive, Teac DC-1 card (Wangtek type) */
#define MT_ISEVEREX_FT40A 0x32 /* Everex FT40A (QIC-40) */
#define MT_ISDDS1 0x51 /* DDS device without partitions */
#define MT_ISDDS2 0x52 /* DDS device with partitions */
#define MT_ISONSTREAM_SC 0x61 /* OnStream SCSI tape drives (SC-x0)
and SCSI emulated (DI, DP, USB) */
#define MT_ISSCSI1 0x71 /* Generic ANSI SCSI-1 tape unit */
#define MT_ISSCSI2 0x72 /* Generic ANSI SCSI-2 tape unit */
/* QIC-40/80/3010/3020 ftape supported drives.
* 20bit vendor ID + 0x800000 (see ftape-vendors.h)
*/
#define MT_ISFTAPE_UNKNOWN 0x800000 /* obsolete */
#define MT_ISFTAPE_FLAG 0x800000
struct mt_tape_info {
long t_type; /* device type id (mt_type) */
char *t_name; /* descriptive name */
};
#define MT_TAPE_INFO { \
{MT_ISUNKNOWN, "Unknown type of tape device"}, \
{MT_ISQIC02, "Generic QIC-02 tape streamer"}, \
{MT_ISWT5150, "Wangtek 5150, QIC-150"}, \
{MT_ISARCHIVE_5945L2, "Archive 5945L-2"}, \
{MT_ISCMSJ500, "CMS Jumbo 500"}, \
{MT_ISTDC3610, "Tandberg TDC 3610, QIC-24"}, \
{MT_ISARCHIVE_VP60I, "Archive VP60i, QIC-02"}, \
{MT_ISARCHIVE_2150L, "Archive Viper 2150L"}, \
{MT_ISARCHIVE_2060L, "Archive Viper 2060L"}, \
{MT_ISARCHIVESC499, "Archive SC-499 QIC-36 controller"}, \
{MT_ISQIC02_ALL_FEATURES, "Generic QIC-02 tape, all features"}, \
{MT_ISWT5099EEN24, "Wangtek 5099-een24, 60MB"}, \
{MT_ISTEAC_MT2ST, "Teac MT-2ST 155mb data cassette drive"}, \
{MT_ISEVEREX_FT40A, "Everex FT40A, QIC-40"}, \
{MT_ISONSTREAM_SC, "OnStream SC-, DI-, DP-, or USB tape drive"}, \
{MT_ISSCSI1, "Generic SCSI-1 tape"}, \
{MT_ISSCSI2, "Generic SCSI-2 tape"}, \
{0, NULL} \
}
/* structure for MTIOCPOS - mag tape get position command */
struct mtpos {
long mt_blkno; /* current block number */
};
/* structure for MTIOCGETCONFIG/MTIOCSETCONFIG primarily intended
* as an interim solution for QIC-02 until DDI is fully implemented.
*/
struct mtconfiginfo {
long mt_type; /* drive type */
long ifc_type; /* interface card type */
unsigned short irqnr; /* IRQ number to use */
unsigned short dmanr; /* DMA channel to use */
unsigned short port; /* IO port base address */
unsigned long debug; /* debugging flags */
unsigned have_dens:1;
unsigned have_bsf:1;
unsigned have_fsr:1;
unsigned have_bsr:1;
unsigned have_eod:1;
unsigned have_seek:1;
unsigned have_tell:1;
unsigned have_ras1:1;
unsigned have_ras2:1;
unsigned have_ras3:1;
unsigned have_qfa:1;
unsigned pad1:5;
char reserved[10];
};
/* structure for MTIOCVOLINFO, query information about the volume
* currently positioned at (zftape)
*/
struct mtvolinfo {
unsigned int mt_volno; /* vol-number */
unsigned int mt_blksz; /* blocksize used when recording */
unsigned int mt_rawsize; /* raw tape space consumed, in kb */
unsigned int mt_size; /* volume size after decompression, in kb */
unsigned int mt_cmpr:1; /* this volume has been compressed */
};
/* raw access to a floppy drive, read and write an arbitrary segment.
* For ftape/zftape to support formatting etc.
*/
#define MT_FT_RD_SINGLE 0
#define MT_FT_RD_AHEAD 1
#define MT_FT_WR_ASYNC 0 /* start tape only when all buffers are full */
#define MT_FT_WR_MULTI 1 /* start tape, continue until buffers are empty */
#define MT_FT_WR_SINGLE 2 /* write a single segment and stop afterwards */
#define MT_FT_WR_DELETE 3 /* write deleted data marks, one segment at time */
struct mtftseg
{
unsigned mt_segno; /* the segment to read or write */
unsigned mt_mode; /* modes for read/write (sync/async etc.) */
int mt_result; /* result of r/w request, not of the ioctl */
void *mt_data; /* User space buffer: must be 29kb */
};
/* get tape capacity (ftape/zftape)
*/
struct mttapesize {
unsigned long mt_capacity; /* entire, uncompressed capacity
* of a cartridge
*/
unsigned long mt_used; /* what has been used so far, raw
* uncompressed amount
*/
};
/* possible values of the ftfmt_op field
*/
#define FTFMT_SET_PARMS 1 /* set software parms */
#define FTFMT_GET_PARMS 2 /* get software parms */
#define FTFMT_FORMAT_TRACK 3 /* start formatting a tape track */
#define FTFMT_STATUS 4 /* monitor formatting a tape track */
#define FTFMT_VERIFY 5 /* verify the given segment */
struct ftfmtparms {
unsigned char ft_qicstd; /* QIC-40/QIC-80/QIC-3010/QIC-3020 */
unsigned char ft_fmtcode; /* Refer to the QIC specs */
unsigned char ft_fhm; /* floppy head max */
unsigned char ft_ftm; /* floppy track max */
unsigned short ft_spt; /* segments per track */
unsigned short ft_tpc; /* tracks per cartridge */
};
struct ftfmttrack {
unsigned int ft_track; /* track to format */
unsigned char ft_gap3; /* size of gap3, for FORMAT_TRK */
};
struct ftfmtstatus {
unsigned int ft_segment; /* segment currently being formatted */
};
struct ftfmtverify {
unsigned int ft_segment; /* segment to verify */
unsigned long ft_bsm; /* bsm as result of VERIFY cmd */
};
struct mtftformat {
unsigned int fmt_op; /* operation to perform */
union fmt_arg {
struct ftfmtparms fmt_parms; /* format parameters */
struct ftfmttrack fmt_track; /* ctrl while formatting */
struct ftfmtstatus fmt_status;
struct ftfmtverify fmt_verify; /* for verifying */
} fmt_arg;
};
struct mtftcmd {
unsigned int ft_wait_before; /* timeout to wait for drive to get ready
* before command is sent. Milliseconds
*/
qic117_cmd_t ft_cmd; /* command to send */
unsigned char ft_parm_cnt; /* zero: no parm is sent. */
unsigned char ft_parms[3]; /* parameter(s) to send to
* the drive. The parms are nibbles
* driver sends cmd + 2 step pulses */
unsigned int ft_result_bits; /* if non zero, number of bits
* returned by the tape drive
*/
unsigned int ft_result; /* the result returned by the tape drive*/
unsigned int ft_wait_after; /* timeout to wait for drive to get ready
* after command is sent. 0: don't wait */
int ft_status; /* status returned by ready wait
* undefined if timeout was 0.
*/
int ft_error; /* error code if error status was set by
* command
*/
};
/* mag tape io control commands */
#define MTIOCTOP _IOW('m', 1, struct mtop) /* do a mag tape op */
#define MTIOCGET _IOR('m', 2, struct mtget) /* get tape status */
#define MTIOCPOS _IOR('m', 3, struct mtpos) /* get tape position */
/* The next two are used by the QIC-02 driver for runtime reconfiguration.
* See tpqic02.h for struct mtconfiginfo.
*/
#define MTIOCGETCONFIG _IOR('m', 4, struct mtconfiginfo) /* get tape config */
#define MTIOCSETCONFIG _IOW('m', 5, struct mtconfiginfo) /* set tape config */
/* the next six are used by the floppy ftape drivers and its frontends
* sorry, but MTIOCTOP commands are write only.
*/
#define MTIOCRDFTSEG _IOWR('m', 6, struct mtftseg) /* read a segment */
#define MTIOCWRFTSEG _IOWR('m', 7, struct mtftseg) /* write a segment */
#define MTIOCVOLINFO _IOR('m', 8, struct mtvolinfo) /* info about volume */
#define MTIOCGETSIZE _IOR('m', 9, struct mttapesize)/* get cartridge size*/
#define MTIOCFTFORMAT _IOWR('m', 10, struct mtftformat) /* format ftape */
#define MTIOCFTCMD _IOWR('m', 11, struct mtftcmd) /* send QIC-117 cmd */
/* Generic Mag Tape (device independent) status macros for examining
* mt_gstat -- HP-UX compatible.
* There is room for more generic status bits here, but I don't
* know which of them are reserved. At least three or so should
* be added to make this really useful.
*/
#define GMT_EOF(x) ((x) & 0x80000000)
#define GMT_BOT(x) ((x) & 0x40000000)
#define GMT_EOT(x) ((x) & 0x20000000)
#define GMT_SM(x) ((x) & 0x10000000) /* DDS setmark */
#define GMT_EOD(x) ((x) & 0x08000000) /* DDS EOD */
#define GMT_WR_PROT(x) ((x) & 0x04000000)
/* #define GMT_ ? ((x) & 0x02000000) */
#define GMT_ONLINE(x) ((x) & 0x01000000)
#define GMT_D_6250(x) ((x) & 0x00800000)
#define GMT_D_1600(x) ((x) & 0x00400000)
#define GMT_D_800(x) ((x) & 0x00200000)
/* #define GMT_ ? ((x) & 0x00100000) */
/* #define GMT_ ? ((x) & 0x00080000) */
#define GMT_DR_OPEN(x) ((x) & 0x00040000) /* door open (no tape) */
/* #define GMT_ ? ((x) & 0x00020000) */
#define GMT_IM_REP_EN(x) ((x) & 0x00010000) /* immediate report mode */
#define GMT_CLN(x) ((x) & 0x00008000) /* cleaning requested */
/* 15 generic status bits unused */
/* SCSI-tape specific definitions */
/* Bitfield shifts in the status */
#define MT_ST_BLKSIZE_SHIFT 0
#define MT_ST_BLKSIZE_MASK 0xffffff
#define MT_ST_DENSITY_SHIFT 24
#define MT_ST_DENSITY_MASK 0xff000000
#define MT_ST_SOFTERR_SHIFT 0
#define MT_ST_SOFTERR_MASK 0xffff
/* Bitfields for the MTSETDRVBUFFER ioctl */
#define MT_ST_OPTIONS 0xf0000000
#define MT_ST_BOOLEANS 0x10000000
#define MT_ST_SETBOOLEANS 0x30000000
#define MT_ST_CLEARBOOLEANS 0x40000000
#define MT_ST_WRITE_THRESHOLD 0x20000000
#define MT_ST_DEF_BLKSIZE 0x50000000
#define MT_ST_DEF_OPTIONS 0x60000000
#define MT_ST_TIMEOUTS 0x70000000
#define MT_ST_SET_TIMEOUT (MT_ST_TIMEOUTS | 0x000000)
#define MT_ST_SET_LONG_TIMEOUT (MT_ST_TIMEOUTS | 0x100000)
#define MT_ST_SET_CLN 0x80000000
#define MT_ST_BUFFER_WRITES 0x1
#define MT_ST_ASYNC_WRITES 0x2
#define MT_ST_READ_AHEAD 0x4
#define MT_ST_DEBUGGING 0x8
#define MT_ST_TWO_FM 0x10
#define MT_ST_FAST_MTEOM 0x20
#define MT_ST_AUTO_LOCK 0x40
#define MT_ST_DEF_WRITES 0x80
#define MT_ST_CAN_BSR 0x100
#define MT_ST_NO_BLKLIMS 0x200
#define MT_ST_CAN_PARTITIONS 0x400
#define MT_ST_SCSI2LOGICAL 0x800
#define MT_ST_SYSV 0x1000
#define MT_ST_NOWAIT 0x2000
/* The mode parameters to be controlled. Parameter chosen with bits 20-28 */
#define MT_ST_CLEAR_DEFAULT 0xfffff
#define MT_ST_DEF_DENSITY (MT_ST_DEF_OPTIONS | 0x100000)
#define MT_ST_DEF_COMPRESSION (MT_ST_DEF_OPTIONS | 0x200000)
#define MT_ST_DEF_DRVBUFFER (MT_ST_DEF_OPTIONS | 0x300000)
/* The offset for the arguments for the special HP changer load command. */
#define MT_ST_HPLOADER_OFFSET 10000
#endif /* _LINUX_MTIO_H */

290
qic117.h Normal file
View File

@@ -0,0 +1,290 @@
#ifndef _QIC117_H
#define _QIC117_H
/*
* Copyright (C) 1993-1996 Bas Laarhoven,
* (C) 1997 Claus-Justus Heine.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Source: /homes/cvs/ftape-stacked/include/linux/qic117.h,v $
* $Revision: 1.2 $
* $Date: 1997/10/05 19:19:32 $
*
* This file contains QIC-117 spec. related definitions for the
* QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux.
*
* These data were taken from the Quarter-Inch Cartridge
* Drive Standards, Inc. document titled:
* `Common Command Set Interface Specification for Flexible
* Disk Controller Based Minicartridge Tape Drives'
* document QIC-117 Revision J, 28 Aug 96.
* For more information, contact:
* Quarter-Inch Cartridge Drive Standards, Inc.
* 311 East Carrillo Street
* Santa Barbara, California 93101
* Telephone (805) 963-3853
* Fax (805) 962-1541
* WWW http://www.qic.org
*
* Current QIC standard revisions (of interest) are:
* QIC-40-MC, Rev. M, 2 Sep 92.
* QIC-80-MC, Rev. N, 20 Mar 96.
* QIC-80-MC, Rev. K, 15 Dec 94.
* QIC-113, Rev. G, 15 Jun 95.
* QIC-117, Rev. J, 28 Aug 96.
* QIC-122, Rev. B, 6 Mar 91.
* QIC-130, Rev. C, 2 Sep 92.
* QIC-3010-MC, Rev. F, 14 Jun 95.
* QIC-3020-MC, Rev. G, 31 Aug 95.
* QIC-CRF3, Rev. B, 15 Jun 95.
* */
/*
* QIC-117 common command set rev. J.
* These commands are sent to the tape unit
* as number of pulses over the step line.
*/
typedef enum {
QIC_NO_COMMAND = 0,
QIC_RESET = 1,
QIC_REPORT_NEXT_BIT = 2,
QIC_PAUSE = 3,
QIC_MICRO_STEP_PAUSE = 4,
QIC_ALTERNATE_TIMEOUT = 5,
QIC_REPORT_DRIVE_STATUS = 6,
QIC_REPORT_ERROR_CODE = 7,
QIC_REPORT_DRIVE_CONFIGURATION = 8,
QIC_REPORT_ROM_VERSION = 9,
QIC_LOGICAL_FORWARD = 10,
QIC_PHYSICAL_REVERSE = 11,
QIC_PHYSICAL_FORWARD = 12,
QIC_SEEK_HEAD_TO_TRACK = 13,
QIC_SEEK_LOAD_POINT = 14,
QIC_ENTER_FORMAT_MODE = 15,
QIC_WRITE_REFERENCE_BURST = 16,
QIC_ENTER_VERIFY_MODE = 17,
QIC_STOP_TAPE = 18,
/* commands 19-20: reserved */
QIC_MICRO_STEP_HEAD_UP = 21,
QIC_MICRO_STEP_HEAD_DOWN = 22,
QIC_SOFT_SELECT = 23,
QIC_SOFT_DESELECT = 24,
QIC_SKIP_REVERSE = 25,
QIC_SKIP_FORWARD = 26,
QIC_SELECT_RATE = 27,
/* command 27, in ccs2: Select Rate or Format */
QIC_ENTER_DIAGNOSTIC_1 = 28,
QIC_ENTER_DIAGNOSTIC_2 = 29,
QIC_ENTER_PRIMARY_MODE = 30,
/* command 31: vendor unique */
QIC_REPORT_VENDOR_ID = 32,
QIC_REPORT_TAPE_STATUS = 33,
QIC_SKIP_EXTENDED_REVERSE = 34,
QIC_SKIP_EXTENDED_FORWARD = 35,
QIC_CALIBRATE_TAPE_LENGTH = 36,
QIC_REPORT_FORMAT_SEGMENTS = 37,
QIC_SET_FORMAT_SEGMENTS = 38,
/* commands 39-45: reserved */
QIC_PHANTOM_SELECT = 46,
QIC_PHANTOM_DESELECT = 47
} qic117_cmd_t;
typedef enum {
discretional = 0, required, ccs1, ccs2
} qic_compatibility;
typedef enum {
unused, mode, motion, report
} command_types;
struct qic117_command_table {
char *name;
__u8 mask;
__u8 state;
__u8 cmd_type;
__u8 non_intr;
__u8 level;
};
#define QIC117_COMMANDS {\
/* command mask state cmd_type */\
/* | name | | | non_intr */\
/* | | | | | | level */\
/* 0*/ {NULL, 0x00, 0x00, mode, 0, discretional},\
/* 1*/ {"soft reset", 0x00, 0x00, motion, 1, required},\
/* 2*/ {"report next bit", 0x00, 0x00, report, 0, required},\
/* 3*/ {"pause", 0x36, 0x24, motion, 1, required},\
/* 4*/ {"micro step pause", 0x36, 0x24, motion, 1, required},\
/* 5*/ {"alternate command timeout", 0x00, 0x00, mode, 0, required},\
/* 6*/ {"report drive status", 0x00, 0x00, report, 0, required},\
/* 7*/ {"report error code", 0x01, 0x01, report, 0, required},\
/* 8*/ {"report drive configuration",0x00, 0x00, report, 0, required},\
/* 9*/ {"report rom version", 0x00, 0x00, report, 0, required},\
/*10*/ {"logical forward", 0x37, 0x25, motion, 0, required},\
/*11*/ {"physical reverse", 0x17, 0x05, motion, 0, required},\
/*12*/ {"physical forward", 0x17, 0x05, motion, 0, required},\
/*13*/ {"seek head to track", 0x37, 0x25, motion, 0, required},\
/*14*/ {"seek load point", 0x17, 0x05, motion, 1, required},\
/*15*/ {"enter format mode", 0x1f, 0x05, mode, 0, required},\
/*16*/ {"write reference burst", 0x1f, 0x05, motion, 1, required},\
/*17*/ {"enter verify mode", 0x37, 0x25, mode, 0, required},\
/*18*/ {"stop tape", 0x00, 0x00, motion, 1, required},\
/*19*/ {"reserved (19)", 0x00, 0x00, unused, 0, discretional},\
/*20*/ {"reserved (20)", 0x00, 0x00, unused, 0, discretional},\
/*21*/ {"micro step head up", 0x02, 0x00, motion, 0, required},\
/*22*/ {"micro step head down", 0x02, 0x00, motion, 0, required},\
/*23*/ {"soft select", 0x00, 0x00, mode, 0, discretional},\
/*24*/ {"soft deselect", 0x00, 0x00, mode, 0, discretional},\
/*25*/ {"skip segments reverse", 0x36, 0x24, motion, 1, required},\
/*26*/ {"skip segments forward", 0x36, 0x24, motion, 1, required},\
/*27*/ {"select rate or format", 0x03, 0x01, mode, 0, required /* [ccs2] */},\
/*28*/ {"enter diag mode 1", 0x00, 0x00, mode, 0, discretional},\
/*29*/ {"enter diag mode 2", 0x00, 0x00, mode, 0, discretional},\
/*30*/ {"enter primary mode", 0x00, 0x00, mode, 0, required},\
/*31*/ {"vendor unique (31)", 0x00, 0x00, unused, 0, discretional},\
/*32*/ {"report vendor id", 0x00, 0x00, report, 0, required},\
/*33*/ {"report tape status", 0x04, 0x04, report, 0, ccs1},\
/*34*/ {"skip extended reverse", 0x36, 0x24, motion, 1, ccs1},\
/*35*/ {"skip extended forward", 0x36, 0x24, motion, 1, ccs1},\
/*36*/ {"calibrate tape length", 0x17, 0x05, motion, 1, ccs2},\
/*37*/ {"report format segments", 0x17, 0x05, report, 0, ccs2},\
/*38*/ {"set format segments", 0x17, 0x05, mode, 0, ccs2},\
/*39*/ {"reserved (39)", 0x00, 0x00, unused, 0, discretional},\
/*40*/ {"vendor unique (40)", 0x00, 0x00, unused, 0, discretional},\
/*41*/ {"vendor unique (41)", 0x00, 0x00, unused, 0, discretional},\
/*42*/ {"vendor unique (42)", 0x00, 0x00, unused, 0, discretional},\
/*43*/ {"vendor unique (43)", 0x00, 0x00, unused, 0, discretional},\
/*44*/ {"vendor unique (44)", 0x00, 0x00, unused, 0, discretional},\
/*45*/ {"vendor unique (45)", 0x00, 0x00, unused, 0, discretional},\
/*46*/ {"phantom select", 0x00, 0x00, mode, 0, discretional},\
/*47*/ {"phantom deselect", 0x00, 0x00, mode, 0, discretional},\
}
/*
* Status bits returned by QIC_REPORT_DRIVE_STATUS
*/
#define QIC_STATUS_READY 0x01 /* Drive is ready or idle. */
#define QIC_STATUS_ERROR 0x02 /* Error detected, must read
error code to clear this */
#define QIC_STATUS_CARTRIDGE_PRESENT 0x04 /* Tape is present */
#define QIC_STATUS_WRITE_PROTECT 0x08 /* Tape is write protected */
#define QIC_STATUS_NEW_CARTRIDGE 0x10 /* New cartridge inserted, must
read error status to clear. */
#define QIC_STATUS_REFERENCED 0x20 /* Cartridge appears to have been
formatted. */
#define QIC_STATUS_AT_BOT 0x40 /* Cartridge is at physical
beginning of tape. */
#define QIC_STATUS_AT_EOT 0x80 /* Cartridge is at physical end
of tape. */
/*
* Status bits returned by QIC_REPORT_DRIVE_CONFIGURATION
*/
#define QIC_CONFIG_RATE_MASK 0x18
#define QIC_CONFIG_RATE_SHIFT 3
#define QIC_CONFIG_RATE_250 0
#define QIC_CONFIG_RATE_500 2
#define QIC_CONFIG_RATE_1000 3
#define QIC_CONFIG_RATE_2000 1
#define QIC_CONFIG_RATE_4000 0 /* since QIC-117 Rev. J */
#define QIC_CONFIG_LONG 0x40 /* Extra Length Tape Detected */
#define QIC_CONFIG_80 0x80 /* QIC-80 detected. */
/*
* Status bits returned by QIC_REPORT_TAPE_STATUS
*/
#define QIC_TAPE_STD_MASK 0x0f
#define QIC_TAPE_QIC40 0x01
#define QIC_TAPE_QIC80 0x02
#define QIC_TAPE_QIC3020 0x03
#define QIC_TAPE_QIC3010 0x04
#define QIC_TAPE_LEN_MASK 0x70
#define QIC_TAPE_205FT 0x10
#define QIC_TAPE_307FT 0x20
#define QIC_TAPE_VARIABLE 0x30
#define QIC_TAPE_1100FT 0x40
#define QIC_TAPE_FLEX 0x60
#define QIC_TAPE_WIDE 0x80
/* Define a value (in feet) slightly higher than
* the possible maximum tape length.
*/
#define QIC_TOP_TAPE_LEN 1500
/*
* Errors: List of error codes, and their severity.
*/
typedef struct {
char *message; /* Text describing the error. */
unsigned int fatal:1; /* Non-zero if the error is fatal. */
} ftape_error;
#define QIC117_ERRORS {\
/* 0*/ { "No error", 0, },\
/* 1*/ { "Command Received while Drive Not Ready", 0, },\
/* 2*/ { "Cartridge Not Present or Removed", 1, },\
/* 3*/ { "Motor Speed Error (not within 1%)", 1, },\
/* 4*/ { "Motor Speed Fault (jammed, or gross speed error", 1, },\
/* 5*/ { "Cartridge Write Protected", 1, },\
/* 6*/ { "Undefined or Reserved Command Code", 1, },\
/* 7*/ { "Illegal Track Address Specified for Seek", 1, },\
/* 8*/ { "Illegal Command in Report Subcontext", 0, },\
/* 9*/ { "Illegal Entry into a Diagnostic Mode", 1, },\
/*10*/ { "Broken Tape Detected (based on hole sensor)", 1, },\
/*11*/ { "Warning--Read Gain Setting Error", 1, },\
/*12*/ { "Command Received While Error Status Pending (obs)", 1, },\
/*13*/ { "Command Received While New Cartridge Pending", 1, },\
/*14*/ { "Command Illegal or Undefined in Primary Mode", 1, },\
/*15*/ { "Command Illegal or Undefined in Format Mode", 1, },\
/*16*/ { "Command Illegal or Undefined in Verify Mode", 1, },\
/*17*/ { "Logical Forward Not at Logical BOT or no Format Segments in Format Mode", 1, },\
/*18*/ { "Logical EOT Before All Segments generated", 1, },\
/*19*/ { "Command Illegal When Cartridge Not Referenced", 1, },\
/*20*/ { "Self-Diagnostic Failed (cannot be cleared)", 1, },\
/*21*/ { "Warning EEPROM Not Initialized, Defaults Set", 1, },\
/*22*/ { "EEPROM Corrupted or Hardware Failure", 1, },\
/*23*/ { "Motion Time-out Error", 1, },\
/*24*/ { "Data Segment Too Long -- Logical Forward or Pause", 1, },\
/*25*/ { "Transmit Overrun (obs)", 1, },\
/*26*/ { "Power On Reset Occurred", 0, },\
/*27*/ { "Software Reset Occurred", 0, },\
/*28*/ { "Diagnostic Mode 1 Error", 1, },\
/*29*/ { "Diagnostic Mode 2 Error", 1, },\
/*30*/ { "Command Received During Non-Interruptible Process", 1, },\
/*31*/ { "Rate or Format Selection Error", 1, },\
/*32*/ { "Illegal Command While in High Speed Mode", 1, },\
/*33*/ { "Illegal Seek Segment Value", 1, },\
/*34*/ { "Invalid Media", 1, },\
/*35*/ { "Head Positioning Failure", 1, },\
/*36*/ { "Write Reference Burst Failure", 1, },\
/*37*/ { "Prom Code Missing", 1, },\
/*38*/ { "Invalid Format", 1, },\
/*39*/ { "EOT/BOT System Failure", 1, },\
/*40*/ { "Prom A Checksum Error", 1, },\
/*41*/ { "Drive Wakeup Reset Occurred", 1, },\
/*42*/ { "Prom B Checksum Error", 1, },\
/*43*/ { "Illegal Entry into Format Mode", 1, },\
}
#endif /* _QIC117_H */

262
stinit.8 Normal file
View File

@@ -0,0 +1,262 @@
.TH STINIT 8 "November 2001" \" -*- nroff -*-
.SH NAME
stinit \- initialize SCSI magnetic tape drives
.SH SYNOPSIS
.B stinit
[\-f conf-file] [\-h] [-p] [-r] [-v] [devices...]
.SH DESCRIPTION
This manual page documents the tape control program
.BR stinit
can used to initialize SCSI tape drive modes at system startup, after
loading the tape driver as module, or after introduction of new device
to the SCSI subsystem at run-time. The initialization is performed by
sending ioctl commands to the drive. The commands are defined in a
text file that is indexed using the inquiry data the drive returns
(manufacturer, device, revision). Values for all of the general and
mode-specific SCSI tape parameters up to Linux version 2.4.15 can be
initialized.
.PP
.SH OPTIONS
.TP
.I \-f conf-file
Specifies the name of the text file containing the definitions for
different tape drive types. By default
.B stinit
tries to find the definition file
.I stinit.def
or
.I /etc/stinit.def
(in this order).
.TP
.I \-h
Print the usage information.
.TP
.I \-p
The definition file is parsed but no tape drive initialization is
attempted. This option can be used for testing the integrity of a
definition file after changes have been made.
.TP
.I \-r
Rewind every device being initialized.
.TP
.I \-v
The more -v options (currently up to two), the more verbose output.
.TP
.I \-\-version
Print the program version.
.PP
.SH THE DEVICES BEING INITIALIZED
If the program is started without arguments, it tries to find all
accessible SCSI tape devices and the device files for the different
modes of the devices. The tape drives are searched in the scanning
order of the kernel and searching is stopped at the first non-existing
tape. All of the found devices are initialized if a matching
description is found from the parameter file. Note that a mode for a
device is not initialized if the corresponding device file is not
found even if a matching description for the mode exists. The
non-rewind device is preferred over the auto-rewind device for each
mode. If the directory
.I /dev/tapes
is found, the devfs filesystem is assumed to be mounted on /dev. Otherwise,
the directories
.I /dev/scsi
and
.I /dev
are scanned for device files.
.PP
SCSI tape drives can be initialized selectively using program
arguments. A numeric argument specifies the number of the tape drive
in the scanning order of the kernel. A file name specifies that the
device corresponding to this name is to be initialized. If the file name
is given without the directory specification, the program searches for
the name in the device directories
.I
/dev/scsi
and
.I /dev.
Only full path names are supported with devfs.
.PP
.SH THE CONFIGURATION FILE
The configuration file is a simple text file that contains
descriptions of tape drives and the corresponding initialization
parameters. The parameter definition blocks are delimited by
.I {}.
Specification of the drive description is restarted after each
parameter definition block.
.PP
The drive descriptions and the parameter definitions consist of pairs
.I name = value.
The value is either a numeric parameter, a string not containing
blanks, or a quoted string. If the
.I =value
-part is omitted, the value
.I
"1"
is used. If the character
.I #
is found from an input line, the rest of the line is discarded. This
allows use of comments in the definition file. The following example
contains definitions for one type of tape drives:
.PP
.RS
.nf
# The XY dat
manufacturer=XY-COMPANY model = "UVW DRIVE" {
scsi2logical=1 # Common definitions for all modes
can-bsr can-partitions auto-lock
# Definition of modes
mode1 blocksize=0 compression=1
mode2 blocksize=1024 compression=1
mode3 blocksize=0 compression=0
mode4 blocksize = 1024 compression=0 }
.fi
.RE
.PP
The devices are identified using zero or more of the following
keywords corresponding to the data returned by the tape device as
response to the SCSI INQUIRY command. The matches are case-sensitive
and performed up to the length defined in the configuration file
(permitting use of partial matches).
.IP manufacturer=
This keyword specifies the string that must match the vendor
identification returned by the device.
.IP model=
This keyword defines the string that must match the
.B product identification
returned by the device.
.IP revision=
This keyword matched the string that must match the
.B product revision level
returned by the device.
.PP
All of the matching initializations are collected in the order they
are defined in the file. This means that common parameters can be
defined for all devices using zero keywords for a definition
block. Another consequence is that, for instance, some parameters can
be easily given different values for a specific firmware revision without
repeating the parameters common to all revisions.
.PP
The tape parameters are defined using the following keywords. More
thorough description of the parameters can be found from the
.I st(4)
man page (not up to date when this is written) or from the file
.I drivers/scsi/README.st
in the Linux kernel source tree. The keywords are matched using only
the first characters. The part of the keywords not used in matching is
enclosed by []. The numeric values may be specified either in decimal
notation or hexadecimal notation (using the prefix 0x).
.IP drive-[buffering]=value
The drive's buffering parameter is set to
.I value.
This parameter if common for all modes.
.IP cleaning
The cleaning request notifying parameter is set to
.I value
.IP no-w[ait]
The immediate mode is used with commands like rewind if
.I value
is non-zero (i.e., the driver does not wait for the command to finish).
.IP mode=value
This keyword starts definition of tape mode
.I value.
The number of the mode must be between 1 and 4.
.IP disab[led]=value
This mode is disabled for this device if
.I value
is non-zero. Can be used if some mode defined in a more general
definition should be disabled by a more specific definition for some
device (for example, for a device with buggy firmware level).
.IP block[size]=value
The default tape block size is set to
.I value.
bytes. The block size zero means variable block mode.
.IP dens[ity]=value
The tape density code is set to
.I value.
.IP buff[ering]=value
The buffered writes by the driver in fixed block mode are enabled if
.I value
is non-zero.
.IP async[-writes]=value
Asynchronous writes by the driver are enabled if
.I value
is non-zero.
.IP read[-ahead]=value
Read-ahead by the driver in fixed block mode is allowed if
.I value
is non-zero.
.IP two[-fms]=value
Two filemarks are written when a file being written to is closed if
.I value
is non-zero. By default, one filemark is written.
.IP comp[ression]=value
Compression of the data by the drive is enabled if
.I value
is non-zero. Note that the tape driver can't enable compression for
all drives that can compress data. Note also that some drives define
compression using density codes.
.IP auto[-lock]=value
The tape drive door is locked automatically when the device file is
opened if
.I value
is non-zero.
.IP fast[-eom]=value
The MTEOM command is performed using the SCSI command that spaces
directly to the end of medium if
.I value
is non-zero. The drawback is that the file number in the status
becomes invalid. By default, spacing to end of medium is performed by
spacing over filemarks until end of medium is detected and the file
number remains valid.
.IP can-b[sr]=value
Backspacing over records is used by the driver when repositioning
the tape when read-ahead is enabled if
.I value
is non-zero.
.IP noblk[limits]=value
The tape driver does not use the READ BLOCK LIMITS SCSI command when
the device is being opened if
.I value
is non-zero. This is for the drives that do not support this SCSI
command.
.IP can-p[artitions]=value
The support for tape partitions is enabled if
.I value
is non-zero.
.IP scsi2[logical]=value
Logical block addresses are used in the MTSEEK and MTIOCPOS
commands if
.I value
is non-zero. The default is to use the device-specific addresses.
.IP defs-for-w[rites]=value
The parameters defining the tape format (density, block size, etc.)
are forced when writing starts at the beginning of a tape if
.I value
is non-zero. The default is to change there parameters each time the
device is opened at the beginning of a tape (or the mode is changed in
the middle of a tape).
.IP timeout
The normal timeout for the device is set to
.I value
seconds.
.IP long-time[out]
The long timeout for the device is set to
.I value
seconds.
.SH RETURN VALUE
The program exits with value one if the command line is incorrect, the
definition file is not found, or option -p is given and parsing the
definition file fails. In all other cases the return value is
zero (i.e., failing of initialization is not currently signaled by
the return value).
.SH RESTRICTIONS
With the exception of the -p option, the program can be used only by
the superuser. This is because the program uses ioctls allowed only
for the superuser.
.SH AUTHOR
The program is written by Kai Makisara <Kai.Makisara@metla.fi>.
.SH COPYRIGHT
The program and the manual page are copyrighted by Kai Makisara, 1998-2001.
They can be distributed according to the GNU Copyleft.
.SH SEE ALSO
st(4) mt(1)

895
stinit.c Normal file
View File

@@ -0,0 +1,895 @@
/* This program initializes Linux SCSI tape drives using the
inquiry data from the devices and a text database.
Copyright 1996-2001 by Kai M<>kisara (email Kai.Makisara@metla.fi)
Distribution of this program is allowed according to the
GNU Public Licence.
Last modified: Thu Nov 8 21:13:48 2001 by makisara@kai.makisara.local
*/
#ifndef lint
static char rcsid[] = "$Id: /usr2/users/makisara/src/sys/mt-st-0.7/stinit.c at Thu Nov 8 21:13:48 2001 by makisara@kai.makisara.local$";
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/sysmacros.h>
#include <linux/major.h>
#include "mtio.h"
#ifndef FALSE
#define TRUE 1
#define FALSE 0
#endif
#define SKIP_WHITE(p) for ( ; *p == ' ' || *p == '\t'; p++)
#define VERSION "0.7"
typedef struct _modepar_tr {
int defined;
int blocksize;
int density;
int buffer_writes;
int async_writes;
int read_ahead;
int two_fm;
int compression;
int auto_lock;
int fast_eod;
int can_bsr;
int no_blklimits;
int can_partitions;
int scsi2logical;
int sysv;
int defs_for_writes;
} modepar_tr;
typedef struct _devdef_tr {
int do_rewind;
int drive_buffering;
int timeout;
int long_timeout;
int cleaning;
int nowait;
modepar_tr modedefs[4];
} devdef_tr;
#define DEFMAX 2048
#define LINEMAX 256
#define MAX_TAPES 32
#define NBR_MODES 4
static int verbose = 0;
/* The device directories being searched */
typedef struct
{
const char *dir;
int selective_scan;
} devdir;
static devdir devdirs[] = { {"/dev/scsi", 0}, {"/dev", 1}, {NULL, 0}};
#define DEVFS_PATH "/dev/tapes"
#define DEVFS_TAPEFMT DEVFS_PATH "/tape%d"
/* The partial names of the tape devices being included in the
search in selective scan */
static char *tape_name_bases[] = {
"st", "nst", "rmt", "nrmt", "tape", NULL};
/* The list of standard definition files being searched */
static char *std_databases[] = {
"/etc/stinit.def",
NULL};
static FILE *
open_database(char *base)
{
int i;
FILE *f;
if (base != NULL) {
if ((f = fopen(base, "r")) == NULL)
fprintf(stderr, "stinit: Can't find SCSI tape database '%s'.\n",
base);
return f;
}
for (i=0; std_databases[i] != NULL; i++) {
if (verbose > 1)
fprintf(stderr, "Trying to open database '%s'.\n", std_databases[i]);
if ((f = fopen(std_databases[i], "r")) != NULL) {
if (verbose > 1)
fprintf(stderr, "Open succeeded.\n");
return f;
}
}
fprintf(stderr, "Can't find the tape characteristics database.\n");
return NULL;
}
static char *
find_string(char *s, char *target, char *buf, int buflen)
{
int have_arg;
char *cp, *cp2, c, *argp;
if (buf != NULL && buflen > 0)
*buf = '\0';
for ( ; *s != '\0'; ) {
SKIP_WHITE(s);
if (isalpha(*s)) {
for (cp=s ; isalnum(*cp) || *cp == '-'; cp++)
;
cp2 = cp;
SKIP_WHITE(cp);
if (*cp == '=') {
cp++;
SKIP_WHITE(cp);
if (*cp == '"') {
cp++;
for (cp2=cp; *cp2 != '"' && *cp2 != '\0'; cp2++)
;
}
else
for (cp2=cp+1; isalnum(*cp2) || *cp2 == '-'; cp2++)
;
if (cp2 == '\0')
return NULL;
have_arg = TRUE;
argp = cp;
}
else {
have_arg = FALSE;
argp = "1";
}
if (!strncmp(target, s, strlen(target))) {
c = *cp2;
*cp2 = '\0';
if (buf == NULL)
buf = strdup(argp);
else {
if (strlen(argp) < buflen)
strcpy(buf, argp);
else {
strncpy(buf, argp, buflen);
buf[buflen - 1] = '\0';
}
}
if (have_arg && c == '"')
cp2++;
else
*cp2 = c;
if (*cp2 != '\0')
memmove(s, cp2, strlen(cp2) + 1);
else
*s = '\0';
return buf;
}
s = cp2;
}
else
for ( ; *s != '\0' && *s != ' ' && *s != '\t'; s++)
;
}
return NULL;
}
static int
next_block(FILE *dbf, char *buf, int buflen, int limiter)
{
int len;
char *cp, *bp;
static char lbuf[LINEMAX];
if (limiter == 0) { /* Restart */
rewind(dbf);
lbuf[0] = '\0';
return TRUE;
}
for (len = 0 ; ; ) {
bp = buf + len;
if ((cp = strchr(lbuf, limiter)) != NULL) {
*cp = '\0';
strcpy(bp, lbuf);
cp++;
SKIP_WHITE(cp);
memmove(lbuf, cp, strlen(cp) + 1);
return TRUE;
}
if (len + strlen(lbuf) >= DEFMAX) {
fprintf(stderr, "Too long definition: '%s'\n", buf);
return FALSE;
}
cp = lbuf;
SKIP_WHITE(cp);
strcpy(bp, cp);
strcat(bp, " ");
len += strlen(cp) + 1;
if (fgets(lbuf, LINEMAX, dbf) == NULL)
return FALSE;
if ((cp = strchr(lbuf, '#')) != NULL)
*cp = '\0';
else
lbuf[strlen(lbuf) - 1] = '\0';
}
}
static int
find_pars(FILE *dbf, char *company, char *product, char *rev, devdef_tr *defs,
int parse_only)
{
int i, mode, modes_defined, errors;
char line[LINEMAX], defstr[DEFMAX], comdef[DEFMAX];
char tmpcomp[LINEMAX], tmpprod[LINEMAX], tmprev[LINEMAX], *cp, c, *t;
char *nextdef, *curdef, *comptr;
static int call_nbr = 0;
call_nbr++;
defs->drive_buffering = (-1);
defs->timeout = (-1);
defs->long_timeout = (-1);
defs->cleaning = (-1);
defs->nowait = (-1);
for (i=0; i < NBR_MODES; i++) {
defs->modedefs[i].defined = FALSE;
defs->modedefs[i].blocksize = (-1);
defs->modedefs[i].density = (-1);
defs->modedefs[i].buffer_writes = (-1);
defs->modedefs[i].async_writes = (-1);
defs->modedefs[i].read_ahead = (-1);
defs->modedefs[i].two_fm = (-1);
defs->modedefs[i].compression = (-1);
defs->modedefs[i].auto_lock = (-1);
defs->modedefs[i].fast_eod = (-1);
defs->modedefs[i].can_bsr = (-1);
defs->modedefs[i].no_blklimits = (-1);
defs->modedefs[i].can_partitions = (-1);
defs->modedefs[i].scsi2logical = (-1);
defs->modedefs[i].sysv = (-1);
defs->modedefs[i].defs_for_writes = (-1);
}
next_block(dbf, NULL, 0, 0);
/* Find matching inquiry block */
for (errors=0 ; ; ) {
if (!next_block(dbf, defstr, DEFMAX, '{'))
break;
find_string(defstr, "manuf", tmpcomp, LINEMAX);
find_string(defstr, "model", tmpprod, LINEMAX);
find_string(defstr, "rev", tmprev, LINEMAX);
if (!next_block(dbf, defstr, DEFMAX, '}')) {
fprintf(stderr,
"End of definition block not found for ('%s', '%s', '%s').\n",
tmpcomp, tmpprod, tmprev);
return FALSE;
}
if (!parse_only) {
if (tmpcomp[0] != '\0' &&
strncmp(company, tmpcomp, strlen(tmpcomp)))
continue;
if (tmpprod[0] != '\0' &&
strncmp(product, tmpprod, strlen(tmpprod)))
continue;
if (tmprev[0] != '\0' &&
strncmp(rev, tmprev, strlen(tmprev)))
continue;
}
else if (verbose > 0)
printf("\nParsing modes for ('%s', '%s', '%s').\n",
tmpcomp, tmpprod, tmprev);
/* Block found, get the characteristics */
for (nextdef=defstr; *nextdef != '\0' &&
(*nextdef != 'm' || strncmp(nextdef, "mode", 2)); nextdef++)
;
c = *nextdef;
*nextdef = '\0';
strcpy(comdef, defstr);
*nextdef = c;
comptr = comdef;
SKIP_WHITE(comptr);
for ( ; *nextdef != '\0'; ) {
curdef = nextdef;
SKIP_WHITE(curdef);
for (nextdef++ ; *nextdef != '\0' &&
(*nextdef != 'm' || strncmp(nextdef, "mode", 2)); nextdef++)
;
c = *nextdef;
*nextdef = '\0';
mode = strtol(curdef + 4, &cp, 0) - 1;
if (mode < 0 || mode >= NBR_MODES) {
fprintf(stderr,
"Illegal mode for ('%s', '%s', '%s'):\n'%s'\n",
tmpcomp, tmpprod, tmprev, curdef);
*nextdef = c;
errors++;
continue;
}
strcpy(defstr, comptr);
strcat(defstr, cp);
*nextdef = c;
if (verbose > 1)
fprintf(stderr, "Mode %d definition: %s\n", mode + 1, defstr);
if ((t = find_string(defstr, "disab", line, LINEMAX)) != NULL &&
strtol(t, NULL, 0) != 0) {
defs->modedefs[mode].defined = FALSE;
continue;
}
if ((t = find_string(defstr, "drive-", line, LINEMAX)) != NULL)
defs->drive_buffering = strtol(t, NULL, 0);
if ((t = find_string(defstr, "timeout", line, LINEMAX)) != NULL)
defs->timeout = strtol(t, NULL, 0);
if ((t = find_string(defstr, "long-time", line, LINEMAX)) != NULL)
defs->long_timeout = strtol(t, NULL, 0);
if ((t = find_string(defstr, "clean", line, LINEMAX)) != NULL)
defs->cleaning = strtol(t, NULL, 0);
if ((t = find_string(defstr, "no-w", line, LINEMAX)) != NULL)
defs->nowait = strtol(t, NULL, 0);
defs->modedefs[mode].defined = TRUE;
if ((t = find_string(defstr, "block", line, LINEMAX)) != NULL)
defs->modedefs[mode].blocksize = strtol(t, NULL, 0);
if ((t = find_string(defstr, "dens", line, LINEMAX)) != NULL)
defs->modedefs[mode].density = strtol(t, NULL, 0);
if ((t = find_string(defstr, "buff", line, LINEMAX)) != NULL)
defs->modedefs[mode].buffer_writes = strtol(t, NULL, 0);
if ((t = find_string(defstr, "async", line, LINEMAX)) != NULL)
defs->modedefs[mode].async_writes = strtol(t, NULL, 0);
if ((t = find_string(defstr, "read", line, LINEMAX)) != NULL)
defs->modedefs[mode].read_ahead = strtol(t, NULL, 0);
if ((t = find_string(defstr, "two", line, LINEMAX)) != NULL)
defs->modedefs[mode].two_fm = strtol(t, NULL, 0);
if ((t = find_string(defstr, "comp", line, LINEMAX)) != NULL)
defs->modedefs[mode].compression = strtol(t, NULL, 0);
if ((t = find_string(defstr, "auto", line, LINEMAX)) != NULL)
defs->modedefs[mode].auto_lock = strtol(t, NULL, 0);
if ((t = find_string(defstr, "fast", line, LINEMAX)) != NULL)
defs->modedefs[mode].fast_eod = strtol(t, NULL, 0);
if ((t = find_string(defstr, "can-b", line, LINEMAX)) != NULL)
defs->modedefs[mode].can_bsr = strtol(t, NULL, 0);
if ((t = find_string(defstr, "noblk", line, LINEMAX)) != NULL)
defs->modedefs[mode].no_blklimits = strtol(t, NULL, 0);
if ((t = find_string(defstr, "can-p", line, LINEMAX)) != NULL)
defs->modedefs[mode].can_partitions = strtol(t, NULL, 0);
if ((t = find_string(defstr, "scsi2", line, LINEMAX)) != NULL)
defs->modedefs[mode].scsi2logical = strtol(t, NULL, 0);
if ((t = find_string(defstr, "sysv", line, LINEMAX)) != NULL)
defs->modedefs[mode].sysv = strtol(t, NULL, 0);
if ((t = find_string(defstr, "defs-for-w", line, LINEMAX)) != NULL)
defs->modedefs[mode].defs_for_writes = strtol(t, NULL, 0);
for (t=defstr; *t == ' ' || *t == '\t'; t++)
;
if (*t != '\0' && call_nbr <= 1) {
fprintf(stderr,
"Warning: errors in definition for ('%s', '%s', '%s'):\n%s\n",
tmpcomp, tmpprod, tmprev, defstr);
errors++;
}
}
}
if (parse_only) {
if (verbose > 0)
printf("\n");
printf("Definition parse completed. ");
if (errors) {
printf("Errors found!\n");
return FALSE;
}
else {
printf("No errors found.\n");
return TRUE;
}
}
else {
for (i=modes_defined=0; i < NBR_MODES; i++)
if (defs->modedefs[i].defined)
modes_defined++;
if (modes_defined == 0) {
fprintf(stderr,
"Warning: No modes in definition for ('%s', '%s', '%s').\n",
tmpcomp, tmpprod, tmprev);
errors++;
}
}
if (modes_defined)
return TRUE;
return FALSE;
}
#define INQUIRY 0x12
#ifndef SCSI_IOCTL_SEND_COMMAND
#define SCSI_IOCTL_SEND_COMMAND 1
#endif
#define IOCTL_HEADER_LENGTH 8
static int
do_inquiry(char *tname, char *company, char *product, char *rev, int print_non_found)
{
int fn;
int result, *ip, i;
#define BUFLEN 256
unsigned char buffer[BUFLEN], *cmd;
if ((fn = open(tname, O_RDONLY | O_NONBLOCK)) < 0) {
if (print_non_found || verbose > 0) {
if (errno == ENXIO)
fprintf(stderr, "Device '%s' not found by kernel.\n", tname);
else
fprintf(stderr, "Can't open tape device '%s' (errno %d).\n",
tname, errno);
}
return FALSE;
}
memset(buffer, 0, BUFLEN);
ip = (int *)&(buffer[0]);
*ip = 0;
*(ip+1) = BUFLEN - 13;
cmd = &(buffer[8]);
cmd[0] = INQUIRY;
cmd[4] = 200;
result = ioctl(fn, SCSI_IOCTL_SEND_COMMAND, buffer);
if (result) {
close(fn);
fprintf(stderr,
"The SCSI INQUIRY for device '%s' failed (power off?).\n",
tname);
return FALSE;
}
memcpy(company, buffer + IOCTL_HEADER_LENGTH + 8, 8);
for (i=8; i > 0 && company[i-1] == ' '; i--)
;
company[i] = '\0';
memcpy(product, buffer + IOCTL_HEADER_LENGTH + 16, 16);
for (i=16; i > 0 && product[i-1] == ' '; i--)
;
product[i] = '\0';
memcpy(rev, buffer + IOCTL_HEADER_LENGTH + 32, 4);
for (i=4; i > 0 && rev[i-1] == ' '; i--)
;
rev[i] = '\0';
close(fn);
return TRUE;
}
static int
tapenum(char *name)
{
int dev;
struct dirent *dent;
DIR *dirp;
char tmpname[PATH_MAX];
const char *dn;
const devdir *dvd;
struct stat statbuf;
if (strchr(name, '/') != NULL) { /* Complete name */
if (stat(name, &statbuf) != 0) {
fprintf(stderr, "Can't stat '%s'.\n", name);
return (-1);
}
if (!S_ISCHR(statbuf.st_mode) ||
major(statbuf.st_rdev) != SCSI_TAPE_MAJOR)
return (-1);
dev = minor(statbuf.st_rdev) & 31;
return dev;
}
else { /* Search from the device directories */
for (dvd=devdirs; dvd->dir != NULL; dvd++) {
dn = dvd->dir;
if ((dirp = opendir(dn)) == NULL)
continue;
for ( ; (dent = readdir(dirp)) != NULL; )
if (!strcmp(dent->d_name, name)) {
strcpy(tmpname, dn);
strcat(tmpname, "/");
strcat(tmpname, dent->d_name);
if (stat(tmpname, &statbuf) != 0) {
fprintf(stderr, "Can't stat '%s'.\n", tmpname);
continue;
}
if (!S_ISCHR(statbuf.st_mode) ||
major(statbuf.st_rdev) != SCSI_TAPE_MAJOR)
continue;
dev = minor(statbuf.st_rdev) & 31;
closedir(dirp);
return dev;
}
closedir(dirp);
}
}
return (-1);
}
static int
accept_tape_name(char *name)
{
char **npp;
for (npp=tape_name_bases; *npp; npp++)
if (!strncmp(name, *npp, strlen(*npp)))
return TRUE;
return FALSE;
}
static int
find_devfiles(int tapeno, char **names)
{
int dev, mode, found;
int non_rew[NBR_MODES];
struct dirent *dent;
DIR *dirp;
char tmpname[PATH_MAX], devname[PATH_MAX];
const char *dn;
const devdir *dvd;
devdir tmpdevdirs[2];
struct stat statbuf;
for (found=0; found < NBR_MODES; found++) {
*names[found] = '\0';
non_rew[found] = FALSE;
}
dvd = devdirs;
strcpy(tmpname, DEVFS_PATH);
if (!stat(tmpname, &statbuf)) {
if (S_ISDIR(statbuf.st_mode)) { /* Assume devfs, one directory for each tape */
sprintf(devname, DEVFS_TAPEFMT, tapeno);
tmpdevdirs[0].dir = devname;
tmpdevdirs[0].selective_scan = FALSE;
tmpdevdirs[1].dir = NULL;
dvd = &tmpdevdirs[0];
}
}
for (found=0; found < NBR_MODES && dvd->dir != NULL; dvd++) {
dn = dvd->dir;
if ((dirp = opendir(dn)) == NULL)
continue;
for ( ; (dent = readdir(dirp)) != NULL; ) {
if (!strcmp (dent->d_name, ".") || !strcmp (dent->d_name, ".."))
continue;
/* Ignore non-tape devices to avoid loading all the modules */
if (dvd->selective_scan && !accept_tape_name(dent->d_name))
continue;
strcpy(tmpname, dn);
strcat(tmpname, "/");
strcat(tmpname, dent->d_name);
if (stat(tmpname, &statbuf) != 0) {
fprintf(stderr, "Can't stat '%s'.\n", tmpname);
continue;
}
if (!S_ISCHR(statbuf.st_mode) || major(statbuf.st_rdev) !=
SCSI_TAPE_MAJOR)
continue;
dev = minor(statbuf.st_rdev);
if ((dev & 31) != tapeno)
continue;
mode = (dev & 127) >> 5;
if (non_rew[mode])
continue;
if (*names[mode] == '\0')
found++;
strcpy(names[mode], tmpname);
non_rew[mode] = (dev & 128) != 0;
}
closedir(dirp);
}
return (found > 0);
}
static int
set_defs(devdef_tr *defs, char **fnames)
{
int i, tape;
int clear_set[2];
struct mtop op;
for (i=0; i < NBR_MODES; i++) {
if (*fnames[i] == '\0' || !defs->modedefs[i].defined)
continue;
if ((tape = open(fnames[i], O_RDONLY | O_NONBLOCK)) < 0) {
fprintf(stderr, "Can't open the tape device '%s' for mode %d.\n",
fnames[i], i);
return FALSE;
}
if (i == 0) {
if (defs->do_rewind) {
op.mt_op = MTREW;
op.mt_count = 1;
ioctl(tape, MTIOCTOP, &op); /* Don't worry about result */
}
if (defs->drive_buffering >= 0) {
op.mt_op = MTSETDRVBUFFER;
op.mt_count = MT_ST_DEF_DRVBUFFER | defs->drive_buffering;
if (ioctl(tape, MTIOCTOP, &op) != 0) {
fprintf(stderr, "Can't set drive buffering to %d.\n",
defs->drive_buffering);
}
}
if (defs->timeout >= 0) {
op.mt_op = MTSETDRVBUFFER;
op.mt_count = MT_ST_SET_TIMEOUT | defs->timeout;
if (ioctl(tape, MTIOCTOP, &op) != 0) {
fprintf(stderr, "Can't set device timeout %d s.\n",
defs->timeout);
}
}
if (defs->long_timeout >= 0) {
op.mt_op = MTSETDRVBUFFER;
op.mt_count = MT_ST_SET_LONG_TIMEOUT | defs->long_timeout;
if (ioctl(tape, MTIOCTOP, &op) != 0) {
fprintf(stderr, "Can't set device long timeout %d s.\n",
defs->long_timeout);
}
}
if (defs->cleaning >= 0) {
op.mt_op = MTSETDRVBUFFER;
op.mt_count = MT_ST_SET_CLN | defs->cleaning;
if (ioctl(tape, MTIOCTOP, &op) != 0) {
fprintf(stderr, "Can't set cleaning request parameter to %x\n",
defs->cleaning);
}
}
}
op.mt_op = MTSETDRVBUFFER;
clear_set[0] = clear_set[1] = 0;
if (defs->nowait >= 0)
clear_set[defs->nowait != 0] |= MT_ST_NOWAIT;
if (defs->modedefs[i].buffer_writes >= 0)
clear_set[defs->modedefs[i].buffer_writes != 0] |= MT_ST_BUFFER_WRITES;
if (defs->modedefs[i].async_writes >= 0)
clear_set[defs->modedefs[i].async_writes != 0] |= MT_ST_ASYNC_WRITES;
if (defs->modedefs[i].read_ahead >= 0)
clear_set[defs->modedefs[i].read_ahead != 0] |= MT_ST_READ_AHEAD;
if (defs->modedefs[i].two_fm >= 0)
clear_set[defs->modedefs[i].two_fm != 0] |= MT_ST_TWO_FM;
if (defs->modedefs[i].fast_eod >= 0)
clear_set[defs->modedefs[i].fast_eod != 0] |= MT_ST_FAST_MTEOM;
if (defs->modedefs[i].auto_lock >= 0)
clear_set[defs->modedefs[i].auto_lock != 0] |= MT_ST_AUTO_LOCK;
if (defs->modedefs[i].can_bsr >= 0)
clear_set[defs->modedefs[i].can_bsr != 0] |= MT_ST_CAN_BSR;
if (defs->modedefs[i].no_blklimits >= 0)
clear_set[defs->modedefs[i].no_blklimits != 0] |= MT_ST_NO_BLKLIMS;
if (defs->modedefs[i].can_partitions >= 0)
clear_set[defs->modedefs[i].can_partitions != 0] |= MT_ST_CAN_PARTITIONS;
if (defs->modedefs[i].scsi2logical >= 0)
clear_set[defs->modedefs[i].scsi2logical != 0] |= MT_ST_SCSI2LOGICAL;
if (defs->modedefs[i].sysv >= 0)
clear_set[defs->modedefs[i].sysv != 0] |= MT_ST_SYSV;
if (defs->modedefs[i].defs_for_writes >= 0)
clear_set[defs->modedefs[i].defs_for_writes != 0] |= MT_ST_DEF_WRITES;
if (clear_set[0] != 0) {
op.mt_count = MT_ST_CLEARBOOLEANS | clear_set[0];
if (ioctl(tape, MTIOCTOP, &op) != 0) {
fprintf(stderr, "Can't clear the tape options (bits 0x%x, mode %d).\n",
clear_set[0], i);
}
}
if (clear_set[1] != 0) {
op.mt_count = MT_ST_SETBOOLEANS | clear_set[1];
if (ioctl(tape, MTIOCTOP, &op) != 0) {
fprintf(stderr, "Can't set the tape options (bits 0x%x, mode %d).\n",
clear_set[1], i);
}
}
if (defs->modedefs[i].blocksize >= 0) {
op.mt_count = MT_ST_DEF_BLKSIZE | defs->modedefs[i].blocksize;
if (ioctl(tape, MTIOCTOP, &op) != 0) {
fprintf(stderr, "Can't set blocksize %d for mode %d.\n",
defs->modedefs[i].blocksize, i);
}
}
if (defs->modedefs[i].density >= 0) {
op.mt_count = MT_ST_DEF_DENSITY | defs->modedefs[i].density;
if (ioctl(tape, MTIOCTOP, &op) != 0) {
fprintf(stderr, "Can't set density %x for mode %d.\n",
defs->modedefs[i].density, i);
}
}
if (defs->modedefs[i].compression >= 0) {
op.mt_count = MT_ST_DEF_COMPRESSION | defs->modedefs[i].compression;
if (ioctl(tape, MTIOCTOP, &op) != 0) {
fprintf(stderr, "Can't set compression %d for mode %d.\n",
defs->modedefs[i].compression, i);
}
}
close(tape);
}
return TRUE;
}
static int
define_tape(int tapeno, FILE *dbf, devdef_tr *defptr, int print_non_found)
{
int i;
char company[10], product[20], rev[5], *tname, *fnames[NBR_MODES];
if (verbose > 0)
printf("\nstinit, processing tape %d\n", tapeno);
if ((fnames[0] = calloc(NBR_MODES, PATH_MAX)) == NULL) {
fprintf(stderr, "Can't allocate name buffers.\n");
return FALSE;
}
for (i=1; i < NBR_MODES; i++)
fnames[i] = fnames[i-1] + PATH_MAX;
if (!find_devfiles(tapeno, fnames) ||
*fnames[0] == '\0') {
fprintf(stderr, "Can't find any device files for tape %d.\n", tapeno);
free(fnames[0]);
return FALSE;
}
if (verbose > 1)
for (i=0; i < NBR_MODES; i++)
printf("Mode %d, name '%s'\n", i, fnames[i]);
tname = fnames[0];
if (!do_inquiry(tname, company, product, rev, print_non_found)) {
free(fnames[0]);
return FALSE;
}
if (verbose > 0)
printf("The manufacturer is '%s', product is '%s', and revision '%s'.\n",
company, product, rev);
if (!find_pars(dbf, company, product, rev, defptr, FALSE)) {
fprintf(stderr, "Can't find defaults for tape number %d.\n", tapeno);
free(fnames[0]);
return FALSE;
}
if (!set_defs(defptr, fnames)) {
free(fnames[0]);
return FALSE;
}
free(fnames[0]);
return TRUE;
}
static char
usage(int retval)
{
fprintf(stderr,
"Usage: stinit [-h] [-v] [-f dbname] [-p] [drivename_or_number ...]\n");
exit(retval);
}
int
main(int argc, char **argv)
{
FILE *dbf = NULL;
int argn;
int tapeno, parse_only = FALSE;
char *dbname = NULL;
devdef_tr defs;
defs.do_rewind = FALSE;
for (argn=1; argn < argc && *argv[argn] == '-'; argn++) {
if (*(argv[argn] + 1) == 'v')
verbose++;
else if (*(argv[argn] + 1) == 'p')
parse_only = TRUE;
else if (*(argv[argn] + 1) == 'h')
usage(0);
else if (*(argv[argn] + 1) == 'r')
defs.do_rewind = TRUE;
else if (*(argv[argn] + 1) == 'f') {
argn += 1;
if (argn >= argc)
usage(1);
dbname = argv[argn];
}
else if (*(argv[argn] + 1) == '-' &&
*(argv[argn] + 2) == 'v') {
printf("stinit v. %s\n", VERSION);
exit(0);
break;
}
else
usage(1);
}
if ((dbf = open_database(dbname)) == NULL)
return 1;
if (parse_only) {
if (argc > argn)
fprintf(stderr, "Extra arguments on command line ignored.\n");
if (!find_pars(dbf, "xyz", "xyz", "xyz", &defs, TRUE))
return 1;
return 0;
}
if (argc > argn) { /* Initialize specific drives */
for ( ; argn < argc; argn++) {
if (*argv[argn] == '-') {
usage(1);
return 1; /* Never executed but makes gcc happy */
}
else if (isdigit(*argv[argn]))
tapeno = strtol(argv[argn], NULL, 0);
else if ((tapeno = tapenum(argv[argn])) < 0) {
fprintf(stderr, "Can't find tape number for name '%s'.\n",
argv[argn]);
continue;
}
if (!define_tape(tapeno, dbf, &defs, TRUE))
fprintf(stderr, "Definition for '%s' failed.\n", argv[argn]);
}
}
else { /* Initialize all SCSI tapes */
for (tapeno=0; tapeno < MAX_TAPES; tapeno++)
if (!define_tape(tapeno, dbf, &defs, FALSE)) {
fprintf(stderr, "Initialized %d tape device%s.\n",
tapeno, (tapeno != 1 ? "s" : ""));
return 0; /* Process tapes until failure */
}
}
return 0;
}

58
stinit.def.examples Normal file
View File

@@ -0,0 +1,58 @@
# This file contains example definitions for different kinds of tape
# devices. If the user agrees with the definitions, they can be used
# in the definition file stinit.def by changing the manufacturer and
# model fields to correspond the real tape device being defined.
# The common definitions that can usually be used
{buffer-writes read-ahead async-writes scsi2logical=1}
# A non-compressing DAT (DDS-1)
# The manufacturer, model, and revision strings can be obtained,
# from the file /proc/scsi/scsi (cat /proc/scsi/scsi).
manufacturer=XYZ model = "UVW1" {
scsi2logical=1 can-bsr can-partitions auto-lock
mode1 blocksize=0
mode2 blocksize=1024 }
# A compressing DAT (DDS-1-DC or DDS-[234])
manufacturer=XYZ model = "UVW2" {
can-bsr can-partitions auto-lock
mode1 blocksize=0 compression=1
mode2 blocksize=1024 compression=1
mode3 blocksize=0 compression=0
mode4 blocksize = 1024 compression=0 }
# A QIC-150 drive
manufacturer=XYZ model="UVW3" {
scsi2logical=0
mode1 # blocksize=512 defined by drive
}
# A QIC-320/525 drive
manufacturer=XYZ model="UVW4" {
defs-for-writes
mode1 blocksize=0 density=0x11 # QIC-320
mode2 blocksize=1024 density=0x11 # QIC-320
mode3 blocksize=512 density=0x10 # QIC-150
}
# Exabyte 8505 and other similar 8 mm helical scan driver
manufacturer=XYZ model = "UVW5" {
can-bsr auto-lock
cleaning=0x080821
mode1 blocksize=0 density=0x8c # 8500 density, compressing
mode2 blocksize=0 density=0x15 # 8500 density, no compression
mode3 blocksize=0 density=0x90 # 8200 density, compressing
mode4 blocksize=0 density=0x14 # 8200 density, no compression
}
# A reel-to-reel tape with 6250/1600/800 bpi densities
manufacturer=XYZ model = "UVW6" {
can-bsr two-fms
scsi2logical=0
mode1 blocksize=0 density=3 # 6250 bpi
mode2 blocksize=0 density=11 # 1600 bpi
mode3 blocksize=0 density=1 # 800 bpi
}