File:  [Research Unix] / researchv9 / sys / boot / stand / st.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:21:59 2018 UTC (8 years, 3 months ago) by root
Branches: belllabs, MAIN
CVS tags: researchv9-SUN3_old, researchv9-SUN3, HEAD
researchv9-SUN3(old)

/*
 * @(#)st.c 1.1 86/02/03 Copyright (c) 1985 by Sun Microsystems, Inc.
 */

#include <sys/types.h>
#include <sys/inode.h>
#include <sun/dklabel.h>
#include <sun/dkio.h>
#include <sys/buf.h>
#include <sundev/screg.h>
#include <sundev/sireg.h>
#include <sundev/scsi.h>
#include <sundev/streg.h>
#include <machine/idprom.h>
#include "saio.h"
#include "param.h"

/*
 * Driver for Sysgen SC4000 and EMULEX MT-02 SCSI tape controllers.
 * Supports qic11 format only.
 */
extern int scdoit();
#ifdef SUN3
extern int sidoit();
#endif

#define min(a,b)	((a)<(b)? (a): (b))

#define NSD 1
unsigned long staddrs[NSD] = { 0x0, };

extern int xxprobe(), xxboot();
int stopen(), stclose(), ststrategy();


struct stparam {
	int		st_target;
	int		st_unit;
	int		st_eof;
	int		st_lastcmd;
	int		st_ctype;
#ifdef SUN3
	int		st_ha_type;
#endif
	struct 		saioreq subsip[1];	/* sip for host adapter */
};

/*
 * DMA-able buffers
 */
#ifdef SUN2
/* virtual addresses are precious on Sun-2 */
#define	MAXSTBSIZE	(20*1024)	
#endif
#ifdef SUN3
#define	MAXSTBSIZE	(127*512)	
#endif
struct stdma {
	char	sbuffer[SENSE_LENGTH];
	char	databuffer[MAXSTBSIZE];
};

#define STSBUF		(((struct stdma *)sip->si_dmaaddr)->sbuffer)
#define STBUF		(((struct stdma *)sip->si_dmaaddr)->databuffer)

#define ISAEMULEX(stp)	(stp->st_ctype == ST_TYPE_EMULEX ? 1 : 0)

/*
 * What resources we need to run
 */
struct devinfo stinfo = {
	0,				/* No device to map in */
	sizeof (struct stdma),
	sizeof (struct stparam),
	NSD,				/* Dummy devices */
	staddrs,			/* Dummy devices */
	MAP_MAINMEM,
	MAXSTBSIZE,			/* transfer size */
};

struct boottab stdriver = {
	"st",	xxprobe, xxboot,
	stopen, stclose, ststrategy,
	"st: SCSI tape", &stinfo
};

#define	TAPE_TARGET		4	/* default SCSI target # for tape */

#define SENSE_BITS \
"\020\17NoCart\16NoDrive\15WriteProt\14EndMedium\13HardErr\12WrongBlock\
\11FileMark\7InvCmd\6NoData\5Flaking\4BOT\0034\0022\1GotReset"

#define SENSELOC	4	/* sysgen returns sense at this offset */

#define	ROUNDUP(x)	((x + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1))

#ifdef SUN3
/*
 * Determine type of host adaptor interface, si or sc.
 * Returns 1 if si host adaptor and 0 if sc host adaptor.
 */
st_probe(sip)
	struct saioreq *sip;
{
	if (siprobe(sip)) {
		return (1);
	} else {
		return (0);
	}
}
#endif

/*
 * Open the SCSI Tape controller
 */
int
stopen(sip)
	register struct saioreq *sip;
{
	register struct stparam *stp;
	register int r;
	register int skip;
	register int i;
	struct scsi_cdb cdb;
	struct scsi_scb scb;

	stp = (struct stparam *) sip->si_devdata;
	bzero( (char *)stp, (sizeof (struct stparam)));

	*stp->subsip = *sip;	/* initialize subsip */

#ifdef SUN3
	{
		extern struct boottab scdriver;
		extern struct boottab sidriver;

		/* FIXME, find out which scsi interface to use */
		if (st_probe(sip)) {
			stp->st_ha_type = 1;

			/* FIXME, must vector thru table */
			stp->subsip->si_boottab = &sidriver;
		} else {
			stp->st_ha_type = 0;

			/* FIXME, must vector thru table */
			stp->subsip->si_boottab = &scdriver;
		}
	}
#endif
#ifdef SUN2
	{
		extern struct boottab scdriver;

		stp->subsip->si_boottab = &scdriver;	
	}
#endif

	stp->subsip->si_unit = sip->si_unit >> 3;       /* Target number */
	if (stp->subsip->si_unit == 0) {
		stp->subsip->si_unit = TAPE_TARGET;
	}

	r = devopen(stp->subsip);
	if (r < 0) return r;

	/* Logical unit number */
	stp->st_unit = sip->si_unit & 0x07;

	/*
	 * Must wait for tape controller to become ready.
	 * This takes about 10 seconds for the Emulex.
	 */
	/*
	 * Test for the controller being ready. First test will fail if
	 * the SCSI bus is permanently busy or if a previous op was 
	 * interrupted in mid-transfer. Second one should work.
	 */
	for (i = 0; i < 2; i++) {
		bzero((char *) &cdb, sizeof cdb);
		cdb.cmd = SC_TEST_UNIT_READY;
		cdb.count = 0;
		stp->subsip->si_cc = 0;
		stp->subsip->si_ma = 0;
#ifdef SUN3
		if (stp->st_ha_type)
			r = sidoit(&cdb, &scb, stp->subsip);
		else
#endif
			r = scdoit(&cdb, &scb, stp->subsip);
	}

	/* 
	 * To figure out what type of tape controller is out there we send 
	 * a REQUEST_SENSE command and see how much sense data comes back.  
	 */
	stp->st_ctype = ST_TYPE_EMULEX;
	cdb.cmd = SC_REQUEST_SENSE;
	stp->subsip->si_cc = cdb.count = ST_EMULEX_SENSE_LEN;
	stp->subsip->si_ma = STSBUF;
#ifdef SUN3
	if (stp->st_ha_type)
		r = sidoit(&cdb, &scb, stp->subsip);
	else
#endif
		r = scdoit(&cdb, &scb, stp->subsip);
	if (r != ST_EMULEX_SENSE_LEN) {
	        stp->st_ctype = ST_TYPE_SYSGEN;
		cdb.cmd = SC_REQUEST_SENSE;
		stp->subsip->si_cc = cdb.count = ST_SYSGEN_SENSE_LEN;
		stp->subsip->si_ma = STSBUF;
#ifdef SUN3
		if (stp->st_ha_type)
			r = sidoit(&cdb, &scb, stp->subsip);
		else
#endif
			r = scdoit(&cdb, &scb, stp->subsip);
		if (r == -1) {
			printf("stopen: cannot get sense, %d\n", r);
			return (-1);
		}
	} 

	/* 
	 * Default format mode for emulex is qic24.
	 * Needs to be qic11.
	 */
	if (ISAEMULEX(stp)) {
		sip->si_cc = EM_MS_PL_LEN;
		if (stcmd(SC_QIC11, sip, 1) == 0) {
			printf("stopen: mode select command fail");
			return (-1);
		}
	}

	/*
	 * Rewind a few times until it works.  First one will fail if
	 * the SCSI bus is permanently busy if a previous op was interrupted
	 * in mid-transfer.  Second one will fail with POR status, after
	 * the scsi bus is reset from the busy.  Third one should work.  
	 */
	sip->si_cc = 0;
	if (stcmd(SC_REWIND, sip, 0) == 0 &&
	    stcmd(SC_REWIND, sip, 0) == 0 &&
	    stcmd(SC_REWIND, sip, 1) == 0) {
		return (-1);
	}
	skip = sip->si_boff;
	while (skip--) {
		sip->si_cc = 0;
		if (stcmd(SC_SPACE_FILE, sip, 1) == 0) {
			return (-1);
		}
	}
	stp->st_eof = 0;
	stp->st_lastcmd = 0;
	return (0);
}

/*
 * Close the tape drive.
 */
stclose(sip)
	register struct saioreq *sip;
{
	register struct stparam *stp;

	stp = (struct stparam *) sip->si_devdata;
	if (stp->st_lastcmd == SC_WRITE) {
		(void) stcmd(SC_WRITE_FILE_MARK, sip, 0);
	}
	(void) stcmd(SC_REWIND, sip, 0);
}


/*
 * Perform a read or write of the SCSI tape.
 */
int
ststrategy(sip, rw)
	register struct saioreq *sip;
	int rw;
{
	register struct stparam *stp;

	stp = (struct stparam *) sip->si_devdata;
	if (stp->st_eof) {
		stp->st_eof = 0;
		return (0);
	}
	return (stcmd(rw == WRITE ? SC_WRITE : SC_READ, sip, 1));
}

/*
 * Execute a scsi tape command
 */
int
stcmd(cmd, sip, errprint)
	int cmd;
	register struct saioreq *sip;
	int errprint;
{
	register struct st_emulex_mspl *mode;
	register int r, i, c;
	register char *buf;
	register struct stparam *stp;
	struct scsi_cdb cdb, scdb;
	struct scsi_scb scb, sscb;
	int count;
	int qic;
	int sense_length;
	char *cp;
	struct st_emulex_sense *ems;
	struct st_sysgen_sense *scs;

	count = sip->si_cc;
	stp = (struct stparam *)sip->si_devdata;
	buf =  sip->si_ma;

	if (cmd == SC_WRITE && buf != STBUF) {
		bcopy(buf, STBUF, (unsigned)count);
	}

	/* set up cdb */
	bzero((char *) &cdb, sizeof cdb);
	cdb.cmd = cmd;
	cdb.lun = stp->st_unit;
	c = ROUNDUP(count) / DEV_BSIZE;
	cdb.high_count = c >> 16;
	cdb.mid_count = (c >> 8) & 0xFF;
	cdb.low_count = c & 0xFF;
	stp->subsip->si_ma = STBUF;
	stp->subsip->si_cc = ROUNDUP(count);

	/* some fields in the cdb are command specific */
	switch (cmd) {

	case SC_QIC11:
	        stp->subsip->si_ma = 0;
	        stp->subsip->si_cc = 0; 
		if (ISAEMULEX(stp)) {
			qic = ST_EMULEX_QIC11;
			cdb.cmd = SC_MODE_SELECT;
			goto MODE;
		} else {
			cdb.cmd = SC_QIC02;
			cdb.high_count = ST_SYSGEN_QIC11;
			break;
		}
		/* NOTREACHED */

	case SC_QIC24:
	        stp->subsip->si_ma = 0;
	        stp->subsip->si_cc = 0; 
		if (ISAEMULEX(stp)) {
			qic = ST_EMULEX_QIC24;
			cdb.cmd = SC_MODE_SELECT;
			goto MODE;
		} else {
			cdb.cmd = SC_QIC02;
			cdb.high_count = ST_SYSGEN_QIC24;
			break;
		}
		/* NOTREACHED */
MODE:
	case SC_MODE_SELECT:
 		mode = (struct st_emulex_mspl *)STSBUF;
		bzero(mode, sizeof(*mode));
		mode->hdr.bufm = 1;
		mode->bd.density = qic;
		mode->hdr.bd_len = EM_MS_BD_LEN;
		stp->subsip->si_ma = (char *)mode;
		stp->subsip->si_cc = cdb.count = EM_MS_PL_LEN;
		break;

	case SC_SPACE_FILE:
		cdb.cmd = SC_SPACE;	/* the real SCSI cmd */
		cdb.t_code = 1;		/* space file, not rec */
		cdb.low_count = 1;	/* space 1 file, not 0 */
		stp->subsip->si_ma = 0;
		stp->subsip->si_cc = 0;
		break;

	case SC_WRITE_FILE_MARK:
		cdb.count = 1;	
		/* fall through... */

	case SC_TEST_UNIT_READY:
	case SC_REWIND:
	case SC_LOAD:
		stp->subsip->si_ma = 0;
		stp->subsip->si_cc = 0;
		break;

	case SC_READ:
	case SC_WRITE:
 		if (ISAEMULEX(stp)) {
		        cdb.t_code = 1;
		}
		break;

	default:
		if (errprint)
			printf("st: unknown command\n");
		return (0);
	}

	/* execute the command */
#ifdef SUN3
	if (stp->st_ha_type)
		r = sidoit(&cdb, &scb, stp->subsip);
	else
#endif
		r = scdoit(&cdb, &scb, stp->subsip);

	/* allow for tape blocking */
	if (r > count)
		r = count;
	/* error */
	if (r == -1) 
		return (0);

	if (cmd == SC_READ) {
		if (min(count, r))
			bcopy(STBUF, buf, (unsigned)(min(count, r)));
	}

	/* we may need to get sense data */
	if (scb.chk) {
		bzero((char *) &scdb, sizeof scdb);
		scdb.cmd = SC_REQUEST_SENSE;
		scdb.lun = stp->st_unit;
		if (ISAEMULEX(stp)) {
			sense_length = stp->subsip->si_cc =
			    scdb.count = ST_EMULEX_SENSE_LEN;
		} else {
			sense_length = stp->subsip->si_cc = 
			    scdb.count = ST_SYSGEN_SENSE_LEN;
		}
		stp->subsip->si_ma = STSBUF;
#ifdef SUN3
		if (stp->st_ha_type)
			i = sidoit(&scdb, &sscb, stp->subsip );
		else
#endif
			i = scdoit(&scdb, &sscb, stp->subsip );
		if (i != sense_length) {
			if (errprint) {
				printf("st: sense error\n");
			}
			stp->st_eof = 1;
			return (0);
		} 
		if (ISAEMULEX(stp)) {
			ems = (struct st_emulex_sense *)STSBUF;
			if ((ems->ext_sense.fil_mk == 0) && errprint) {
				printf("st: error:\n");
				printf("\tsense key is %x", 
				    ems->ext_sense.key);
				printf("\terror is %x\n",
				    ems->error);
			}
			stp->st_eof = 1;
			return (r);
		} else  {
			scs = (struct st_sysgen_sense *)STSBUF;
			if ((scs->qic_sense.file_mark == 0) && errprint) {
#ifdef STBOOT
				printf("st: error %x\n", 
					*(unsigned short *)&STSBUF[SENSELOC]);
#else  STBOOT
				printf("st: error %b\n", 
					*(unsigned short *)&STSBUF[SENSELOC],
					SENSE_BITS);
#endif STBOOT
			}
			stp->st_eof = 1;
			return (r);
		}
	}
	if (r >= count) {
		return (count ? count : 1);
	} else {
		if (errprint) {
			printf("st: short transfer\n");
		}
		return (r);
	}
}


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.