crash@ckctpa.UUCP (Frank J. Edwards) (06/20/91)
Where should this have been posted to? alt.sources? c.s.a.programmer? I'm listening ... In article <1991Jun19.100002.26682@NCoast.ORG> lesle@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@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...