Newsgroups: comp.sys.next.misc
Subject: MT-02 tape controller on NeXT SCSI
From: mark@abekas.uucp (Mark Andrews)
Date: Tue, 7 Apr 1992 20:32:30 GMT
Organization: Abekas Video Systems Inc., Redwood City, California.
Keywords: next scsi MT-02 QIC
Summary: MT-02 tape controller on NeXT SCSI



 How to make the MT-02 SCSI tape drive useable ...

	*****This post has no warranty*****

NeXT Answers hardware.762 gives details of how to set the NeXT st
driver into fixed block mode, allowing it to work with QIC style tape
drives.  This has enabled my tape drive to work, but my drive has
performed badly, often bombing out with write errors. I knew it wasn't
the drive or the media because they both work when connected to a sun,
and the tape works in other drives. The clue came from the NeXT
Console where I found the following message.

st: cmd = 0xa sr_io_status = 2H
    Sense key = 0x1  Sense Code = 0x0

A sense key of 0x1 indicates a recoverable error, ie one that the tape
drive fixed, e.g. a write failed but was sucessful on a subsequent
retry (provided the max retry limit wasn't reached). Some tape drivers
report the number of recoverable errors, and on some machines "mt
status" will report the number.

Anyway it turns out that the MT-02 tape controller has a bit which
controls how it handles soft errors. The default case when connected
to a NeXT is to for the tape drive to issue a CHECK CONDITION status
code after it executes a command if a recoverable error occured. The
NeXT st driver seems to handle this as if it were a real error and
bombs out at this point. The other mode is to have the drive
accumulate the number of soft errors, so that they can be collected
later by the REQUEST SENSE command.

The following tape utility adds to the code in NA.762 and gives the
option of setting the SEC bit. It also allows the user to set the
driver back to variable block mode, and execute the MODE SENSE and
REQUEST SENSE scsi commands.



Mark Andrews
abekas!mark@pyramid.com		<non-NeXT mail>
abekas!cookie!mark@pyramid.com	<NeXT mail ok>




/* */
/* NeXT scsi tape driver utility */
/* Mark Andrews 1992 */
/* This Code is Supplied as is with NO WARRANTY of any kind */
/* No resposibility can or will be accepted for any damage that using */
/* this program may cause */
/* It has only been tested on a SCSI tape drive with an MT-02 tape controller */
/* Note the SEC bit is part of the VENDOR unique stuff and may or may not */
/* work with other drives */
/* The MTIOCFIXBLK code is from NA.562 */
/* The REQUEST SENSE code is from a posting by louie@sayshell.umd.edu 3/10/92 */
/* */

#include <stdio.h>
#include <libc.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/mtio.h>
#include <nextdev/scsireg.h>

#define	SEC	0x1
#define	QUIET	0
#define	VERBOSE	1

char tape_name[11];
int tape;
struct modesel_parms msp;
struct scsi_req sr;

main(argc,argv)
int argc;
char *argv[];
{
  char c;
  int sf,sv,ms,rs,sc;

  sf=sv=ms=rs=sc=0;
  strcpy(tape_name,"/dev/rst0");

  while((c=getopt(argc,argv,"hvrmsnf:")) !=EOF)
    {
    if(c=='n') strcpy(tape_name,"/dev/nrst0");	/* set device to non rewind */
    if(c=='f') if(optarg!=NULL)sf=atoi(optarg);else sf=512;	/* set to fixed block mode */
    if(c=='v') sv=1;		/* set to variable block mode */
    if(c=='m') ms=1;		/* mode sense */
    if(c=='r') rs=1;		/* request sense */
    if(c=='s') sc=1;		/* set the soft error bit */
    if(c=='h') 			/* print usage */
      fprintf(stderr,"Usage: %s -hvrmsnf [block size]\n%s%s%s%s%s%s%s",argv[0],
        "\t-h   : print usage,\n",
        "\t-f # : set fixed block mode with size #,\n",
	"\t-v   : set variable block mode,\n",
	"\t-m   : do a mode sense command and report the results,\n",
	"\t-r   : do a request sense command and report the results,\n",
        "\t-s   : set the soft error bit,\n",
	"\t-n   : use /dev/nrst0 instead of /dev/rst0.\n");
    }

  /* open /dev/rst0 */
  if((tape=open(tape_name,O_RDWR,777))==0)
    {
    fprintf(stderr,"Unable to open %s\n",tape_name);
    exit(1);
    }

  if(sv)
    {
    if(!sf)
      set_var_blk();
    else
      {
      fprintf(stderr,"Can't set to both fixed and variable block mode\n");
      problem();
      }
    }
  if(sf) set_fix_blk(sf);
  if(ms) mode_sense(VERBOSE);
  if(rs) request_sense();
  if(sc) set_sec();

  /* close tape */
  close (tape);
  exit(0);
}

problem()
{
  /* close tape */
  close (tape);
  exit(-1);
}

mode_sense(verbose)
int verbose;
{
  /* do a mode sense ioctl */
  msp.msp_bcount=13; /*  think this is correct */
  if(ioctl(tape,MTIOCMODSEN,&msp))
    {
      perror("mode sense ioctl failed\n");
      exit(1); 
    }
  if(!verbose)
    return;
  /* print out the returned data */
  fprintf(stdout,"mode sense data length  = %d\n",(int)msp.msp_data.msd_header.msh_sd_length_0);
  fprintf(stdout,"media type              = %d\n",(int)msp.msp_data.msd_header.msh_med_type);
  if(msp.msp_data.msd_header.msh_wp)
    fprintf(stdout,"tape is                 = write protected\n");
  else
    fprintf(stdout,"tape is                 = writeable\n");
   
  fprintf(stdout,"buffered mode           = %d\n",(int)msp.msp_data.msd_header.msh_bufmode);
  fprintf(stdout,"speed                   = %d\n",(int)msp.msp_data.msd_header.msh_speed);
  fprintf(stdout,"block descriptor length = %d\n",(int)msp.msp_data.msd_header.msh_bd_length);
  fprintf(stdout,"density code            = %d\n",(int)msp.msp_data.msd_blockdescript.msbd_density);
  fprintf(stdout,"number of blocks        = %d\n",(int)msp.msp_data.msd_blockdescript.msbd_numblocks);
  fprintf(stdout,"block length            = %d\n",(int)msp.msp_data.msd_blockdescript.msbd_blocklength);
  fprintf(stdout,"vendor unique byte[0]   = %d\n",(int)msp.msp_data.msd_vudata[0]);
}

mode_slct()
{
  /* this routine assumes that the msp structure has already been */
  /* initialized, a mode_sense is the best way to do this */

  /* do a mode select ioctl */
  if(ioctl(tape,MTIOCMODSEL,&msp))
    {
      perror("mode select ioctl failed\n");
      return 1; 
    }
}

set_sec()
{

  mode_sense(QUIET);				/* do a mode sense */
  msp.msp_data.msd_vudata[0]|=SEC;	/* set the SEC bit */
  mode_slct();				/* do a mode select */
}

set_fix_blk(blocksize)
int blocksize;
{

  if(ioctl(tape,MTIOCFIXBLK,&blocksize))
    {
    perror("fixed block ioctl failed\n");
    return 1; 
    }
}


set_var_blk()
{

  if(ioctl(tape,MTIOCVARBLK))
    {
    perror("variable block ioctl failed\n");
    return 1; 
    }
}

request_sense()
{
  /* set up sr */
  bzero(&sr, sizeof(sr));
  sr.sr_dma_dir=SR_DMA_RD;
  sr.sr_dma_max=sizeof(sr.sr_esense);
  sr.sr_addr=(caddr_t)&sr.sr_esense;
  sr.sr_ioto=2;
  sr.sr_cdb.cdb_c6.c6_opcode=C6OP_REQSENSE;
  sr.sr_cdb.cdb_c6.c6_len=sizeof(sr.sr_esense);

  /* execute the request sense */
  if(ioctl(tape,MTIOCSRQ,&sr))
    {
    perror("request sense ioctl failed\n");
    return 1; 
    }

  /* print the answers */
  fprintf(stdout,"io status               = %d\n",(int)sr.sr_io_status);
  fprintf(stdout,"scsi status             = %d\n",(int)sr.sr_scsi_status);
  fprintf(stdout,"bytes dma'd             = %d\n",(int)sr.sr_dma_xfr);
  fprintf(stdout,"sense info bytes are    =");
  if(sr.sr_esense.er_ibvalid)
    fprintf(stdout,"valid\n");
  else
    fprintf(stdout,"not valid\n");
  fprintf(stdout,"error class             = %d\n",(int)sr.sr_esense.er_class);
  fprintf(stdout,"error code              = %d\n",(int)sr.sr_esense.er_code);
  fprintf(stdout,"segment number          = %d\n",(int)sr.sr_esense.er_segment);
  fprintf(stdout,"file mark found         = ");
  if(sr.sr_esense.er_filemark)
    fprintf(stdout,"yes\n");
  else
    fprintf(stdout,"no\n");
  fprintf(stdout,"end of medium           = ");
  if(sr.sr_esense.er_endofmedium)
    fprintf(stdout,"yes\n");
  else
    fprintf(stdout,"no\n");
  fprintf(stdout,"incorect length         = ");
  if(sr.sr_esense.er_badlen)
    fprintf(stdout,"yes\n");
  else
    fprintf(stdout,"no\n");
  fprintf(stdout,"sense key               = %d\n",(int)sr.sr_esense.er_sensekey);
  fprintf(stdout,"residue length          = %d\n",(int)sr.sr_esense.er_info+((int)sr.sr_esense.er_infomsb<<24));
  fprintf(stdout,"additional sense length = %d\n",(int)sr.sr_esense.er_addsenselen);
  fprintf(stdout,"additional sense code   = %d\n",(int)sr.sr_esense.er_addsensecode);
  fprintf(stdout,"sense code qualifier    = %d\n",(int)sr.sr_esense.er_qualifier);
  fprintf(stdout,"status byte 13          = %d\n",(int)sr.sr_esense.er_stat_13);
  fprintf(stdout,"status byte 14          = %d\n",(int)sr.sr_esense.er_stat_14);
  fprintf(stdout,"status byte 15          = %d\n",(int)sr.sr_esense.er_stat_15);
  fprintf(stdout,"recoverable errors      = %d\n",(int)sr.sr_esense.er_err_count);
}

-- 
Mark Andrews
abekas!mark@pyramid.com

Newsgroups: comp.sys.next.misc