Files
Arquivotheca.SunOS-4.1.4/sys/boot/lib/common/st.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

707 lines
16 KiB
C

/*
* @(#)st.c 1.1 94/10/31 Copyright (c) 1988 by Sun Microsystems, Inc.
*
* Driver for SCSI tape drives.
*/
/*#define STDEBUG /* Allow compiling of debug code */
#define REL4 /* Enable release 4 mods */
#ifdef REL4
#include <stand/saio.h>
#include <stand/param.h>
#include <sys/buf.h>
#include <sun/dklabel.h>
#include <sun/dkio.h>
#include <mon/cpu.addrs.h>
#include <mon/idprom.h>
#include <stand/scsi.h>
#include <stand/streg.h>
#else REL4
#include "saio.h"
#include "param.h"
#include "../h/buf.h"
#include "../sun/dklabel.h"
#include "../sun/dkio.h"
#include "../mon/idprom.h"
#include "../mon/cpu.addrs.h"
#include "scsi.h"
#include "streg.h"
#endif REL4
#ifdef STDEBUG
int st_debug = 1; /* 0 = normal operation
* 1 = extended error info only
* 2 = debug and error info
* 3 = all status info
*/
/* Handy debugging 0, 1, and 2 argument printfs */
#define DPRINTF(str) \
if (st_debug > 1) printf(str)
#define DPRINTF1(str, arg1) \
if (st_debug > 1) printf(str,arg1)
#define DPRINTF2(str, arg1, arg2) \
if (st_debug > 1) printf(str,arg1,arg2)
/* Handy extended error reporting 0, 1, and 2 argument printfs */
#define EPRINTF(str) \
if (st_debug) printf(str)
#define EPRINTF1(str, arg1) \
if (st_debug) printf(str,arg1)
#define EPRINTF2(str, arg1, arg2) \
if (st_debug) printf(str,arg1,arg2)
static char *st_key_error_str[] = SENSE_KEY_INFO;
#define MAX_KEY_ERROR_STR \
(sizeof(st_key_error_str)/sizeof(st_key_error_str[0]))
#else STDEBUG
#define DPRINTF(str)
#define DPRINTF1(str, arg2)
#define DPRINTF2(str, arg1, arg2)
#define EPRINTF(str)
#define EPRINTF1(str, arg2)
#define EPRINTF2(str, arg1, arg2)
#endif STDEBUG
#ifdef BOOTBLOCK
#define SC_ERROR(str)
#define SC_ERROR1(str, arg1)
#else BOOTBLOCK
#define SC_ERROR printf
#define SC_ERROR1 printf
#endif BOOTBLOCK
extern int sc_reset;
extern int xxprobe(), xxboot();
int stopen(), stclose(), ststrategy();
/*
* This table defines the auto-probe disk list. Currently only st0 and st1
* are probed automatically. Others can be added by simply adding them
* to the following table.
*/
u_char st_index[] = { 0x20, 0x28, 0x18, 0x10 };
struct stparam {
int st_target;
int st_unit;
int st_ctype;
int st_dev_bsize;
int st_eof;
int st_lastcmd;
struct host_saioreq h_sip; /* sip for host adapter */
};
/*
* DMA-able buffers
*/
/* Max tape record length allowed for various devices */
#define ST_MAXXTREC (10*1024) /* 1/2-inch reel */
/* Define virtual addresses space. Note, sun-2 is limited. */
#ifdef sun2
#define ST_MAXSIZE (30 * 1024)
/*#define ST_MAXSIZE (20 * 1024) */
#else sun2
#define ST_MAXSIZE (126 * 512)
#endif sun2
struct stdma {
char databuffer[ST_MAXSIZE];
char sbuffer[SC_SENSE_LENGTH];
};
#define BUF (((struct stdma *)sip->si_dmaaddr)->databuffer)
#define STSBUF (((struct stdma *)sip->si_dmaaddr)->sbuffer)
/*
* What resources we need to run
*/
struct devinfo stinfo = {
0, /* No device to map in */
sizeof (struct stdma),
sizeof (struct stparam),
0, /* Dummy devices */
0, /* Dummy devices */
MAP_MAINMEM,
ST_MAXSIZE, /* 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 SENSELOC 4 /* sysgen returns sense at this offset */
#define ROUNDUP(x) ((x + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1))
#define min(a,b) ((a)<(b)? (a): (b))
#define ISASYSGEN(stp) (stp->st_ctype == ST_TYPE_SYSGEN ? 1 : 0)
#ifdef SDDEBUG
sd_print_buffer(y, count)
register u_char *y;
register int count;
{
register int x;
for (x = 0; x < count; x++)
printf("%x ", *y++);
printf("\n");
}
#endif SDDEBUG
/*
* Open the SCSI Tape controller
*/
stopen(sip)
register struct saioreq *sip;
{
register struct stparam *stp;
register struct host_saioreq *h_sip; /* Host adapter block */
register struct st_ms_mspl *ms;
register short r;
register int skip;
int i, index, ctlr;
int do_reset = 1;
DPRINTF("stopen:\n");
stp = (struct stparam *) sip->si_devdata;
bzero( (char *)stp, (sizeof (struct stparam)));
ctlr = sip->si_ctlr; /* Ctlr. number */
stp->st_ctype = ST_TYPE_SYSGEN; /* Always start with Sysgen! */
/* Set up host adapter and target info. */
ST_OPEN_PROBE:
if (sip->si_unit == 0) {
index = 0;
stp->st_unit = st_index[index] & 0x07; /* Logical unit */
stp->st_target = st_index[index++] >> 3; /* Target number */
} else {
stp->st_unit = sip->si_unit & 0x07; /* Logical unit */
stp->st_target = sip->si_unit >> 3; /* Target number */
}
ST_RETRY_OPEN:
EPRINTF2("stopen: ctlr= 0x%x unit= 0x%x ", ctlr, stp->st_unit);
EPRINTF1("target= 0x%x\n", stp->st_target);
h_sip = &stp->h_sip;
h_sip->ctlr = ctlr; /* Ctlr. number */
h_sip->unit = stp->st_target; /* Target number */
/* Probe for host adapter. */
if (scsi_probe(h_sip) == 0) {
/* If an error occurred previously, reset the SCSI bus. */
if (sc_reset && do_reset) {
EPRINTF("stopen: reset\n");
(void) (*h_sip->reset)(h_sip, 1);
}
/*
* 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 < 4; i++) {
if ((r=stcmd(SC_TEST_UNIT_READY, sip, 0)) > 0)
goto ST_OPEN;
else if (r < -1)
break;
/*DELAY(2200); /* XXX: Extra timing margin for HP */
}
/* If current tape device not ready, try the next one. */
if (sip->si_unit == 0 && index < sizeof(st_index)) {
stp->st_unit = st_index[index] & 0x07;
stp->st_target = st_index[index++] >> 3;
do_reset = 0;
goto ST_RETRY_OPEN;
}
}
/* If current host adapter not ready, try the next one. */
if (sip->si_ctlr == 0 && ctlr < SC_NSC) {
EPRINTF("stopen: trying next ha\n");
do_reset++;
ctlr++;
h_sip->devaddr = 0;
goto ST_OPEN_PROBE;
}
EPRINTF("stopen: device offline\n");
return (-1);
/*
* Check for tape drive (that is..it's not a disk). Note, if
* inquiry fails, we will presume the user knows what he's doing.
*/
ST_OPEN:
sc_reset = 1;
sip->si_ctlr = ctlr;
sip->si_unit = (stp->st_target <<3) + stp->st_unit;
sip->si_ma = BUF;
if (stcmd(SC_INQUIRY, sip, 0) > 0 &&
((struct scsi_inquiry_data *)sip->si_ma)->dtype != 1) {
EPRINTF("stopen: not a tape\n");
return (-1);
}
/*
* 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;
sip->si_cc = ST_EMULEX_SENSE_LEN;
/*sip->si_ma = BUF;*/
r = stcmd(SC_REQUEST_SENSE, sip, 0);
DPRINTF1("stopen: sense bytes= %x\n", r);
if (r == ST_SYSGEN_SENSE_BYTES) {
EPRINTF("stopen: Sysgen found\n");
stp->st_ctype = ST_TYPE_SYSGEN;
stp->st_dev_bsize = DEV_BSIZE;
sip->si_cc = ST_SYSGEN_SENSE_LEN;
if (stcmd(SC_REQUEST_SENSE, sip, 0) <= 0) {
SC_ERROR("st: cannot get sense\n");
return (-1);
}
} else {
/* sip->si_ma = BUF; */
ms = (struct st_ms_mspl *) sip->si_ma;
r = stcmd(SC_MODE_SENSE, sip, 0);
stp->st_dev_bsize = (ms->high_bl <<16) + (ms->mid_bl <<8) +
(ms->low_bl);
EPRINTF1("stopen: density= 0x%x\n", ms->density);
EPRINTF1("stopen: block size= %d\n", stp->st_dev_bsize);
}
/* Check for 1/2" tape */
(void) stcmd(SC_REWIND, sip, 0);
if (stp->st_dev_bsize == 0) {
if (stcmd(SC_QIC24, sip, 0) <= 0) {
EPRINTF("stopen: Fixed CCS found\n");
stp->st_dev_bsize = DEV_BSIZE;
goto STOPEN_TAPE;
}
sip->si_cc = 0;
/* sip->si_ma = BUF; */
if (stcmd(SC_READ, sip, 0) <= 0) {
EPRINTF("stopen: Emulex found\n");
stp->st_dev_bsize = DEV_BSIZE;
(void) stcmd(SC_REWIND, sip, 0);
goto STOPEN_TAPE;
} else {
EPRINTF("stopen: Variable CCS found\n");
stinfo.d_maxiobytes = ST_MAXXTREC;
(void) stcmd(SC_REWIND, sip, 0);
goto STOPEN_SKIP;
}
}
/*
* Default format mode for emulex is Qic24 for Sun-3
* or later. For Sun-2 use Qic-11.
*/
STOPEN_TAPE:
#ifdef sun2
(void) stcmd(SC_QIC11, sip, 1);
#else sun2
(void) stcmd(SC_QIC24, sip, 1);
#endif sun2
/*
* If read fails, try the other format. For Sun-3 or later
* this means Qic-11. For Sun-2, try Qic-24.
*/
sip->si_cc = 512;
sip->si_ma = BUF;
if (stcmd(SC_READ, sip, 0) > 0) {
(void) stcmd(SC_REWIND, sip, 0);
} else {
(void) stcmd(SC_REWIND, sip, 0);
#ifdef sun2
if (stcmd(SC_QIC24, sip, 1) <= 0)
SC_ERROR("st: mode select failed\n");
#else sun2
if (stcmd(SC_QIC11, sip, 1) <= 0)
SC_ERROR("st: mode select failed\n");
#endif sun2
}
STOPEN_SKIP:
skip = sip->si_boff;
while (skip--) {
sip->si_cc = 0;
if (stcmd(SC_SPACE_FILE, sip, 1) <= 0) {
SC_ERROR("st: space file failed\n");
return (-1);
}
}
sc_reset = 0;
stp->st_eof = 0;
stp->st_lastcmd = 0;
return (0);
}
/*
* Close the tape drive and don't rewind it.
*/
stclose(sip)
register struct saioreq *sip;
{
register struct stparam *stp;
EPRINTF("stclose:\n");
stp = (struct stparam *) sip->si_devdata;
if (stp->st_lastcmd == SC_WRITE)
(void) stcmd(SC_WRITE_FILE_MARK, sip, 0);
sc_reset = 0;
return (0);
}
/*
* Perform a read or write of the SCSI tape.
*/
ststrategy(sip, rw)
register struct saioreq *sip;
int rw;
{
register struct stparam *stp;
DPRINTF("ststrategy:\n");
sc_reset = 1;
stp = (struct stparam *) sip->si_devdata;
if (stp->st_eof) {
stp->st_eof = 0;
return (0);
}
DPRINTF2("ststrategy: addr= 0x%x size= 0x%x\n",
(int)(sip->si_ma), sip->si_cc);
rw = stcmd((rw == WRITE ? SC_WRITE : SC_READ), sip, 1);
return (rw);
}
/*
* Execute a scsi tape command
*/
static int
stcmd(cmd, sip, errprint)
short cmd;
register struct saioreq *sip;
int errprint;
{
register struct stparam *stp;
register struct host_saioreq *h_sip; /* Sip for host adapter */
register struct st_ms_mspl *mode;
register struct st_sense *ems;
register struct sysgen_sense *scs;
struct scsi_cdb6 cdb, scdb;
struct scsi_scb scb, sscb;
char *buf;
int density;
int r, cc;
DPRINTF("stcmd:\n");
AGAIN:
stp = (struct stparam *)sip->si_devdata;
h_sip = &stp->h_sip; /* host adapter */
h_sip->unit = stp->st_target;
buf = sip->si_ma;
/* Set up cdb */
bzero((char *) &scb, sizeof scb);
bzero((char *) &cdb, CDB_GROUP0);
stp->st_lastcmd = cmd;
cdb.cmd = cmd;
cdb.lun = stp->st_unit;
h_sip->cc = 0;
h_sip->ma = 0;
DPRINTF2("stcmd: cmd= 0x%x lun= 0x%x\n", cdb.cmd, cdb.lun);
/* Some fields in the cdb are command specific */
switch (cmd) {
case SC_READ:
h_sip->dma_dir = SC_RECV_DATA;
if (stp->st_dev_bsize == 0) {
DPRINTF1("stcmd: variable read (0x%x)\n", sip->si_cc);
cc = h_sip->cc = sip->si_cc;
} else {
DPRINTF1("stcmd: read (0x%x)\n", sip->si_cc);
cc = h_sip->cc = ROUNDUP(sip->si_cc);
cc = cc / stp->st_dev_bsize;
cdb.t_code = 1;
}
STCMD_RD:
cdb.mid_count = (cc >> 8) & 0xFF;
cdb.low_count = cc & 0xFF;
if (buf >= DVMA_BASE) {
h_sip->ma = buf;
DPRINTF1("stcmd: DVMA addr= 0x%x\n", h_sip->ma);
} else {
h_sip->ma = BUF;
DPRINTF("stcmd: BUF addr\n");
}
if (cc == 0)
h_sip->dma_dir = SC_NO_DATA;
break;
case SC_WRITE:
h_sip->dma_dir = SC_SEND_DATA;
if (buf < DVMA_BASE) {
DPRINTF("stcmd: write bcopy\n");
bcopy(buf, BUF, sip->si_cc);
}
if (stp->st_dev_bsize == 0) {
DPRINTF1("stcmd: variable write (0x%x)\n", sip->si_cc);
cc = h_sip->cc = sip->si_cc;
} else {
DPRINTF1("stcmd: write (0x%x)\n", sip->si_cc);
cc = h_sip->cc = ROUNDUP(sip->si_cc);
cc = cc / stp->st_dev_bsize;
cdb.t_code = 1;
}
goto STCMD_RD;
/* break; */
case SC_MODE_SENSE:
DPRINTF("stcmd: mode sense\n");
h_sip->dma_dir = SC_RECV_DATA;
h_sip->cc = sip->si_cc = sizeof (struct st_ms_mspl);
cdb.low_count = h_sip->cc & 0xFF;
h_sip->ma = BUF;
bzero((char *)h_sip->ma, sizeof (struct st_ms_mspl));
break;
case SC_QIC11:
EPRINTF("stcmd: low density (qic-11)\n");
sip->si_cc = 0;
h_sip->dma_dir = SC_NO_DATA;
if (ISASYSGEN(stp)) {
cdb.cmd = SC_QIC02;
cdb.high_count = ST_SYSGEN_QIC11;
} else {
density = ST_EMULEX_QIC11;
goto MODE;
}
break;
case SC_QIC24:
EPRINTF("stcmd: high density (qic-24)\n");
sip->si_cc = 0;
h_sip->dma_dir = SC_NO_DATA;
if (ISASYSGEN(stp)) {
cdb.cmd = SC_QIC02;
cdb.high_count = ST_SYSGEN_QIC24;
} else {
density = ST_EMULEX_QIC24;
goto MODE;
}
break;
case SC_MODE_SELECT:
MODE:
cdb.cmd = SC_MODE_SELECT;
mode = (struct st_ms_mspl *)STSBUF;
bzero((char *)mode, sizeof(*mode));
mode->bufm = 1;
mode->bd_len = MS_BD_LEN;
mode->density = density;
mode->mid_bl = (stp->st_dev_bsize >> 8) & 0xff;
mode->low_bl = stp->st_dev_bsize & 0xff;
h_sip->ma = (char *)mode;
h_sip->dma_dir = SC_SEND_DATA;
sip->si_cc = h_sip->cc = cdb.low_count =
sizeof (struct st_ms_mspl);
break;
case SC_SPACE_FILE:
EPRINTF("stcmd: space file\n");
cdb.cmd = SC_SPACE_REC; /* Real SCSI cmd */
cdb.t_code = 1; /* Space file, not rec */
cdb.low_count = 1; /* Space 1 file */
sip->si_cc = 0;
h_sip->dma_dir = SC_NO_DATA;
break;
case SC_WRITE_FILE_MARK:
DPRINTF("stcmd: write file mark\n");
cdb.low_count = 1;
sip->si_cc = 0;
h_sip->dma_dir = SC_NO_DATA;
break;
case SC_REQUEST_SENSE:
DPRINTF("stcmd: request sense\n");
cdb.low_count = h_sip->cc = sip->si_cc;
h_sip->ma = buf;
h_sip->dma_dir = SC_RECV_DATA;
/* bzero((char *)buf, sizeof (struct st_ms_mspl)); */
break;
case SC_INQUIRY:
EPRINTF("stcmd: inquiry\n");
h_sip->cc = sip->si_cc = sizeof (struct scsi_inquiry_data);
cdb.low_count = h_sip->cc;
h_sip->dma_dir = SC_RECV_DATA;
h_sip->ma = buf;
bzero((char *)buf, h_sip->cc);
break;
case SC_TEST_UNIT_READY:
#ifdef STDEBUG
DPRINTF("stcmd: test unit\n");
sip->si_cc = 0;
h_sip->dma_dir = SC_NO_DATA;
break;
#endif STDEBUG
case SC_REWIND:
DPRINTF("stcmd: rewind\n");
sip->si_cc = 0;
h_sip->dma_dir = SC_NO_DATA;
break;
default:
SC_ERROR1("st: unknown command (0x%x)\n", cdb.cmd);
return (0);
}
/* Execute the command */
r = (*h_sip->doit)(&cdb, &scb, h_sip);
DPRINTF2("stcmd: r = 0x%x (0x%x)\n", r, sip->si_cc);
/* Host adapter error */
if (r < 0) {
EPRINTF("stcmd: SCSI bus error\n");
return (r);
}
/* Allow for tape blocking */
if (r > sip->si_cc) {
EPRINTF1("stcmd: round to 0x%x\n", sip->si_cc);
r = sip->si_cc;
}
/* Device busy error */
if(scb.busy) {
EPRINTF("stcmd: busy\n");
DELAY(2000000);
goto AGAIN;
}
if (cmd == SC_READ && buf < DVMA_BASE) {
DPRINTF("stcmd: read bcopy\n");
if (min(sip->si_cc, r))
bcopy(BUF, buf, (min(sip->si_cc, r)));
}
/* We may need to get sense data */
if (scb.chk) {
EPRINTF("stcmd: check condition\n");
bzero((char *) &sscb, sizeof(sscb));
bzero((char *) &scdb, CDB_GROUP0);
scdb.cmd = SC_REQUEST_SENSE;
scdb.lun = stp->st_unit;
if (ISASYSGEN(stp)) {
h_sip->cc = scdb.low_count = ST_SYSGEN_SENSE_LEN;
} else {
h_sip->cc = scdb.low_count = ST_EMULEX_SENSE_LEN;
}
h_sip->ma = STSBUF;
h_sip->dma_dir = SC_RECV_DATA;
(void) (*h_sip->doit)(&scdb, &sscb, h_sip);
/* Process Sysgen tape drive errors */
if (ISASYSGEN(stp)) {
scs = (struct sysgen_sense *)STSBUF;
stp->st_eof = 1;
/* No tape */
if (scs->no_cart) {
EPRINTF("stcmd: not ready\n");
return (-2);
}
/* QIC-24 */
if (scs->no_data != 0) {
EPRINTF("stcmd: blank check\n");
return (-2);
}
/* EOF */
#ifdef STDEBUG
if (scs->file_mark)
EPRINTF("stcmd: eof\n");
#endif STDEBUG
/* Other errors */
if ((scs->file_mark == 0) && errprint) {
SC_ERROR1("st: error %x\n",
*(u_short *)&STSBUF[SENSELOC]);
}
return (r);
/* Process MT-02 and CCS tape drive errors */
} else {
ems = (struct st_sense *)STSBUF;
/* Blank Check error */
if (ems->key == SC_BLANK_CHECK) {
EPRINTF("stcmd: blank check\n");
stp->st_eof = 1;
return (-2);
/* Soft error */
} else if (ems->key == SC_RECOVERABLE_ERROR) {
EPRINTF("stcmd: soft error\n");
return (r ? r : 1);
/* EOF error */
}else if (ems->fil_mk) {
EPRINTF("stcmd: eof\n");
stp->st_eof = 1;
return (r);
/* Illegal length error */
} else if (ems->ili) {
EPRINTF("stcmd: illegal length\n");
return (r ? r : 1);
/* Other errors */
} else if(errprint) {
SC_ERROR1("st: sense key = %x", ems->key);
#ifdef STDEBUG
SC_ERROR1(" (%s)", st_key_error_str[ems->key]);
#endif STDEBUG
SC_ERROR1(" error = %x\n", ems->error);
stp->st_eof = 1;
return (-1);
}
}
}
if (r >= sip->si_cc) {
/* Normal completion exit */
return (sip->si_cc ? sip->si_cc : 1);
} else {
/* Error exit */
if (errprint) {
EPRINTF1("stcmd: short transfer (0x%x)\n", r);
}
return (r);
}
}