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