A3070 Tape Drive programming
Frank J. Edwards
crash at ckctpa.UUCP
Thu Jun 20 14:55:17 AEST 1991
Where should this have been posted to? alt.sources? c.s.a.programmer?
I'm listening ...
In article <1991Jun19.100002.26682 at NCoast.ORG> lesle at NCoast.ORG (David A. Lesle) writes:
>Is there any way to program the A3070 tape drive to read tapes
>of lower density than 150M? I have several tape cartridges with
>tar files from a Sun system circa 1989 that I would like to read.
>
>I remember that the Sun had several different devices depending
>on which density you wanted to read, but I can't find anything
>like that in the UNIX documentation. I'd like to do this under
>either AmigaDOS or UNIX so any help would be appreciated.
Well, here are a couple of general purpose programs to manipulate the
tape drive directly using SCSIDirect, ie. talking to the scsi.device .
Don't complain about the coding style or I'll never again write another
piece of code! ;-) Seriously, though, I've since added more options than
what is given here, but they're still useful. As soon as I pull out
a more recent backup I'll post the newer ones if anyone's interested.
>------------------------------------------------------------------------------
>David Lesle uucp: ncoast!lesle
>CLEVELAND AREA-AMIGA USERS' GROUP (CA-AUG) (216) 642-3344 (BBS)
>------------------------------------------------------------------------------
[Hey! say hello to Brandon at NCoast, huh? He won't remember me but I do him!]
Here's how I tend to use these things:
1.System2.0:> tctl rewind ; rewind the tape
1.System2.0:> tctl reqblk ; request current block address
1
1.System2.0:> tctl fsf 1 ; forward space to next (1) filemark
1.System2.0:> tctl fsb -200 ; move backward 200 blocks
1.System2.0:> tctl mselect 1 60 ; buffered, 60MB mode
1.System2.0:> tctl mselect 0 120 ; unbuffered, 120MB mode
1.System2.0:> tctl mselect 1 150 ; buffered, 150MB mode
; normally the drive auto-syncs on read
1.System2.0:> tctl msense ; what is the current mode?
1.System2.0:> tctl seek 94300 ; seek to...
1.System2.0:> tctl reqblk
94300
1.System2.0:>
There are other options as well; play around a little... This next
one is handy when you've accidentally blown away your BTN tape-handler
(nice program, huh?) and you need something to read your tapes. Now that
I've started using BRU for everything, this isn't as necessary...
1.System2.0:> run scsird -o pipe:tar ; send tape data to PIPE:
[CLI 4]
1.System2.0:> tar -xvf pipe:tar ; extract files
I have similar programs for Amix, but everytime you re-open the tape
device (/dev/rmt/4*) the drive gets re-initialized (unfortunate, but
necessary) so you can make these changes or position the tape, but when
you again access the tape the only thing that remains is the tape position.
------ cut here ------ cut here ------ cut here ------ cut here ------
#!/bin/sh
# This is a shell archive. Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# This archive contains:
# Makefile scsird.c tctl.c
#
echo x - Makefile
sed -e 's/^X//' >Makefile <<'@EOF'
X# Copyright (c) 1991 by Frank J. Edwards
X# All Rights Reserved.
X#
XCC = cc
XCFLAGS = -bs -wl -DFJE
XLD = ln
XLDFLAGS = -g -w
XLIBS = -lc
X
Xtctl: tctl.o getopt.o
X ln -o $@ tctl.o getopt.o $(LDFLAGS) $(LIBS)
X
Xscsird: scsird.o getopt.o
X ln -o $@ scsird.o getopt.o $(LDFLAGS) $(LIBS)
@EOF
chmod 666 Makefile
echo x - scsird.c
sed -e 's/^X//' >scsird.c <<'@EOF'
X/* Copyright (c) 1991 by Frank J. Edwards
X * All Rights Reserved.
X */
X#include <stdio.h>
X#include <fcntl.h>
X#include <string.h>
X#include <devices/scsidisk.h>
X#include <proto/exec.h>
X
Xstruct reqsense {
X UBYTE bits[3];
X UBYTE rlen[4];
X UBYTE addlen;
X UBYTE srcptr, destptr;
X UBYTE recerrs[4];
X} rs;
X#define REQ_SIZE (UBYTE) sizeof(struct reqsense)
X#define CLASS 0x70
X#define ERRCODE 0x0F
X#define SEGMENT 0xFF
X#define FILEMARK 0x80
X#define EOM 0x40
X#define SENSEKEY 0x0F
X#define LONG(x) ((x[0]<<24) | (x[1]<<16) | (x[2]<<8) | (x[3]))
X
Xtypedef UBYTE Block[512];
Xchar *sensekey(int sense);
Xint DoSCSI(struct IOStdReq *ior);
X
Xextern int optind;
Xextern char *optarg;
Xchar *prog;
X
XUBYTE readcmd[] = { 8, 1, 0, 0, 0, 0 };
XUBYTE rewindcmd[] = { 1, 1, 0, 0, 0, 0 };
Xstruct SCSICmd sc;
Xstruct reqsense sensedata;
Xstruct MsgPort *port;
Xstruct IOStdReq *ior;
X
Xvoid cleanup(void)
X{
X if (ior->io_Device)
X CloseDevice((struct IORequest *) ior), ior->io_Device = 0;
X if (ior)
X DeleteStdIO(ior), ior = 0;
X if (port)
X DeletePort(port), port = 0;
X}
X
Xvoid usage(int ier, char *fmt, ...)
X{
X va_list args;
X
X if (fmt) {
X va_start(args, fmt);
X vfprintf(stderr, fmt, args);
X va_end(args);
X }
X fprintf(stderr, "Usage: %s [-b #] [-r] [-t] -o outfile\n", prog);
X exit( ier );
X}
X
Xmain(int argc, char **argv)
X{
X char *fname;
X int err, out, ch, rewind, reten;
X Block *buffer;
X ULONG nblks = 100, bufsiz;
X
X if ((prog = strrchr(*argv,'/')) || (prog = strrchr(*argv,':')))
X prog++;
X else
X prog = *argv;
X fname = NULL;
X rewind = FALSE;
X reten = FALSE;
X
X while ((ch = getopt(argc, argv, "b:rto:")) != EOF) {
X switch (ch) {
X case 'b' :
X nblks = atol(optarg);
X break;
X case 'r' :
X rewind = TRUE;
X break;
X case 't' :
X fprintf(stderr, "Retensioning is unimplemented.\n");
X /* reten = TRUE; */
X break;
X case 'o' :
X if (fname)
X usage(1, "Specified output file twice!\n");
X fname = optarg;
X break;
X default :
X usage(1, "Illegal option -%c\n", ch);
X }
X }
X buffer = (Block *) malloc(bufsiz = sizeof(Block) * nblks);
X if (!buffer) {
X fputs("Couldn't allocate buffer!\n", stderr);
X exit( 10 );
X }
X init_io( &port, &ior );
X ior->io_Command = HD_SCSICMD;
X ior->io_Data = (APTR) ≻
X ior->io_Length = sizeof(sc);
X
X sc.scsi_SenseData = (UBYTE *) &sensedata;
X sc.scsi_SenseLength = (UWORD) sizeof(sensedata);
X sc.scsi_Data = (UWORD *) buffer;
X sc.scsi_Length = (ULONG) bufsiz;
X
X if (rewind) {
X sc.scsi_Command = (UBYTE *) rewindcmd;
X sc.scsi_CmdLength = (UWORD) sizeof(rewindcmd);
X sc.scsi_Flags = (UBYTE) SCSIF_READ | SCSIF_AUTOSENSE;
X if (err = DoSCSI(ior))
X exit( 20 );
X }
X#if 0
X if (reten) {
X sc.scsi_Command = (UBYTE *) retencmd;
X sc.scsi_CmdLength = (UWORD) sizeof(retencmd);
X sc.scsi_Flags = (UBYTE) SCSIF_READ | SCSIF_AUTOSENSE;
X if (err = DoSCSI(ior))
X exit( 20 );
X }
X#endif
X sc.scsi_Command = (UBYTE *) readcmd;
X sc.scsi_CmdLength = (UWORD) sizeof(readcmd);
X sc.scsi_Flags = (UBYTE) SCSIF_READ | SCSIF_AUTOSENSE;
X
X readcmd[2] = (bufsiz >> 25) & 0xFF; /* ((siz/512) >> 16) */
X readcmd[3] = (bufsiz >> 17) & 0xFF; /* ((siz/512) >> 8) */
X readcmd[4] = (bufsiz >> 9) & 0xFF;
X
X if (!fname)
X usage(10, "Must specify an output filename!\n");
X out = open(fname, O_WRONLY | O_CREAT | O_TRUNC);
X ior->io_Error = 0;
X while (!ior->io_Error) {
X if (err = DoSCSI(ior)) {
X fprintf(stderr, "Error %d during DoSCSI()!\n", err);
X break;
X } else if (sc.scsi_Actual) {
X if (sc.scsi_Actual != bufsiz)
X fprintf(stderr, "Short block (%ld bytes)\n", sc.scsi_Actual);
X if (write(out, buffer, sc.scsi_Actual) != sc.scsi_Actual)
X perror(fname);
X } else
X fprintf(stderr, "No data transferred.\n");
X }
X close(out);
X if (rewind) {
X sc.scsi_Command = (UBYTE *) rewindcmd;
X sc.scsi_CmdLength = (UWORD) sizeof(rewindcmd);
X sc.scsi_Flags = (UBYTE) SCSIF_READ | SCSIF_AUTOSENSE;
X (void) DoSCSI(ior);
X }
X return( err );
X}
X
Xinit_io(struct MsgPort **Port, struct IOStdReq **Ior)
X{
X int err;
X
X atexit(cleanup);
X if (! (*Port = CreatePort(0L, 0L)) ) {
X fputs("Couldn't CreatePort()!\n", stderr);
X exit( 10 );
X }
X if (! (*Ior = CreateStdIO(*Port)) ) {
X fputs("Couldn't CreateStdIO()!\n", stderr);
X exit( 10 );
X }
X err = 2;
X if (err = OpenDevice("scsi.device", err, (struct IORequest *) *Ior, 0)) {
X fprintf(stderr, "Error %d from OpenDevice()!\n", err);
X exit( 10 );
X }
X return( 0 );
X}
X
Xchar *sensekey(int sense)
X{
X static char *SenseKey[] = {
X "FM/EOM or no status available",
X "Recovered from error; command successful",
X "Not Ready",
X "Medium Error",
X "Hardware Error",
X "Illegal Request (illegal parameter in CDB)",
X "Unit Attention (cartridge changed or Viper reset)",
X "Data Protect (cartridge is write-portected)",
X "Blank Check (no-data condition found on tape)",
X "Vendor Unique Code -- not used on Viper",
X "-- undocumented error --",
X "Aborted Command (may retry)",
X "-- undocumented error --",
X "Volume Overflow (internal buffer may contain data)",
X };
X#define KEY_SIZE sizeof(SenseKey)
X return( SenseKey[sense] );
X}
X
Xint DoSCSI(struct IOStdReq *ior)
X{
X int err;
X
X while (err = DoIO( (struct IORequest *)ior )) {
X struct SCSICmd *scsicmd = (struct SCSICmd *) ior->io_Data;
X struct reqsense *rs;
X
X if (err!=HFERR_BadStatus || !(scsicmd->scsi_Flags&SCSIF_AUTOSENSE)) {
X fprintf(stderr, "Got error %d from DoIO()!\n", err);
X return( err );
X }
X rs = (struct reqsense *) scsicmd->scsi_SenseData;
X if (err = rs->bits[2]) {
X if (err & FILEMARK) {
X fputs("Filemark.\n", stderr);
X return( err );
X } else if (err & EOM) {
X fputs("End-of-Media.\n", stderr);
X return( err );
X } else {
X err &= SENSEKEY;
X fprintf(stderr, "sense key = %d (%s)\n", err, sensekey(err));
X if (err == 1) /* Recovered Error */
X break;
X else if (err != 6) /* Unit Attention */
X return( err );
X }
X }
X }
X return( 0 );
X}
@EOF
chmod 666 scsird.c
echo x - tctl.c
sed -e 's/^X//' >tctl.c <<'@EOF'
X/* Copyright (c) 1991 by Frank J. Edwards
X * All Rights Reserved.
X */
X#include <stdio.h>
X#include <fcntl.h>
X#include <string.h>
X#include <stdarg.h>
X#include <exec/types.h>
X#include <exec/io.h>
X#include <devices/scsidisk.h>
X#include <clib/exec_protos.h>
X
Xextern struct MsgPort *CreatePort(char *, long);
Xextern struct IOStdReq *CreateStdIO(struct MsgPort *);
X
Xstruct reqsense {
X UBYTE bits[3];
X UBYTE rlen[4];
X UBYTE addlen;
X UBYTE srcptr, destptr;
X UBYTE recerrs[4];
X} rs;
X#define REQ_SIZE (UBYTE) sizeof(struct reqsense)
X#define CLASS 0x70
X#define ERRCODE 0x0F
X#define SEGMENT 0xFF
X#define FILEMARK 0x80
X#define EOM 0x40
X#define SENSEKEY 0x0F
X#define LONG(x) ((x[0]<<24) | (x[1]<<16) | (x[2]<<8) | (x[3]))
X
Xchar *sensekey(int sense);
Xint DoSCSI(struct IOStdReq *ior);
X
Xextern int optind;
Xextern char *optarg;
Xchar *prog;
X
XUBYTE trewind[] = { 1, 0, 0, 0, 0, 0 };
XUBYTE treqblk[] = { 2, 0, 0, 0, 0, 0 }; /* blockaddr returned in [2..4] */
XUBYTE tseekblk[] = { 12, 0, 0, 0, 0, 0 }; /* blockaddr into [2..4] */
XUBYTE twritefm[] = { 16, 0, 0, 0, 0, 0 }; /* count into [2..4] */
XUBYTE tspace[] = { 17, 0, 0, 0, 0, 0 }; /* (tspace[1] & 03) == SPACE_TYPE */
XUBYTE tverify[] = { 19, 1, 0, 0, 0, 0 }; /* length into [2..4] */
XUBYTE terase[] = { 25, 1, 0, 0, 0, 0 };
X
Xstruct SCSICmd sc;
Xstruct reqsense sensedata;
Xstruct MsgPort *port;
Xstruct IOStdReq *ior;
X
Xvoid cleanup(void)
X{
X if (ior->io_Device)
X CloseDevice((struct IORequest *) ior), ior->io_Device = 0;
X if (ior)
X DeleteStdIO(ior), ior = 0;
X if (port)
X DeletePort(port), port = 0;
X}
X
Xchar *cmdlist[] = {
X#define REWIND 0
X "rewind", /* Rewind the tape */
X/* "reset", /* Reset the controller */
X/* "sense", /* Print out request sense data */
X#define REQBLK 1
X#define SEEK 2
X#define FSB 3
X#define FSF 4
X#define FSSF 5
X#define EOD 6
X "reqblk", /* Print out current block position */
X "seek", /* Seek to given block */
X "fsb", /* Forward Space Blocks */
X "fsf", /* Forward Space Filemarks */
X "fssf", /* Forward Space Sequential Filemarks */
X "eod", /* Forward to EOD */
X#define ERASE 7
X#define WRITEFM 8
X "erase", /* Erase the tape */
X "writefm", /* Write filemarks on tape */
X#define VERIFY 9
X "verify", /* Run CRC verification on # blocks */
X NULL };
Xchar **cmdp;
X
Xvoid usage(int ier, char *fmt, ...)
X{
X if (fmt) {
X va_list args;
X
X va_start(args, fmt);
X vfprintf(stderr, fmt, args);
X va_end(args);
X }
X fprintf(stderr, "Usage: %s [-s #] { command }\n", prog);
X fputs(" -s specifies the SCSI ID of the tape drive, and\n", stderr);
X fputs(" the rest are: rewind, eod, reqblk, verify, and\n", stderr);
X fputs(" erase don't take parameters; seek, fsb, fsf, fssf,\n", stderr);
X fputs(" and writefm do.\n", stderr);
X
X exit( ier );
X}
X
Xvoid encode_size(register UBYTE *cmd, register int offset, register long pos)
X{
X cmd[offset++] = (pos >> 16) & 0xFF;
X cmd[offset++] = (pos >> 8) & 0xFF;
X cmd[offset] = ( pos ) & 0xFF;
X}
X
Xmain(int argc, char **argv)
X{
X int err, cmd, scsi_id = 2; /* My tape drive is at "2" */
X long value;
X
X if ((prog = strrchr(*argv,'/')) || (prog = strrchr(*argv,':')))
X prog++;
X else
X prog = *argv;
X
X if (argc > 1 && !strcmp(argv[1], "-s")) {
X if (argc > 2)
X scsi_id = atoi(argv[2]);
X argc -= 2;
X argv += 2;
X }
X if (argc < 2)
X usage(5, NULL);
X
X init_io( scsi_id, &port, &ior );
X ior->io_Command = HD_SCSICMD;
X ior->io_Data = (APTR) ≻
X ior->io_Length = sizeof(sc);
X
X sc.scsi_SenseData = (UBYTE *) &sensedata;
X sc.scsi_SenseLength = (UWORD) sizeof(sensedata);
X sc.scsi_Data = (UWORD *) 0;
X sc.scsi_Length = (ULONG) 0;
X
X while (argc-- > 1) {
X ++argv;
X for (cmdp = cmdlist; *cmdp; cmdp++)
X if (!strcmp(*cmdp, *argv))
X break;
X if (!*cmdp)
X usage(5, "Unknown option `%s'\n", *argv);
X cmd = cmdp - cmdlist;
X if (cmd == FSB || cmd == FSF || cmd == FSSF ||
X cmd == SEEK || cmd == WRITEFM) {
X if (argc-- > 1)
X value = atol(*++argv);
X else
X usage(5, "Required value for `%s' missing\n", *argv);
X }
X if (err = doit(cmd, value))
X break;
X }
X return( err );
X}
X
Xint doit(int cmd, long value)
X{
X int err;
X unsigned char reqblkd[3];
X
X sc.scsi_Flags = (UBYTE) SCSIF_READ | SCSIF_AUTOSENSE;
X switch (cmd) {
X case REWIND :
X sc.scsi_Command = (UBYTE *) trewind;
X sc.scsi_CmdLength = (ULONG) sizeof(trewind);
X break;
X#if 0
X case RESET :
X sc.scsi_Command = (UBYTE *) treset;
X sc.scsi_CmdLength = (ULONG) sizeof(treset);
X break;
X case SENSE :
X sc.scsi_Command = (UBYTE *) treqsen;
X sc.scsi_CmdLength = (ULONG) sizeof(treqsen);
X break;
X#endif
X case REQBLK :
X sc.scsi_Command = (UBYTE *) treqblk;
X sc.scsi_CmdLength = (ULONG) sizeof(treqblk);
X sc.scsi_Data = (UWORD *) reqblkd;
X sc.scsi_Length = (ULONG) 3;
X break;
X case SEEK :
X sc.scsi_Command = (UBYTE *) tseekblk;
X sc.scsi_CmdLength = (ULONG) sizeof(tseekblk);
X encode_size(tseekblk, 2, value);
X break;
X case FSB :
X sc.scsi_Command = (UBYTE *) tspace;
X sc.scsi_CmdLength = (ULONG) sizeof(tspace);
X tspace[1] = 0;
X encode_size(tspace, 2, value);
X break;
X case FSF :
X sc.scsi_Command = (UBYTE *) tspace;
X sc.scsi_CmdLength = (ULONG) sizeof(tspace);
X tspace[1] = 1;
X encode_size(tspace, 2, value);
X break;
X case FSSF :
X sc.scsi_Command = (UBYTE *) tspace;
X sc.scsi_CmdLength = (ULONG) sizeof(tspace);
X tspace[1] = 2;
X encode_size(tspace, 2, value);
X break;
X case EOD :
X sc.scsi_Command = (UBYTE *) tspace;
X sc.scsi_CmdLength = (ULONG) sizeof(tspace);
X tspace[1] = 3;
X break;
X case ERASE :
X sc.scsi_Command = (UBYTE *) terase;
X sc.scsi_CmdLength = (ULONG) sizeof(terase);
X break;
X case WRITEFM :
X sc.scsi_Command = (UBYTE *) twritefm;
X sc.scsi_CmdLength = (ULONG) sizeof(twritefm);
X encode_size(twritefm, 2, value);
X break;
X case VERIFY :
X sc.scsi_Command = (UBYTE *) tverify;
X sc.scsi_CmdLength = (ULONG) sizeof(tverify);
X encode_size(tverify, 2, value);
X break;
X }
X ior->io_Error = 0;
X if (err = DoSCSI(ior))
X fprintf(stderr, "Error %d during DoSCSI()!\n", err);
X if (cmd == REQBLK)
X printf("%ld\n", (reqblkd[0] << 16) | (reqblkd[1] << 8) | reqblkd[2]);
X return( err ? err : ior->io_Error );
X}
X
Xinit_io(int scsi_id, struct MsgPort **Port, struct IOStdReq **Ior)
X{
X int err;
X
X atexit(cleanup);
X if (! (*Port = CreatePort(0L, 0L)) ) {
X fputs("Couldn't CreatePort()!\n", stderr);
X exit( 10 );
X }
X if (! (*Ior = CreateStdIO(*Port)) ) {
X fputs("Couldn't CreateStdIO()!\n", stderr);
X exit( 10 );
X }
X if (err = OpenDevice("scsi.device", scsi_id, (struct IORequest *) *Ior, 0)) {
X fprintf(stderr, "Error %d from OpenDevice()!\n", err);
X exit( 10 );
X }
X return( 0 );
X}
X
Xchar *sensekey(int sense)
X{
X static char *SenseKey[] = {
X "FM/EOM or no status available",
X "Recovered from error; command successful",
X "Not Ready",
X "Medium Error",
X "Hardware Error",
X "Illegal Request (illegal parameter in CDB)",
X "Unit Attention (cartridge changed or Viper reset)",
X "Data Protect (cartridge is write-portected)",
X "Blank Check (no-data condition found on tape)",
X "Vendor Unique Code -- not used on Viper",
X "-- undocumented error --",
X "Aborted Command (may retry)",
X "-- undocumented error --",
X "Volume Overflow (internal buffer may contain data)",
X };
X#define KEY_SIZE sizeof(SenseKey)
X return( SenseKey[sense] );
X}
X
Xint DoSCSI(struct IOStdReq *ior)
X{
X register int err, retry = 4;
X
X while ((err = DoIO((struct IORequest *)ior)) && (retry-- > 0)) {
X struct SCSICmd *scsicmd = (struct SCSICmd *) ior->io_Data;
X struct reqsense *rs;
X
X if (err != HFERR_BadStatus) {
X fprintf(stderr, "Got error %d from DoIO()!\n", err);
X return( err );
X }
X rs = (struct reqsense *) scsicmd->scsi_SenseData;
X if (err = rs->bits[2]) {
X if (err & FILEMARK) {
X fputs("Filemark encountered.\n", stderr);
X return( err );
X } else if (err & EOM) {
X fputs("End-Of-Media encountered.\n", stderr);
X return( err );
X } else {
X err &= SENSEKEY;
X fprintf(stderr, "sense key = %d (%s)\n", err, sensekey(err));
X if (err == 1) /* Recovered Error */
X break;
X else if (err != 6) /* Unit Attention */
X return( err );
X }
X }
X }
X return( 0 );
X}
@EOF
chmod 666 tctl.c
exit 0
------ cut here ------ cut here ------ cut here ------ cut here ------
--
Frank J. Edwards | "I did make up my own mind -- there
2677 Arjay Court | simply WASN'T ANY OTHER choice!"
Palm Harbor, FL 34684-4504 | -- Me
Phone (813) 786-3675 (voice) | Only Amiga Makes It Possible...
More information about the Comp.unix.amiga
mailing list