Copyright ©1997 Peter Urbanec. All rights reserved
EMail: peteru@null.net Phone: +61 2 9398 3456 Snail: Peter Urbanec PO Box 752 Randwick NSW 2031 AUSTRALIA
TAPE is a device driver for SCSI-2 tape devices. It is not called tape or Tape, the proper name is in all capitals - TAPE.
An associated program called tape
comes with the TAPE driver. It can be used
to control the operation of the TAPE driver. See the documentation
for tape for instructions on how to use it.
This file contains the documentation for the TAPE device driver. The documentation has two target audiences.
This document describes version 1.01 of the TAPE device driver.
Copy the file named TAPE
to /boot/system/drivers
The main goal is a minimal, yet sufficient driver for all your tape backup purposes. The driver implements a non-rewinding model, that is, there are no implicit rewind operations performed at any stage. The tape keeps on moving forward, unless you tell it otherwise. There is no planned compatibility with UNIX st, or any other platform or implementation for that matter. This is a BeOS native driver written from scratch.
The device driver automatically configures any SCSI-2 tape devices it can find. It will scan all SCSI buses and look for all supported targets and logical units. The driver supports WIDE SCSI-2 (upto 15 targets per bus.) The theoretical maximum number of tape drives that this driver will support is 1050. 10 SCSI-buses, 15 targets per bus, 7 logical units per target.
The devices are named /dev/scsi/tape/XYZ
, where X
is the
bus number and can range from 0 to 9. Y
is the target number, also
known as the SCSI id of the device. It can range from 0 to 7 for standard 8-bit
SCSI configurations and from 0 to F (ie. all valid hex digits) for exclusively
WIDE SCSI configurations. Z
is the logical unit number, also known
as LUN. Some tape libraries use the LUN to select which tape to use. Normal desktop
style tape drives usually only respond to LUN 0.
A lot of care and time was spent studying the SCSI-2 specification and making sure that the driver only utilises features that must be provided by all SCSI-2 compliant units. The driver implements very few operations which use optional SCSI-2 commands, all of these are non-essential to correct operation of basic data transfer.
The driver has code to completely decode all SCSI-2 specified sense information and present it in a very usable manner to the application. Error handling is a piece of cake for the clients.
I am very reluctant to add support for vendor specific extensions to this driver. As much as this would allow users to take advantage of any extras that a particular vendor might provide, it would hurt the uniform API to ALL SCSI-2 tape devices and in the long run make life harder for both, the application writers and end users.
If you would like to have some special features supported for a custom solution, I will be more than happy to negotiate a deal to provide a custom driver. Please contact me as indicated above if you wish to negotiate a deal.
Although the driver has been in daily use and tested extensively for a few weeks before release, the author does not provide any guarantees or warranties as far as the performance of the software is concerned and shall not be held liable for any losses associated with the use of this software. If you do find bugs or compatibility problems, please report them so that they can be fixed.
When reporting bugs or incompatibilities, please include the following information:
"tape DEVICE_NAME info"
command.
Examination of the SCSI-3 draft documents promises to enhance, but also complicate the interface to sequential devices even further than SCSI-2. My plan is to support SCSI-3 when it is ratified and products begin to appear on the market. One of the great things about SCSI-3 is the fact that the delivery mechanism can be a lot more diverse than the current SCSI-2 specification allows. SCSI-3 will run over your FireWire, SSA or even fibre channel connections. All this will ofcourse require CAM-3 support in BeOS ...
I believe that the compiler is (most of the time) your friend. It is good at catching silly bugs. If you let it. One of the best ways of letting the compiler help you is to make a good use of types.
The ioctl()
philosophy seems to be exactly the opposite. All it gives
you is a pointer to a void. Good luck sorting it all out. Well, things are not that
bad. It only takes a couple of structures and you can come up with a workable interface
that the compiler will be able to check for type correctness.
Now, keep in mind that since this is a kernel level driver, it is written in C, not C++. The good thing is, that you can still keep the compiler in C++ mode to get the convenience of doing such things as being able to declare variables anywhere you like.
Now back to ioctl()
calls and the philosophy behind them. The idea is,
that every command that needs to take arguments or return some data has a command
structure associated with it. To make it easier to work with, the naming is kept consistent
between the commands and their command structures. For example, if a command to get
blerks from the drive exists, the command would be called TAPE_CMD_GET_BLERKS
and the corresponding structure would be called TAPE_GET_BLERKS
. The
structure would of course contain all the parameters required to communicate a blerk.
See TAPE.h
for concrete examples of real commands.
Now, when you actually want to go and ask the tape drive for some blerks, you create
a TAPE_GET_BLERKS
structure and fill in its contents as appropriate.
You then call the ioctl()
like this:
TAPE_GET_BLERKS *blerker = (TAPE_GET_BLERKS *) malloc(sizeof(TAPE_GET_BLERKS)); // Fill in blerker with details status_t result = ioctl(tape_file, TAPE_CMD_GET_BLERKS, blerker);
Some commands do not require any arguments and do not return any data (except for
success or failure), such commands do not have a structure defined for them. For
example, the command TAPE_CMD_EXPLODE
does not need any arguments.
status_t result = ioctl(tape_file, TAPE_CMD_EXPLODE, NULL);If there wasn't enough semtex in the drive to complete the operation, the
ioctl()
would return a failure.
OK, so here are the commands that are available, together with a bit of a description for each one of them.
TAPE_CMD_REWIND
Arguments: noneReturns when the medium has been positioned at the beginning of recordable area. If there was data written to the tape, a filemark will be written at the current position before the tape is rewound.
TAPE_CMD_EJECT
Arguments: noneAttempts to physically eject medium from the drive if the device is capable of this. An eject often (not always) implies a rewind of the medium. If there was data written to the tape, a filemark will be written at the current position before the tape is ejected.
TAPE_CMD_FILE_SEEK
Arguments:file_count
- the number of filemarksWill cause the medium to be re-positioned
file_count
filemarks from the current position. Negative numbers will move backwards, positive numbers will move forwards. The resulting position will be the begining of the first block after the filemark. If there was data written to the tape, a filemark will be written at the current position before the tape is repositioned.
TAPE_CMD_GET_BLOCK_SIZES
Arguments:smallest
- the smallest supported block size
largest
- the largest supported block sizeReports the range of block sizes supported by the tape drive. Tape drives which only support fixed size blocks will return the same number in both variables. The maximum block size achievable is 16 MB, which is certainly not a recommended default value. Use your discretion when choosing block size for your application, but think hard. Here are some points to consider:
- When you write a block of a certain size, you must read it as that size, otherwise you will get an error. The
residue
field if theTAPE_GET_LAST_ERROR
structure will tell you by how much you differed from the real block size, however this will only be possible if the device can determine what the correct block size should have been.- Tar uses default block size equivalent to 10 kB
- Keeping block sizes even is a good idea.
- Making block sizes a power of two might improve performance on some hardware
- Small block sizes will cause a lot of overhead and slow everything down
Given those comments above, my suggestion would be to pick an upper limit on block size in your application (64 k seems like a good number) and then use which ever is smaller out of the limit or the
largest
value. Once you pick your block size, stick to it. It is possible to write every block with different size, but it might be a bit difficult to read your data back.
TAPE_CMD_GET_LAST_ERROR
Arguments:severity
- how serious was the error
residue
- the difference between the request and actual operation
key
- SCSI-2 sense key
asc
- SCSI-2 ASC
ascq
- SCSI-2 ASCQ
message
- the text of the error message (NULL
terminated)
This
ioctl()
call will always returnB_NO_ERROR
and fill in the arguments with the last encountered error values. If there were no previous errors, the return values are undefined. Note that this information is not preserved acrossclose()
andopen()
calls, it is therefore impossible to use one application to find out which tape related error occured in another application.It is suggested that this command be used as soon as an error occurs, however, the values returned will be those of the last error condition, not just the last call. In particular, do not use this command to check for errors. Use the return values from
read()
,write()
andioctl()
to discover the existence of an error condition, then use this command to find out the severity and cause.All arguments except for
severity
andmessage
are bitwise copies of the SCSI-2 defined fields. See the SCSI-2 standard for a detailed break down of all tape related error conditions.
severity
is an attempt at simplifying the application programmers life. The errors are classified into different severities depending on their impact. Here are some guidelines as to how to treat different severities:
TAPE_SEVERITY_UNKNOWN
- This is most commonly caused by a vendor specific error. The driver has no idea how serious the problem is. The application should show the details to the user and let her/him decide how to handle the situation, unless the application knows how to decipher and handle the error.TAPE_SEVERITY_INFO
- This is just an informational message which flags an event that should have not caused any loss of data, but the application might have been interested in. (eg. passing a setmark )TAPE_SEVERITY_WARNING
- Something is going on, the user should probably be aware of the warning, however no data should have been lost. The application can continue, but if warnings persist should probably take action. (eg. Recovered read/write errors)TAPE_SEVERITY_ERROR
- An error caused data loss. Intervention is probably required to correct this problem. (eg. End of tape)TAPE_SEVERITY_FAILURE
- A key component in the system has failed, causing the drive to be unusable. Human intervention is almost certainly required, possibly requiring drive repair or replacement. (eg. Tape jam, RAM failure)
message
is a text string that the client might want to record in a log or present to the user as part of an error message. It is aNULL
terminated ASCII string, as specified in the SCSI-2 standard. The string is copied into the message buffer.
TAPE_CMD_WRITE_FILEMARK
Arguments: noneWrites a filemark to tape. Filemarks are used to divide the tape into multiple files. Typically filemarks are used to implement different save sets and backups and provide fast positioning on the tape.
Be carefull when using filemarks, they should be only written in sequence with data. Some devices might erase a large section of physical media after writing a filemark, that is why you should never attempt to write any filemarks into the middle of existing data if you wish to preserve this data.
Some devices use two filemarks in a row to mark the end of tape. It is in your best interest to not write two filemarks without at least some data in between them if you wish to remain compatible with all devices.
TAPE_CMD_GET_WRITE_PROTECT
Arguments:protection
- true if tape is write protectedReports the current status of the write protect flag for the media. It is a good idea to check this flag before attempting to write to the media.
TAPE_CMD_GET_WRITE_BUFFERING
TAPE_CMD_SET_WRITE_BUFFERING
Arguments:buffering
- true if drive is buffering writesDo not change the default setting carelessly. Turning off buffering might prevent a device from streaming data, resulting in horrible performance, waste of tape, excessive repositioning and unnecessary wear on the mechanism. On the other hand, some devices prefer not to buffer writes as they might not be able to write out the entire buffer if they encounter an early end of tape warning. This is a rare case, however if you do encounter it, the residue field should tell you how much data remains unwritten.
TAPE_CMD_GET_DATA_COMPRESSION
TAPE_CMD_SET_DATA_COMPRESSION
Arguments:compression
- true if compression is enabledSince most drives default to the best speed / capacity configuration, you should not need to change the compression setting. In rare circumstances, you might want to disable compression to achieve backward compatibility with older drives. Note that not all drives support compression. Some devices which do support compression may not honour this flag. A notable example of this is the SONY SDT-5000 drive.
TAPE_CMD_GET_VERSION_STRING
Arguments:version
- NULL terminated version stringThis command provides a way for an application to report to the user the version of the TAPE driver currently in use. The application may also use this to provide compatibility between different versions of this driver, should the need arise in the future.
The format of the string is fairly simple and not too difficult to parse. There are 5 fields with separators between them:
"<NAME> <VERSION>.<REVISION> (<DATE>) <COPYRIGHT>
"The
<NAME>
field is"TAPE_Driver"
followed by a space.
<VERSION>
is a decimal integer numerical quantity, suitable for conversion to an int. For examplever=atoi(VERSION)
<REVISION>
is a decimal integer quantity, just like<VERSION>
. Note that<REVISION>
may or may not have leading zeros, but it is always treated as an integer, not a fractional part of the<VERSION>
field. Revisions 0001, 01 and 1 are exactly the same, they are both earlier revisions than 10. To re-iterate this; the revisions would chronologicaly progress in this sequence: ... 7, 8, 9, 10, 11, 12, ... The<REVISION>
field is terminated by a space
<DATE>
is the string generated by the__DATE__
directive given to the compiler. It is in whatever format the Metrowerks CodeWarrior compiler will generate. If / when BeOS supports localisation and if the Metrowerks__DATE__
implementation will respect it, the format will be as defined by the Australia/NSW timezone. I would advise to treat this field as informative only and not to try to compare different versions of the driver based on this field. Because this field could become unpredictable depending on localisation support, it is enclosed in brackets to allow for easy parsing. The entire<DATE>
field is terminated by a space.
<COPYRIGHT>
is the rest of the string, up to the terminating NULL. It contains author and copyright information for the driver.