From: michael@wft.stack.urc.tue.nl (Michael Brouwer)
Newsgroups: comp.sys.next.hardware
Subject: nmt -new mt for qic tape drives. (was: SCSI tape drive problems)
Date: 13 Dec 92 03:35:33 GMT
Organization: WayForward Technologies
NNTP-Posting-Host: tar.wft.stack.urc.tue.nl
In-reply-to: spy@dpls.dacc.wisc.edu's message of 9 Dec 92 22:32:27 GMT

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	nmt.1
#	nmt.c
#	Makefile
# This archive created: Sun Dec 13 04:18:25 1992
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(706 characters)'
sed 's/^X//' << \SHAR_EOF > 'README'
XREADME for nmt
X
X$Header: /Users/michael/Unix/src/nmt/RCS/README,v 1.2 1992/12/13 03:17:25 michael Exp $
X
X`Nmt' (New MT) is a replacement for the BSD `mt' command.  It enables you to
Xuse a QIC-type tapestreamer with your NeXT.  The most important function of
X`nmt' is invoking it as:
X
X	nmt init
X
Xthe effect of which is that the NeXT SCSI driver is put in 512 byte fixed
Xblock mode.  If you have a QIC type streamer you can put the follwing code
Xin `/etc/rc.local':
X
X# Initialize SCSI driver for quarter inch tape.
Xif [ -f /usr/local/bin/nmt ]; then
X	/usr/local/local/bin/nmt init && (echo -n ' tape')  >/dev/console 2>&1
Xfi
X
XI hope you find nmt useful.  I did :-)
X
XMichael L.H. Brouwer
X<michael@urc.tue.nl>
SHAR_EOF
echo shar: extracting "'nmt.1'" '(1998 characters)'
sed 's/^X//' << \SHAR_EOF > 'nmt.1'
X.\" Copyright (C) 1992 co\"operatie WayForward Technologies U.A.
X.\" All rights reserved.
X.\"
X.\" $Header: /Users/michael/Unix/src/nmt/RCS/nmt.1,v 1.3 1992/12/13 03:17:25 michael Exp $
X.\"
X.TH NMT 1 "December 12, 1992"
X.UC 4
X.SH NAME
Xnmt \- send commands to a magnetic tape.
X.SH SYNOPSIS
X.B nmt \fR[\-f \fIdevice\fN] \fIcommand\fN [\fIcount\fN]
X
X.SH DESCRIPTION
X\fINmt\fR is used to control a magnetic tape drive.  If no device name is
Xgiven, \fInmt\fR uses the device \fI/dev/nrst0\fR. Note that \fIdevice\fR
Xmust reference a raw (not block) tape device.  By default \fInmt\fR performs
Xthe requested operation once.  Operations may be performed multiple times by
Xspecifying \fIcount\fR. (If no count is specified, a count of 1 is implied.)
X.PP
XThe available commands are listed below.  Only as many
Xcharacters as are required to uniquely identify a command
Xneed to be specified.
X.TP
X.I bsf
X(not implemented)
X.TP
X.I bsr
X(not implemented)
X.TP
X.I fsf
XSkip over next
X.I count
Xfilemarks.
X.TP
X.I fsr
XSkip over next
X.I count
Xrecords.
X.TP
X.I eof
XWrite
X.I count
Xfilemarks.
X.TP
X.I erase
XErase (format) the tape (\fIcount\fR is ignored).
X.TP
X.I init
XSet the
X.B NeXT
XSCSI device driver to a 512 byte fixed block size (\fIcount\fR is ignored).
X.TP
X.I load
XLoad the tape (\fIcount\fR is ignored).
X.TP
X.I qic
XUse the value of
X.I count
Xto set the streamer to (0) automatic, (24) qic-24, (120) qic-120 or
X(150) qic-150.
X.TP
X.I retention
XRetention the tape (\fIcount\fR is ignored).
X.TP
X.I rewind
XRewind the tape (\fIcount\fR is ignored).
X.TP
X.I status
XShow the current mode sense info (\fIcount\fR is ignored).
X.TP
X.I unload
XUnload the tape (\fIcount\fR is ignored).
X.TP
X.I weof
XSame as \fIeof\fR.
X
X.SH DIAGNOSTICS
X\fBNmt\fR returns a 0 exit status when the operation(s) were successful,
X1 if the command was unrecognized, and 2 if an operation failed.
X
X.SH "SEE ALSO"
Xdd(1), st(4)
X
X.SH AUTHOR
X\fBnmt\fR was written by Michael L.H. Brouwer (michael@urc.tue.nl).
X
X.SH BUGS
X\fBnmt\fR should have no reason to exist.
SHAR_EOF
echo shar: extracting "'nmt.c'" '(7694 characters)'
sed 's/^X//' << \SHAR_EOF > 'nmt.c'
X/* This is nmt, a replacement of mt for the NeXT.
X
X   Nmt by Michael L.H. Brouwer (michael@urc.tue.nl)
X   Copyright (c) 1992 Cooperatie WayForward Technologies U.A.
X   Contact us for software development for NeXT or any other platform.
X
X   Do with this software whatever you like.  Nmt comes without any warranty
X   neither expressed nor implied.  Use it at your own risk.  If you don't
X   like these terms or some law allows you to claim waranty you may *not*
X   use this software.
X
X   $Header: /Users/michael/Unix/src/nmt/RCS/nmt.c,v 1.4 1992/12/13 03:17:25 michael Exp $  */
X
X#include <libc.h>
X#include <errno.h>
X#include <stdio.h>
X#include <string.h>
X#include <sys/ioctl.h>
X#include <sys/file.h>
X#include <sys/types.h>
X#include <sys/mtio.h>
X#include <bsd/dev/scsireg.h>
X
Xchar *prog;
X
X/* Display a useful message and exit.  */
Xstatic void
Xusage (void)
X{
X  fprintf (stderr, "Usage: %s [-f device] command [count]\n", prog);
X  exit (1);
X}
X
Xstatic void
Xdo_ioctl (int dev, int com, void *data)
X{
X  if (ioctl (dev, com, data))
X    {
X      fprintf (stderr, "%s: ioctl %d: %s\n",
X	       prog, com, strerror (errno));
X      exit (2);
X    }
X} /* do_ioctl */
X
Xstatic void
Xmt_bsf (int dev, int count, struct scsi_req *sr)
X{
X  do_ioctl (dev, MTIOCSRQ, sr);
X} /* mt_bsf */
X
Xstatic void
Xmt_bsr  (int dev, int count, struct scsi_req *sr)
X{
X  do_ioctl (dev, MTIOCSRQ, sr);
X} /* mt_bsr */
X
Xstatic void
Xmt_eof  (int dev, int count, struct scsi_req *sr)
X{
X  sr->sr_cdb.cdb_c6s.c6s_opcode = C6OP_WRTFM;
X  sr->sr_cdb.cdb_c6s.c6s_spare = 4;
X  sr->sr_cdb.cdb_c6s.c6s_len = count;
X  do_ioctl (dev, MTIOCSRQ, sr);
X} /* mt_eof */
X
Xstatic void
Xmt_erase (int dev, struct scsi_req *sr)
X{
X  sr->sr_cdb.cdb_c6s.c6s_opcode = 0x19;
X  sr->sr_cdb.cdb_c6s.c6s_spare = 6;
X  sr->sr_cdb.cdb_c6s.c6s_opt = 1;
X  sr->sr_cdb.cdb_c6s.c6s_len = 1 << 24;
X  do_ioctl (dev, MTIOCSRQ, sr);
X} /* mt_erase */
X
Xstatic void
Xmt_fsf  (int dev, int count, struct scsi_req *sr)
X{
X  sr->sr_cdb.cdb_c6s.c6s_opcode = C6OP_SPACE;
X  sr->sr_cdb.cdb_c6s.c6s_spare = 4;
X  sr->sr_cdb.cdb_c6s.c6s_opt = 1;
X  sr->sr_cdb.cdb_c6s.c6s_len = count + (1 << 24);
X  do_ioctl (dev, MTIOCSRQ, sr);
X} /* mt_fsf */
X
Xstatic void
Xmt_fsr  (int dev, int count, struct scsi_req *sr)
X{
X  sr->sr_cdb.cdb_c6s.c6s_opcode = C6OP_SPACE;
X  sr->sr_cdb.cdb_c6s.c6s_spare = 4;
X  sr->sr_cdb.cdb_c6s.c6s_opt = 1;
X  sr->sr_cdb.cdb_c6s.c6s_len = count;
X  do_ioctl (dev, MTIOCSRQ, sr);
X} /* mt_fsr */
X
Xstatic void
Xmt_load (int dev, struct scsi_req *sr)
X{
X  sr->sr_cdb.cdb_c6s.c6s_opcode = 0x1B;
X  sr->sr_cdb.cdb_c6s.c6s_spare = 6;
X  sr->sr_cdb.cdb_c6s.c6s_opt = 3;
X  sr->sr_cdb.cdb_c6s.c6s_len = 1;
X  do_ioctl (dev, MTIOCSRQ, sr);
X} /* mt_load */
X
Xstatic void
Xmt_qic (int dev, int count)
X{
X  struct modesel_parms msp;
X  int density;
X
X  if (count == 0)
X    density = 0;
X  else if (count == 24)
X    density = 5;
X  else if (count == 120)
X    density = 15;
X  else if (count == 150)
X    density = 16;
X  else
X    {
X      fprintf (stderr, "Unknown QIC standard: %d\n", count);
X      exit (1);
X    }
X
X  msp.msp_bcount = 13;
X  do_ioctl (dev, MTIOCMODSEN, &msp);
X  msp.msp_data.msd_blockdescript.msbd_density = density;
X  do_ioctl (dev, MTIOCMODSEL, &msp);
X} /* mt_qic */
X
Xstatic void
Xmt_retention (int dev, struct scsi_req *sr)
X{
X  sr->sr_cdb.cdb_c6s.c6s_opcode = 0x1B;
X  sr->sr_cdb.cdb_c6s.c6s_spare = 6;
X  sr->sr_cdb.cdb_c6s.c6s_opt = 3;
X  sr->sr_cdb.cdb_c6s.c6s_len = 3;
X  do_ioctl (dev, MTIOCSRQ, sr);
X} /* mt_retention */
X
Xstatic void
Xmt_rewind (int dev, struct scsi_req *sr)
X{
X  sr->sr_cdb.cdb_c6s.c6s_opcode = C6OP_REWIND;
X  sr->sr_cdb.cdb_c6s.c6s_opt = 1;
X  do_ioctl (dev, MTIOCSRQ, sr);
X} /* mt_rewind */
X
Xstatic void
Xmt_status (int dev)
X{
X  struct modesel_parms msp;
X  unsigned char mt, wp, bm, sp, dc;
X  long nb, bl;
X
X  msp.msp_bcount = 13;
X  do_ioctl (dev, MTIOCMODSEN, &msp);
X
X  mt = msp.msp_data.msd_header.msh_med_type;
X  wp = msp.msp_data.msd_header.msh_wp;
X  bm = msp.msp_data.msd_header.msh_bufmode;
X  sp = msp.msp_data.msd_header.msh_speed;
X  dc = msp.msp_data.msd_blockdescript.msbd_density;
X  nb = msp.msp_data.msd_blockdescript.msbd_numblocks;
X  bl = msp.msp_data.msd_blockdescript.msbd_blocklength;
X
X  printf ("Medium type: %d=%s\n", mt, (mt == 128 ? "high" :
X				       (mt == 129 ? "low" :
X					(mt == 0 ? "automatic" : "unknown"))));
X  printf ("Write protected: %s\n", (wp == 1 ? "yes" : "no"));
X  printf ("Buffered mode: %s\n", (bm == 1 ? "yes" : "no"));
X  printf ("Speed: %d\n", sp);
X  printf ("Density: %d=%s\n", dc, (dc == 5 ? "QIC-24" : 
X				   (dc == 15 ? "QIC-120" : 
X				    (dc == 16 ? "QIC-150" :
X				     (dc == 0 ? "automatic" : "unknown")))));
X  printf ("Number of blocks: %ld\n", nb);
X  printf ("Block length: %ld\n", bl);
X} /* mt_status */
X
Xstatic void
Xmt_unload (int dev, struct scsi_req *sr)
X{
X  sr->sr_cdb.cdb_c6s.c6s_opcode = 0x1B;
X  sr->sr_cdb.cdb_c6s.c6s_spare = 6;
X  sr->sr_cdb.cdb_c6s.c6s_opt = 3;
X  do_ioctl (dev, MTIOCSRQ, sr);
X} /* mt_unload */
X  
Xstatic void
Xmt_init (dev)
X{
X  int size;
X
X  /* Set the tapedriver to a fixed block size of 512 bytes.  */
X  size = 512;
X  do_ioctl (dev, MTIOCFIXBLK, &size);
X} /* mt_init */
X
X#if 0
Xstatic void
Xmt_report_status (struct scsi_req *sr)
X{
X  printf ("io_status: %lx, scsi_status: %x, dma_xfr: %lx, exec_time: %d\n",
X	  sr->sr_io_status, sr->sr_scsi_status,
X	  sr->sr_dma_xfr, sr->sr_exec_time);
X} /* mt_report_status */
X#endif
X
Xstatic void
Xmt_command (int dev, char **commands, int nc)
X{
X  char *c = commands[0];
X  struct scsi_req sr;
X  int count, len;
X  
X  sr.sr_cdb.cdb_c6s.c6s_opcode = 0;
X  sr.sr_cdb.cdb_c6s.c6s_lun = 0;
X  sr.sr_cdb.cdb_c6s.c6s_spare = 0;
X  sr.sr_cdb.cdb_c6s.c6s_opt = 0;
X  sr.sr_cdb.cdb_c6s.c6s_len = 0;
X  sr.sr_cdb.cdb_c6s.c6s_ctrl = 0;
X  sr.sr_dma_dir = SR_DMA_RD;
X  sr.sr_addr = NULL;
X  sr.sr_dma_max = 0;
X  sr.sr_ioto = 3600;
X
X  /* Commands: bsf, bsr, eof, erase, fsf, fsr, init, load, retention,
X     rewind, status, unload, weof */
X  if (nc == 1)
X    count = 1;
X  else
X    count = atoi (commands[1]);
X  len = strlen (c);
X  if (!strcmp (c, "bsf"))
X    mt_bsf (dev, count, &sr);
X  else if (!strcmp (c, "bsr"))
X    mt_bsr (dev, count, &sr);
X  else if (!strcmp (c, "fsf"))
X    mt_fsf (dev, count, &sr);
X  else if (!strcmp (c, "fsr"))
X    mt_fsr (dev, count, &sr);
X  else if (1 < len && len < 4 && !strncmp (c, "eof", len))
X    mt_eof (dev, count, &sr);
X  else if (1 < len && len < 6 && !strncmp (c, "erase", len))
X    mt_erase (dev, &sr);
X  else if (0 < len && len < 5 && !strncmp (c, "init", len))
X    mt_init (dev);
X  else if (0 < len && len < 5 && !strncmp (c, "load", len))
X    mt_load (dev, &sr);
X  else if (0 < len && len < 4 && !strncmp (c, "qic", len))
X    mt_qic (dev, count);
X  else if (2 < len && len < 10 && !strncmp (c, "retention", len))
X    mt_retention (dev, &sr);
X  else if (2 < len && len < 7 && !strncmp (c, "rewind", len))
X    mt_rewind (dev, &sr);
X  else if (0 < len && len < 7 && !strncmp (c, "status", len))
X    mt_status (dev);
X  else if (0 < len && len < 7 && !strncmp (c, "unload", len))
X    mt_unload (dev, &sr);
X  else if (0 < len && len < 5 && !strncmp (c, "weof", len))
X    mt_eof (dev, count, &sr);
X  else
X    usage ();
X#if 0
X  mt_report_status (&sr);
X#endif
X}
X
Xint
Xmain (int argc, char **argv)
X{
X  int arg, dev;
X  char *device;
X
X  prog = strrchr (argv[0], '/');
X  prog = prog ? prog + 1 : argv[0];
X
X  if (argc < 2)
X    usage ();
X  if (!strcmp (argv[1], "-f"))
X    {
X      if (argc < 4)
X	usage ();
X      arg = 3;
X      device = argv[2];
X    }
X  else
X    {
X      arg = 1;
X      device = "/dev/nrst0";
X    }
X  dev = open (device, O_RDONLY, 0);
X  if (dev < 0)
X    {
X      fprintf (stderr, "%s: open %s: %s\n", prog, device, strerror (errno));
X      return (1);
X    }
X  mt_command (dev, &argv[arg], argc - arg);
X  close (dev);
X  return (0);
X} /* main */
SHAR_EOF
echo shar: extracting "'Makefile'" '(770 characters)'
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X# Makefile for nmt (New mt).
X#
X# $Header: /Users/michael/Unix/src/nmt/RCS/Makefile,v 1.5 1992/12/13 03:17:25 michael Exp $
X
X# Destination directory.
XDSTROOT =	/usr/local
XBINDIR =	$(DSTROOT)/bin
XMANDIR =	$(DSTROOT)/man
X
X# Install program.
XINSTALL =	install -c
XINSTALL_BIN =	$(INSTALL) -s -m 755
XINSTALL_MAN =	$(INSTALL) -m 644
X
X# Compiler options.
XCFLAGS =	-O2
X
Xall: nmt
X
Xclean:
X	rm -f core nmt.o nmt nmt.shar
X
Xdist:
X	shar -s -v -p X README nmt.1 nmt.c Makefile > nmt.shar
X
Xinstall: install-prog install-man
X
Xinstall-prog: nmt
X	rm -f $(BINDIR)/nmt
X	$(INSTALL_BIN) nmt $(BINDIR)/nmt
X
Xinstall-man:
X	[ -d $(MANDIR)/man1 ] || mkdir $(MANDIR)/man1
X	rm -f $(MANDIR)/man1/nmt.1
X	$(INSTALL_MAN) nmt.1 $(MANDIR)/man1/nmt.1
X
Xnmt: nmt.o
X	$(CC) $(LDFLAGS) -o nmt nmt.o
X
Xnmt.o: nmt.c
SHAR_EOF
#	End of shell archive
exit 0