|
|
researchv9-SUN3(old)
/* @(#)st.c 1.1 86/02/03 SMI */
/*
* Copyright (c) 1985 by Sun Microsystems, Inc.
*/
#include "st.h"
#if NST > 0
/*
* Driver for Sysgen SC400 and Emulex MT02 SCSI tape controller.
*/
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/buf.h"
#include "../h/dir.h"
#include "../h/conf.h"
#include "../h/user.h"
#include "../h/file.h"
#include "../h/map.h"
#include "../h/vm.h"
#include "../h/mtio.h"
#include "../h/cmap.h"
#include "../sun/dklabel.h"
#include "../sun/dkio.h"
#include "../machine/psl.h"
#include "../machine/mmu.h"
#include "../machine/cpu.h"
#include "../sundev/mbvar.h"
#include "../sundev/screg.h"
#include "../sundev/sireg.h"
#include "../sundev/scsi.h"
#include "../sundev/streg.h"
#define INF 1000000000
/*
* Max # of buffers outstanding per unit.
*/
#define MAXSTBUF 3
/*
* Bits in minor device.
*/
#define STUNIT(dev) (minor(dev) & 03)
#define T_NOREWIND 04
#define QIC_24 8
extern struct scsi_unit stunits[];
extern struct scsi_unit_subr scsi_unit_subr[];
extern struct scsi_tape stape[];
extern int nstape;
/*
* Return a pointer to this unit's unit structure.
*/
stunitptr(md)
register struct mb_device *md;
{
return ((int)&stunits[md->md_unit]);
}
/*
* Attach device (boot time).
*/
stattach(md)
register struct mb_device *md;
{
register struct scsi_unit *un;
register struct scsi_tape *dsi;
un = &stunits[md->md_unit];
dsi = &stape[md->md_unit];
un->un_md = md;
un->un_mc = md->md_mc;
un->un_unit = md->md_unit;
un->un_target = TARGET(md->md_slave);
un->un_lun = LUN(md->md_slave);
un->un_ss = &scsi_unit_subr[TYPE(md->md_flags)];
dsi->un_ctype = ST_TYPE_INVALID;
dsi->un_openf = CLOSED;
}
stinit(dev)
dev_t dev;
{
register int unit;
register struct scsi_tape *dsi;
unit = STUNIT(dev);
dsi = &stape[unit];
dsi->un_ctype = 1;
dsi->un_openf = OPENING;
stcmd(dev, SC_TEST_UNIT_READY, 0);
if (stcmd(dev, SC_REQUEST_SENSE, 0) == 0 || dsi->un_openf != OPEN) {
dsi->un_openf = OPENING;
if (stcmd(dev, SC_REQUEST_SENSE, 0) == 0 ||
dsi->un_openf != OPEN) {
if (stcmd(dev, SC_REQUEST_SENSE, 0) == 0 ||
dsi->un_openf != OPEN) {
if (dsi->un_openf == OPEN_FAILED) {
uprintf("st%d: no cartridge loaded\n",
unit);
} else {
uprintf("st%d: tape not online \n",
unit);
}
dsi->un_openf = CLOSED;
dsi->un_ctype = ST_TYPE_INVALID;
return (0);
}
}
}
dsi->un_openf = CLOSED;
return (1);
}
sctopen(dev, flag)
dev_t dev;
int flag;
{
register struct scsi_unit *un;
register int unit, s;
register struct scsi_tape *dsi;
register int i;
unit = STUNIT(dev);
if (unit > nstape) {
u.u_error = ENXIO;
return;
}
un = &stunits[unit];
dsi = &stape[unit];
if (un->un_mc == 0) { /* never attached */
u.u_error = ENXIO;
return;
}
/* determine type of tape controller */
if (dsi->un_ctype == ST_TYPE_INVALID) {
if (stinit(dev) == 0) {
u.u_error = EIO;
return;
}
}
s = spl6();
if (dsi->un_openf != CLOSED) { /* already open */
(void) splx(s);
u.u_error = EBUSY;
return;
}
dsi->un_openf = OPEN;
(void) splx(s);
dsi->un_read_only = 0;
/* must be qic 24 format */
if (!(minor(dev) & QIC_24)) {
dsi->un_openf = CLOSED;
u.u_error = EIO;
return;
}
if ((flag & FWRITE) && dsi->un_read_only) {
uprintf("st%d: cartridge is write protected \n", unit);
dsi->un_openf = CLOSED;
u.u_error = EIO;
return;
}
dsi->un_lastiow = 0;
dsi->un_lastior = 0;
dsi->un_next_block = 0;
dsi->un_last_block = INF;
/* if we reset these here they will never be preserved for "mt status"
dsi->un_retry_ct = 0;
dsi->un_underruns = 0;
*/
u.u_error = 0;
return;
}
/*ARGSUSED*/
sctclose(dev, flag)
register dev_t dev;
int flag;
{
register struct scsi_unit *un;
register struct scsi_tape *dsi;
un = &stunits[STUNIT(dev)];
dsi = &stape[STUNIT(dev)];
dsi->un_openf = CLOSING;
if (dsi->un_lastiow) {
if (stcmd(dev, SC_WRITE_FILE_MARK, 0) == 0) {
printf("st%d: stclose failed to write file mark\n",
un - stunits);
}
}
if ((minor(dev) & T_NOREWIND) == 0) {
(void) stcmd(dev, SC_REWIND, -1);
}
dsi->un_openf = CLOSED;
}
stcmd(dev, cmd, count)
dev_t dev;
int cmd, count;
{
register struct buf *bp;
register int s, error;
register struct scsi_unit *un;
un = &stunits[STUNIT(dev)];
bp = &un->un_sbuf;
s = splx(pritospl(un->un_mc->mc_intpri));
while (bp->b_flags & B_BUSY) {
/*
* special test because B_BUSY never gets cleared in
* the non-waiting rewind case.
*/
if (bp->b_bcount == -1 && (bp->b_flags & B_DONE)) {
break;
}
bp->b_flags |= B_WANTED;
sleep((caddr_t) bp, PRIBIO);
}
bp->b_flags = B_BUSY | B_READ;
(void) splx(s);
bp->b_dev = dev;
bp->b_bcount = count;
un->un_scmd = cmd;
ststrategy(bp);
/*
* In case of rewind on close, don't wait.
*/
if (cmd == SC_REWIND && count == -1) {
return (1);
}
s = splx(pritospl(un->un_mc->mc_intpri));
while ((bp->b_flags & B_DONE) == 0) {
sleep((caddr_t) bp, PRIBIO);
}
(void) splx(s);
error = geterror(bp);
if (bp->b_flags & B_WANTED) {
wakeup((caddr_t) bp);
}
bp->b_flags &= B_ERROR; /* clears B_BUSY */
return (error == 0);
}
ststrategy(bp)
register struct buf *bp;
{
register struct scsi_unit *un;
register int unit, s;
register struct buf *ap;
register struct scsi_tape *dsi;
unit = STUNIT(bp->b_dev);
if (unit > nstape) {
printf("st%d: ststrategy: invalid unit %x\n", unit, unit);
bp->b_flags |= B_ERROR;
iodone(bp);
return;
}
un = &stunits[unit];
dsi = &stape[unit];
if (dsi->un_openf != OPEN && bp != &un->un_sbuf) {
bp->b_flags |= B_ERROR;
iodone(bp);
return;
}
s = splx(pritospl(un->un_mc->mc_intpri));
while (dsi->un_bufcnt >= MAXSTBUF) {
sleep((caddr_t) &dsi->un_bufcnt, PRIBIO);
}
dsi->un_bufcnt++;
/*
* Put the block at the end of the queue.
* Should probably have a pointer to the end of
* the queue, but the queue can't get too long,
* so the added code complexity probably isn't
* worth it.
*/
ap = &un->un_utab;
while (ap->b_actf != NULL) {
ap = ap->b_actf;
}
ap->b_actf = bp;
bp->b_actf = NULL;
if (un->un_utab.b_active == 0) {
(*un->un_c->c_ss->scs_ustart)(un);
bp = &un->un_mc->mc_tab;
if (bp->b_actf && bp->b_active == 0) {
(*un->un_c->c_ss->scs_start)(un);
}
}
(void) splx(s);
}
/*
* Start the operation.
*/
/*ARGSUSED*/
ststart(bp, un)
register struct buf *bp;
register struct scsi_unit *un;
{
register int bno;
register struct scsi_tape *dsi;
dsi = &stape[STUNIT(bp->b_dev)];
/*
* Default is that last command was NOT a read/write command;
* if we issue a read/write command we will notice this in stintr().
*/
dsi->un_lastiow = 0;
dsi->un_lastior = 0;
if (bp == &un->un_sbuf) {
un->un_cmd = un->un_scmd;
un->un_count = bp->b_bcount;
} else if (bp == &un->un_rbuf) {
if (bp->b_flags & B_READ) {
if (dsi->un_eof) {
bp->b_resid = bp->b_bcount;
iodone(bp);
if (dsi->un_bufcnt-- >= MAXSTBUF) {
wakeup((caddr_t) &dsi->un_bufcnt);
}
return (0);
}
un->un_cmd = SC_READ;
} else {
un->un_cmd = SC_WRITE;
}
un->un_count = howmany(bp->b_bcount, DEV_BSIZE);
un->un_flags |= SC_UNF_DVMA;
} else {
bno = bp->b_blkno;
if (bno > dsi->un_last_block && bp->b_flags & B_READ) {
/*
* Can't read past EOF.
*/
bp->b_flags |= B_ERROR;
bp->b_error = EIO;
iodone(bp);
if (dsi->un_bufcnt-- >= MAXSTBUF) {
wakeup((caddr_t) &dsi->un_bufcnt);
}
return (0);
}
if (bno == dsi->un_last_block && bp->b_flags & B_READ) {
/*
* Reading at EOF returns 0 bytes.
*/
bp->b_resid = bp->b_bcount;
iodone(bp);
if (dsi->un_bufcnt-- >= MAXSTBUF) {
wakeup((caddr_t) &dsi->un_bufcnt);
}
return (0);
}
if ((bp->b_flags & B_READ) == 0) {
/*
* Writing sets EOF.
*/
dsi->un_last_block = bno + 1;
}
if (bno != dsi->un_next_block) {
/*
* Not the next record.
* In theory we could space forward, or even rewind
* and space forward, and maybe someday we will.
* For now, no one really needs this capability.
*/
bp->b_flags |= B_ERROR;
bp->b_error = ENXIO;
iodone(bp);
if (dsi->un_bufcnt-- >= MAXSTBUF) {
wakeup((caddr_t) &dsi->un_bufcnt);
}
return (0);
}
/*
* Position OK, we can do the read or write.
*/
if (bp->b_flags & B_READ) {
un->un_cmd = SC_READ;
} else {
un->un_cmd = SC_WRITE;
}
un->un_count = howmany(bp->b_bcount, DEV_BSIZE);
un->un_flags |= SC_UNF_DVMA;
}
bp->b_resid = 0;
return (1);
}
/*
* Make a command description block.
*/
stmkcdb(c, un)
register struct scsi_ctlr *c;
register struct scsi_unit *un;
{
register struct scsi_cdb *cdb;
register struct scsi_tape *dsi;
int density = 0;
cdb = &c->c_cdb;
bzero((caddr_t)cdb, sizeof (*cdb));
cdb->cmd = un->un_cmd;
cdb->lun = un->un_lun;
un->un_dma_addr = un->un_dma_count = 0;
dsi = &stape[un->un_unit];
switch (un->un_cmd) {
case SC_WRITE_FILE_MARK:
case SC_LOAD:
cdb->count = 1;
break;
case SC_TEST_UNIT_READY:
break;
case SC_REWIND:
break;
case SC_ERASE_CARTRIDGE:
cdb->t_code = 1;
break;
case SC_REQUEST_SENSE:
un->un_dma_addr = (int)c->c_sense - (int)DVMA;
un->un_dma_count = cdb->count = sizeof (struct st_archive_sense);
break;
case SC_READ:
case SC_WRITE:
cdb->t_code = 1;
cdb->high_count = un->un_count >> 16;
cdb->mid_count = (un->un_count >> 8) & 0xFF;
cdb->low_count = un->un_count & 0xFF;
un->un_dma_addr = un->un_baddr;
un->un_dma_count = un->un_count * DEV_BSIZE;
break;
case SC_SPACE_FILE:
if (un->un_count == 0)
cdb->t_code = 3; /* space to end */
else
cdb->t_code = 1; /* space files, not records */
/* fall through ... */
case SC_SPACE_REC:
cdb->cmd = SC_SPACE;
cdb->high_count = un->un_count >> 16;
cdb->mid_count = (un->un_count >> 8) & 0xFF;
cdb->low_count = un->un_count & 0xFF;
un->un_dma_addr = un->un_dma_count = 0;
break;
default:
printf("st%d: stmkcdb: invalid command %x\n", un - stunits,
un->un_cmd);
break;
}
}
stintr(c, resid, error)
register struct scsi_ctlr *c;
register int resid, error;
{
register struct scsi_unit *un;
register struct buf *bp;
register struct scsi_tape *dsi;
struct st_archive_sense *ars;
un = c->c_un;
bp = un->un_mc->mc_tab.b_actf->b_actf;
dsi = &stape[STUNIT(bp->b_dev)];
ars = (struct st_archive_sense *)c->c_sense;
/*
* We determine which tape controller we have by the number of
* sense bytes we get back.
*/
if (dsi->un_openf == OPENING && un->un_cmd == SC_REQUEST_SENSE) {
if (resid || ST_NO_CART(dsi, c->c_sense)) {
dsi->un_openf = OPEN_FAILED;
} else {
dsi->un_openf = OPEN;
if (ST_WRITE_PROT(dsi, c->c_sense))
dsi->un_read_only = 1;
}
}
if (c->c_scb.busy)
bp->b_flags |= B_ERROR;
else if (error || c->c_scb.chk || resid > 0) {
if (c->c_scb.chk && ST_RESET(dsi, c->c_sense)) {
/*
* Power on or reset occurred
*/
dsi->un_reset_occurred = 1;
bp->b_flags |= B_ERROR;
} else if (c->c_scb.chk && ST_NO_CART(dsi, c->c_sense)) {
printf("st%d: no cartridge loaded \n",
un - stunits);
bp->b_flags |= B_ERROR;
} else if (c->c_scb.chk && ST_FILE_MARK(dsi, c->c_sense)) {
/*
* File mark detected.
*/
dsi->un_eof = 1;
switch (un->un_cmd) {
case SC_READ:
dsi->un_next_block += un->un_count -
resid / DEV_BSIZE;
dsi->un_last_block = dsi->un_next_block;
break;
case SC_SPACE_REC:
dsi->un_last_block = dsi->un_next_block +=
un->un_count; /* a little high */
break;
default:
printf("st%d: scsi tape hit eof on cmd %x\n",
un - stunits, un->un_cmd);
break;
}
bp->b_resid = resid;
} else if ((un->un_cmd == SC_WRITE ||
un->un_cmd == SC_WRITE_FILE_MARK) &&
c->c_scb.chk && ST_WRITE_PROT(dsi, c->c_sense)) {
/*
* Write protected tape.
*/
printf("st%d: tape is write protected \n, ",
un - stunits);
dsi->un_read_only = 1;
bp->b_flags |= B_ERROR;
} else if (c->c_scb.chk && ST_EOT(dsi, c->c_sense)) {
/*
* End of tape.
*/
bp->b_resid = bp->b_bcount;
if (un->un_cmd == SC_WRITE) {
/*
* Setting this flag makes stclose()
* write a file mark before closing.
* Until a file mark is written, the
* tape will return invalid command
* indications and not respond to
* rewinds.
*/
dsi->un_lastiow = 1;
}
} else if (c->c_scb.chk && ST_ILLEGAL(dsi, c->c_sense)) {
printf("st%d: illegal command 0x%x\n", un-stunits, un->un_cmd);
bp->b_flags |= B_ERROR;
} else if (ST_CORRECTABLE(dsi, c->c_sense))
goto success;
else if (ST_EOD(dsi, c->c_sense))
bp->b_flags |= B_ERROR;
else {
/*
* Some other error which we can't handle.
*/
if ((c->c_scb.chk && dsi->un_openf == OPEN))
st_pr_sense(c, dsi);
bp->b_flags |= B_ERROR;
}
} else {
success:
switch (un->un_cmd) {
case SC_REWIND:
case SC_ERASE_CARTRIDGE:
case SC_MODE_SELECT:
case SC_LOAD:
dsi->un_next_block = 0;
dsi->un_eof = 0;
break;
case SC_WRITE:
dsi->un_lastiow = 1;
dsi->un_next_block += un->un_count;
break;
case SC_READ:
dsi->un_lastior = 1;
dsi->un_next_block += un->un_count;
break;
case SC_SPACE_FILE:
dsi->un_next_block = 0;
dsi->un_last_block = INF;
dsi->un_eof = 0;
break;
case SC_SPACE_REC:
dsi->un_next_block += un->un_count;
break;
case SC_REQUEST_SENSE:
dsi->un_status = ((char *)ars)[2];
/*
if (ars->ext_sense.add_len >= AR_ES_ADD_LEN) {
dsi->un_retry_ct +=
(ars->retries_msb << 8) +
ars->retries_lsb;
}
*/
break;
case SC_TEST_UNIT_READY:
break;
case SC_WRITE_FILE_MARK:
dsi->un_next_block = 0;
dsi->un_last_block = 0; /* i.e. no reads allowed */
break;
default:
printf("st%d: stintr: invalid command %x\n",
un - stunits, un->un_cmd);
break;
}
}
if (bp == &un->un_sbuf &&
((un->un_flags & SC_UNF_DVMA) == 0)) {
(*c->c_ss->scs_done)(un->un_mc);
} else {
mbdone(un->un_mc);
un->un_flags &= ~SC_UNF_DVMA;
}
if (dsi->un_bufcnt-- >= MAXSTBUF) {
wakeup((caddr_t) &dsi->un_bufcnt);
}
}
st_pr_sense(c, dsi)
register struct scsi_ctlr *c;
register struct scsi_tape *dsi;
{
register struct scsi_unit *un;
register u_char *cp;
register int i;
un = c->c_un;
cp = (u_char *)c->c_sense;
printf("st%d: stintr: sense ", un - stunits);
for (i=0; i < sizeof (struct st_archive_sense); i++)
printf("%x ", *cp++);
printf("\n");
}
sctread(dev)
register dev_t dev;
{
register struct scsi_unit *un;
register int unit, r, resid;
register struct scsi_tape *dsi;
unit = STUNIT(dev);
if (unit > nstape) {
u.u_error = ENXIO;
return;
}
un = &stunits[unit];
dsi = &stape[unit];
if (u.u_count % DEV_BSIZE) {
u.u_error = ENXIO;
return; /* drive can't do it */
}
resid = u.u_count;
dsi->un_next_block = u.u_offset / DEV_BSIZE;
dsi->un_last_block = INF;
physio(ststrategy, &un->un_rbuf, dev, B_READ, minphys);
if (dsi->un_eof && u.u_count == resid) {
dsi->un_eof = 0; /* the user is really getting it */
}
}
sctwrite(dev)
register dev_t dev;
{
register struct scsi_unit *un;
register int unit;
register struct scsi_tape *dsi;
unit = STUNIT(dev);
if (unit > nstape) {
u.u_error = ENXIO;
return;
}
un = &stunits[unit];
dsi = &stape[unit];
dsi->un_next_block = u.u_offset / DEV_BSIZE;
dsi->un_last_block = INF;
physio(ststrategy, &un->un_rbuf, dev, B_WRITE, minphys);
}
/*ARGSUSED*/
sctioctl(dev, cmd, data, flag)
register dev_t dev;
register int cmd;
register caddr_t data;
int flag;
{
register int fcount;
struct mtop mtop;
struct mtget mtget;
register int unit;
register struct scsi_tape *dsi;
static int ops[] = {
SC_WRITE_FILE_MARK, /* write tape mark */
SC_SPACE_FILE, /* forward space file */
SC_SPACE_FILE, /* backspace file */
SC_SPACE_REC, /* forward space record */
SC_SPACE_REC, /* backspace record */
SC_REWIND, /* rewind tape */
SC_REWIND, /* unload - we just rewind */
SC_REQUEST_SENSE, /* get status */
SC_REWIND, /* retension - rewind + vu_57 */
SC_ERASE_CARTRIDGE, /* erase entire tape */
};
unit = STUNIT(dev);
if (unit > nstape) {
u.u_error = ENXIO;
return;
}
dsi = &stape[unit];
switch (cmd) {
case MTIOCTOP: /* tape operation */
if (copyin((caddr_t)data, (caddr_t)&mtop, sizeof(mtop))) {
u.u_error = EFAULT;
return;
}
switch (mtop.mt_op) {
case MTWEOF:
case MTERASE:
if (dsi->un_read_only) {
u.u_error = ENXIO;
return;
}
}
switch (mtop.mt_op) {
case MTBSF:
case MTBSR:
fcount = -mtop.mt_count;
break;
case MTRETEN:
dsi->un_reten_rewind = 1;
/* FALL THRU */
case MTFSF:
fcount = mtop.mt_count;
if (fcount == -1)
fcount = 0;
case MTWEOF:
case MTREW:
case MTOFFL:
case MTNOP:
case MTERASE:
fcount = mtop.mt_count;
break;
default:
u.u_error = ENXIO;
return;
}
/*
* If eof_flag then we hit eof but didn't tell the user yet.
*/
if (ops[mtop.mt_op] == SC_SPACE_FILE && dsi->un_eof) {
dsi->un_eof = 0;
if (mtop.mt_op == MTFSF)
fcount--;
}
if (stcmd(dev, ops[mtop.mt_op], fcount) == 0)
u.u_error = EIO;
return;
case MTIOCGET:
if (stcmd(dev, SC_REQUEST_SENSE, 0) == 0) {
u.u_error = EIO;
return;
}
mtget.mt_type = MT_ISSC;
mtget.mt_erreg = dsi->un_status;
mtget.mt_fileno = dsi->un_retry_ct;
mtget.mt_blkno = dsi->un_underruns;
dsi->un_retry_ct = dsi->un_underruns = 0;
if (copyout((caddr_t)&mtget, data, sizeof(mtget)))
u.u_error = EFAULT;
return;
default:
u.u_error = ENXIO;
return;
}
}
#endif NST > 0
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.