|
|
coherent
/***********************************************************************
* Module: haict.c
*
* Unix device driver functions for accessing SCSI tape drives as
* character devices. Conforms to Mark Williams Coherent definition
* of the Unix Device Driver interface for Coherent v4.0.1.
*
* The philosophy of this driver is to support basic functions on
* the tape drive (read, write, retension, rewind, skip, etc). There
* are more features out there for all the SCSI tape drives out there
* than I know what to do with. I leave custom support for these
* drives to the people who have them. To this end this drive will
* blindly follow whatever information it can get using Mode Sense
* and Read Block Limits CDB's. These tests are done at open time.
* An application can change the operation of the driver by applying
* the mode select command through I/O Control mechanism.
*
* Copyright (c) 1993, Christopher Sean Hilton, All Rights Reserved.
*
* Last Modified: Mon Jul 26 17:18:59 1993 by [chris]
*
* $Id: haiCt.c,v 1.1.1.1 2019/05/29 04:56:36 root Exp $
*
* $Log: haiCt.c,v $
* Revision 1.1.1.1 2019/05/29 04:56:36 root
* coherent
*
* Revision 2.4 93/08/09 13:45:06 bin
* Kernel 82 changes
*
*/
#include <stddef.h>
#include <sys/coherent.h>
#include <sys/buf.h>
#include <sys/inode.h>
#include <sys/stat.h>
#include <sys/sched.h>
#include <errno.h>
#include <sys/mtioctl.h>
#include <sys/file.h> /* Louis */
#include <sys/haiscsi.h>
#include <sys/haiioctl.h>
#define REWINDTAPE 0x01
#define IMMEDIATE 0x0010
#define REQSENSE 0x03
#define READBLKLMT 0x05
#define READ 0x08
#define WRITE 0x0a
#define WRITEFM 0x10
#define SPACE 0x11
#define SKIP_BLK 0x00
#define SKIP_FM 0x01
#define SKIP_SEQFM 0x02
#define SKIP_EOT 0x03
#define MODESELECT 0x15
#define ERASE 0x19
#define ERASE_BLOCK 0x0000
#define ERASE_TAPE 0x0001
#define MODESENSE 0x1a
#define LOAD 0x1b
#define RETENSION 0x0020
/***********************************************************************
* The Tandberg TDC3600 requires special handling because it doesn't
* respond properly to a write when the tape is at the logical end
* of tape. Tape should be organized as follows:
*
* File 1 --------------| File 2-m
* DATA[1] | ...DATA[n] | FILEMARK | DATA[1-n] | FILEMARK | FILEMARK |
*
* The double file marks signify the Logical End of Tape. When a
* tape opened in write mode is written to and then closed on the
* no rewind device it is necessary to write a Logical End of tape
* and then skip backwards over one of the filemarks. This leaves
* the tape positioned between the two filemarks. When this happens
* on my Tandberg the drive will then lock up (nice huh?) and fail
* to do anything until the tape is rewound or retensioned.
*/
#define CTDIRTY 0x0001
#define CTCLOSING 0x0002
#define CTILI 0x0020 /* Sensekey's Illegal Length Indicator */
#define CTEOM 0x0040 /* Sensekey's End OF Media bit */
#define CTFILEMARK 0x0080 /* Sensekey's Filemark bit */
#define CTSKMASK (CTILI | CTEOM | CTFILEMARK)
#define CTRDMD 0x0100 /* we are reading from the tape */
#define CTWRTMD 0x0200 /* we are writing to the tape */
#ifndef HAICACHESZ
/*
* There wasn't much of a difference in speed between 32 and 40 block
* in my experiance so save as much kalloc memory as possible.
*/
#define HAICACHESZ (32 * BSIZE)/* 32 Block Cache for each device */
#endif
#ifndef HAICTVERBOSE
/*
* HAICTVERBOSE is done as a define because I wanted to be able to use two
* tape drives in my system at one time (why?!). ld complains if it is a
* patchable constant. This problem must be solved but products must also
* ship. [csh]
*/
#define HAICTVERBOSE 0x0001 /* Switch console messages on/off */
#endif
typedef enum {
CTIDLE = 0,
CTINIT,
CTFBRD,
CTVBRD,
CTFBWRT,
CTVBWRT,
CTLASTWRT,
CTSENSE,
CTWRITEFM,
CTSPACE,
CTREWIND,
CTERASE,
CTLOADRETEN,
CTIOCTL
} ctstate_t;
/* Block Descriptors in the mode sense command. */
typedef struct blkdscr_s *blkdscr_p;
typedef struct blkdscr_s {
union {
unsigned char mediatype;
unsigned long totalblocks;
} mt;
union {
unsigned char reserved;
unsigned long blocksize;
} rb;
} blkdscr_t;
typedef struct blklim_s *blklim_p;
typedef struct blklim_s {
unsigned blmax; /* Maximum size for Reads/Writes */
unsigned short blmin; /* Minimum size for Reads/Writes */
} blklim_t;
typedef struct ctctrl_s *ctctrl_p;
typedef struct ctctrl_s {
char *cache, /* Transfer Cache */
*start; /* Start of data in cache */
size_t cachesize, /* Size of cache */
avail; /* bytes availaible in cache */
ctstate_t state;
unsigned block, /* Block size of device */
blmax; /* Block limits maximum */
unsigned short blmin, /* Block Limits minimum */
flags, /* Flags from device */
inuse; /* In Use flag */
srb_t srb; /* SCSI Request block for transfers */
} ctctrl_t;
static int ctinit(); /* Initialize a SCSI device at (id) */
static void ctopen(); /* Open SCSI tape at (dev) */
static void ctclose(); /* Close a SCSI tape at (dev) */
static void ctread(); /* Read from SCSI tape at (dev) */
static void ctwrite(); /* Write SCSI tape at (dev) */
static void ctioctl(); /* I/O Control routine */
static int fillcache(); /* Fill the tape cache */
static int flushcache(); /* Flush the tape cache */
extern int nulldev();
extern int nonedev();
#define min(a, b) ((a) < (b) ? (a) : (b))
#ifdef TDC3600
#define ctdca haict3600
#endif
dca_t ctdca = {
ctopen, /* Open */
ctclose, /* Close */
hainonblk, /* No Block point here but don't just drop Buffers */
ctread, /* Read */
ctwrite, /* Write */
ctioctl, /* Ioctl */
ctinit, /* Load */
nulldev, /* Unload */
nulldev /* Poll */
};
static ctctrl_p ctdevs[MAXDEVS];
/***********************************************************************
* Utility functions. *
***********************************************************************/
#define ctvmsg(l, cmd) { if (HAICTVERBOSE & (l)) { (cmd); } }
/***********************************************************************
* ctbusywait()
*
* Wait for the tape drive state to return to idle. This is easy
* for two reasons: 1) With no block entry point its safe to sleep
* at any time. 2) We shouldn't really need this anyhow. This is
* unneccessary because without a block routine and with only one
* process able to open the tape drive at a time the state of the
* tape drive driver is well defined. So, why is it here you ask?
* because one day some user might fork a process that owns the tape
* drive. This would cause 40 days and nights worth of rain etc.
* Now all that will happen is both processes will be able to write/read
* from the tape drive and the data that they get will be complete
* garbage. However, the kernel will not break.
*/
static int ctbusywait(c, newstate)
register ctctrl_p c;
register ctstate_t newstate;
{
register int s;
int retval;
s = sphi();
retval = 1;
while (c->state != CTIDLE)
if (x_sleep(&(c->srb. status), pritape, slpriSigCatch, "ctbsywt")) {
u. u_error = EINTR;
retval = 0;
break;
}
c->state = newstate;
spl(s);
return retval;
} /* ctbusywait() */
/***********************************************************************
* loadtape()
*
* Move the tape to the load point.
*/
static int loadtape(c, opt)
register ctctrl_p c;
int opt;
{
register srb_p r = &(c->srb);
register g0cmd_p g0 = &(r->cdb. g0);
if (!ctbusywait(c, CTLOADRETEN))
return 0;
r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
r->xferdir = 0;
r->timeout = 300;
memset(g0, 0, sizeof(cdb_t));
g0->opcode = LOAD;
g0->xfr_len = 1; /* Move tape to load point. */
if (opt & IMMEDIATE)
g0->lun_lba |= 1;
if (opt & RETENSION)
g0->xfr_len |= 2;
doscsi(r, 4, pritape, slpriSigCatch, "loadtape");
if (r->status != ST_GOOD && printerror(r, "Load failed"))
u. u_error = EIO;
c->state = CTIDLE;
c->flags &= ~(CTFILEMARK | CTEOM);
return (r->status == ST_GOOD);
} /* loadtape() */
/***********************************************************************
* writefm()
*
* Write Filemarks on the tape.
*/
static void writefm(c, count)
register ctctrl_p c;
int count;
{
register srb_p r = &(c->srb);
register g0cmd_p g0 = &(r->cdb. g0);
if (!ctbusywait(c, CTWRITEFM))
return;
r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
r->xferdir = 0;
r->timeout = 40;
g0->opcode = WRITEFM;
g0->lun_lba = (r->lun << 5);
g0->lba_mid = ((unsigned char *) &count)[2];
g0->lba_low = ((unsigned char *) &count)[1];
g0->xfr_len = ((unsigned char *) &count)[0];
g0->control = 0;
doscsi(r, 4, pritape, slpriSigCatch, "writefm");
if (r->status != ST_GOOD && printerror(r, "Write filemarks failed"))
u. u_error = EIO;
c->state = CTIDLE;
} /* writefm() */
/***********************************************************************
* space()
*
* Space over blocks/filemarks/etc.
*/
static void space(c, count, object)
register ctctrl_p c;
int count;
int object;
{
register srb_p r = &(c->srb);
register g0cmd_p g0 = &(r->cdb. g0);
if (!ctbusywait(c, CTSPACE))
return;
r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
r->xferdir = 0;
r->timeout = 300;
g0->opcode = SPACE;
g0->lun_lba = (r->lun << 5) | (object & 3);
g0->lba_mid = ((unsigned char *) &count)[2];
g0->lba_low = ((unsigned char *) &count)[1];
g0->xfr_len = ((unsigned char *) &count)[0];
g0->control = 0;
doscsi(r, 2, pritape, slpriSigCatch, "space");
if (r->status != ST_GOOD && printerror(r, "Space failed"))
u. u_error = EIO;
c->state = CTIDLE;
} /* space() */
/***********************************************************************
* rewind()
*
* Rewind the tape drive back to the load point.
*/
static void rewind(c, wait)
register ctctrl_p c;
int wait;
{
register srb_p r = &(c->srb);
register g0cmd_p g0 = &(r->cdb. g0);
if (!ctbusywait(c, CTREWIND))
return;
r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
r->timeout = 300;
r->xferdir = 0;
memset(g0, 0, sizeof(cdb_t));
g0->opcode = REWINDTAPE;
if (!wait)
g0->lun_lba = (r->lun << 5) | 1;
doscsi(r, 2, pritape, slpriSigCatch, "rewind");
if (r->status != ST_GOOD && printerror(r, "Rewind failed"))
u. u_error = EIO;
c->flags = 0;
c->state = CTIDLE;
} /* rewind() */
static void erase(c, to_eot)
register ctctrl_p c;
int to_eot;
{
register srb_p r = &(c->srb);
register g0cmd_p g0 = &(r->cdb. g0);
if (!ctbusywait(c, CTERASE))
return;
r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
r->timeout = 300;
r->xferdir = 0;
memset(g0, 0, sizeof(cdb_t));
g0->opcode = ERASE;
g0->lun_lba = (r->lun << 5);
if (to_eot)
g0->lun_lba |= 1;
doscsi(r, 2, pritape, slpriSigCatch, "erase");
if (r->status != ST_GOOD && printerror(r, "Erase failed"))
u. u_error = EIO;
if (to_eot)
c->flags &= ~(CTFILEMARK | CTEOM | CTILI | CTDIRTY);
c->state = CTIDLE;
} /* erase() */
/***********************************************************************
* Device Driver Entry Point routines. *
***********************************************************************/
/***********************************************************************
* ctinit()
*
* Initialize the tape device at (id). This doesn't do anything,
* not even verify that the drive is there because it could be powered
* off.
*/
static int ctinit(id)
register int id;
{
register ctctrl_p c = kalloc(sizeof(ctctrl_t));
if (!c) {
printf("\tTape Driver: Could not allocate control structure.\n");
return 0;
}
printf("\tCoherent SCSI Tape driver v1.1\n");
memset(c, 0, sizeof(ctctrl_t));
c->inuse = 0;
c->cache = c->start = NULL;
c->cachesize = HAICACHESZ;
c->srb. target = id;
c->srb. lun = 0;
c->state = CTIDLE;
ctdevs[id] = c;
return 1;
}
static void ctopen(dev, mode)
dev_t dev;
int mode;
{
register ctctrl_p c = ctdevs[tid(dev)];
register srb_p r = &(c->srb);
register g0cmd_p g0 = &(r->cdb. g0);
int rblerf; /* read block limits error flag */
int s;
char buf[64];
blkdscr_p bd = (blkdscr_p) (buf + 4);
blklim_p bl = (blklim_p) (buf);
if (!c) {
u. u_error = ENXIO;
return;
}
if ((mode != IPR) && (mode != IPW)) {
u. u_error = EINVAL;
return;
}
s = sphi();
if (c->inuse) {
u. u_error = EDBUSY;
goto done;
}
c->inuse = 1;
c->state = CTINIT;
r->dev = dev; /* Save the rewind bit for close. */
/***********************************************************************
* Repeat the test unit ready command to the tape drive. This tells
* the state of the tape drive. Repeating the command also clears out
* the request sense condition which comes after each time the user
* changes tapes.
*/
r->timeout = 2;
r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
r->xferdir = 0;
memset(g0, 0, sizeof(cdb_t)); /* Test Unit Ready */
memset(r->sensebuf, 0, sizeof(r->sensebuf));
doscsi(r, 4, pritape, slpriSigCatch, "ctopen");
/***********************************************************************
* If the command fails there probably wasn't a tape in the drive.
*/
if (r->status != ST_GOOD) { /* Is there a tape in the drive? */
if (r->status != ST_USRABRT) {
/* Otherwise assume no tape. */
u. u_error = ENXIO;
devmsg(r->dev, "Tape drive not ready.");
goto openfailed;
}
}
#ifdef TDC3600
/***********************************************************************
* TDC3600's do a retension after all bus device resets and tape changes
* so I block in the open routine waiting for that operation to complete.
* This is a kludge to fix the author's system. I anticipate that
* other host adapters may not be so kind about retrying commands
* until a device reports that it is no longer busy. Hence the error
* message when this command fails. All this really does is allows
* me to safely start applications while the initial retension is still
* happening [csh].
*/
else {
if (r->sensebuf[0] == 0x70 && r->sensebuf[2] == 0x06) {
/* Yep, The user just put it there */
r->buf. space = (int) r->buf. addr. caddr = r->buf. size = 0;
r->xferdir = 0;
r->timeout = 300;
memset(g0, 0, sizeof(cdb_t));
g0->opcode = LOAD;
g0->xfr_len = 1; /* Move tape to load point. */
doscsi(r, 4, pritape, slpriSigCatch, "ctopen");
if (r->status != ST_GOOD &&
printerror(r, "Load failed - TDC3600 not ready")) {
u. u_error = ENXIO;
goto openfailed;
}
}
}
#endif
ctvmsg(0x0100, (devmsg(r->dev, "Read block limits.")));
#ifdef TDC3600
c->blmin = c->blmax = 512;
ctvmsg(0x0100, (devmsg(r->dev, "Tandberg TDC36XX - Block limits set")));
rblerf = 0;
#else
r->buf. space = KRNL_ADDR;
r->buf. addr. caddr = (caddr_t) bl;
r->buf. size = sizeof(blklim_t);
r->xferdir = DMAREAD;
r->timeout = 2;
memset(g0, 0, sizeof(cdb_t));
g0->opcode = READBLKLMT;
g0->xfr_len = 6;
doscsi(r, 3, pritape, slpriSigCatch, "ctopen");
/***********************************************************************
* This is a bit oblique: The Read Block limits is used to determine
* what the tape drive that we have can do. I have yet to run into
* a tape drive that can do variable mode yet so I'm just winging
* this. In any case all I did was make it okay for the Archive Tape
* drives to say "I don't support that command".
*/
if (rblerf = (r->status != ST_GOOD)) {
ctvmsg(0x0010, (printerror(r, "Read Block LImits")));
c->blmax = c->blmin = 0;
}
else {
flip(bl->blmax); /* SCSI to INTEL order */
flip(bl->blmin); /* Ditto */
c->blmax = (bl->blmax & 0x00ffffff);
c->blmin = bl->blmin;
}
#endif
/***********************************************************************
* Use mode sense to find out if the tape is write protected and also
* to find out what the current tape blocksize is.
*/
r->buf. space = KRNL_ADDR;
r->buf. addr. caddr = (caddr_t) buf;
r->buf. size = sizeof(buf);
r->xferdir = DMAREAD;
r->timeout = 2;
memset(g0, 0, sizeof(cdb_t));
g0->opcode = MODESENSE;
g0->xfr_len = sizeof(buf);
doscsi(r, 3, pritape, slpriSigCatch, "ctopen");
if (r->status != ST_GOOD) {
if (printerror(r, "Mode sense failed"))
u. u_error = EIO;
goto openfailed;
}
/***********************************************************************
* If tape drive opened in write mode make sure the tape is not write
* protected now.
*/
if (mode == IPW && (buf[2] & 0x80) != 0) {
devmsg(dev, "Tape is write protected");
u. u_error = ENXIO;
goto openfailed;
}
/***********************************************************************
* If mode sense returned any media descriptors take the first one,
* it's the default, and use it.
*
* There are two possible pitfalls here:
*
* 1) According to SCSI-1 it not an error to return zero block
* descriptors. If you're favorite drive returns zero block descriptors
* this driver will fail here.
*
* 2) I am assuming that the first media descriptor returned is
* the "current" media type which the drive expects to use. This
* works with SCSI-2 but SCSI-1 says that the first descriptor is
* the "default" media type. SCSI-1 is lacking in the definition
* of "default" for my conservative taste (when programming is
* concerned).
*
* I would like to put the issue of supporting real oddball tape drives
* like my Tandberg to rest and only have one source source file for
* scsi tape drives but I don't think that will work. In the future
* there will probably be two or maybe three if DAT proves to be as
* much fun as this is.
*/
if (buf[3]) { /* If mode sense returned any media descriptors */
bd->rb. blocksize &= 0xffffff00;
flip(bd->rb. blocksize);
c->block = bd->rb. blocksize;
if (c->block && c->cachesize % c->block)
c->cachesize -= (c->cachesize % c->block);
}
else {
devmsg(r->dev, "No media descriptors: Contact Mark Williams Tech support");
u. u_error = ENXIO;
goto openfailed;
}
ctvmsg(0x0001, devmsg(dev, "Blocksize: %d bytes.", c->block));
/***********************************************************************
* One last check: If we aren't using block mode (!c->block)
* and we didn't get any block limits then we cannot support this
* drive.
*/
if (!c->block && rblerf) {
devmsg(r->dev, "<No block limits on variable mode tape drive>");
devmsg(r->dev, "<Contact Mark Williams Tech Support>");
u. u_error = ENXIO;
goto openfailed;
}
c->flags = (mode == IPR) ? CTRDMD : CTWRTMD;
if (c->block) {
c->cache = kalloc(HAICACHESZ);
if (!c->cache) {
devmsg(dev, "Could not allocate tape cache");
u. u_error = ENOMEM;
goto openfailed;
}
c->avail = (c->flags & CTRDMD) ? 0 : HAICACHESZ;
c->start = c->cache;
}
c->state = CTIDLE;
goto done;
openfailed:
c->state = CTIDLE;
c->inuse = 0;
done:
spl(s);
} /* ctopen() */
/***********************************************************************
* ctclose()
*
* Close the SCSI Device at (dev).
*/
static void ctclose(dev)
register dev_t dev;
{
register ctctrl_p c = ctdevs[tid(dev)];
register srb_p r = &(c->srb);
int s;
if (!c) {
u. u_error = ENXIO;
return;
}
s = sphi();
if (c->block && (c->flags & CTWRTMD)) {
if (ctbusywait(c, CTLASTWRT))
flushcache(c);
c->state = CTIDLE;
}
spl(s);
if (c->cache) {
kfree(c->cache);
c->cache = c->start = NULL;
c->avail = 0;
}
#ifndef TDC3600
/*
* Write two filemarks (Logical End of Tape) and then skip backwards
* over one of them to ready for the another write operation on the
* no rewind device. This code guarantees properly formed tapes according
* to the wisened old nine-track hacker that I work with. The problem
* is that...
*/
if (c->flags & CTDIRTY) {
writefm(c, 1); /* Write a file mark */
writefm(c, 1); /* Write another filemark */
space(c, -1, SKIP_FM); /* Go back to the first filemark */
}
#else
/*
* ... problem is that my Tandberg considers it an error to do anything
* after it has skipped past a filemark. So all commands except load and
* rewind fail after the previous code. The following will work in
* all situations but there is a risk that a user's tapes will only
* have one filemark at Logical End-of-Tape if the user isn't careful
* to use the rewind device the last time he uses tape drive. This is
* only a problem with drives which insist upon doing a Retension each
* time the tape is changed or the drive gets reset.
*/
if (c->flags & CTDIRTY) {
writefm(c, 1);
if (r->dev & REWIND)
writefm(c, 1);
}
#endif
if (r->dev & REWIND)
rewind(c, 0);
c->inuse = 0;
return;
} /* ctclose() */
/***********************************************************************
* fillcache() -- Read from the tape into the cache (really?)
*
* return 0 and set u. u_error on any errors.
*/
static int fillcache(c)
register ctctrl_p c;
{
register srb_p r = (&c->srb);
register g0cmd_p g0 = &(r->cdb. g0);
size_t blocks;
extsense_p e;
int info;
int retval = 0;
r->buf. space = KRNL_ADDR;
r->buf. addr. caddr = (caddr_t) c->cache;
r->buf. size = HAICACHESZ;
r->xferdir = DMAREAD;
r->timeout = 30;
r->tries = 0;
g0->opcode = READ;
g0->lun_lba = (r->lun << 5) | 1;
blocks = HAICACHESZ / c->block;
g0->lba_mid = ((unsigned char *) &blocks)[2];
g0->lba_low = ((unsigned char *) &blocks)[1];
g0->xfr_len = ((unsigned char *) &blocks)[0];
g0->control = 0;
doscsi(r, 1, pritape, slpriSigCatch, "ctblkrd");
switch (r->status) {
case ST_GOOD:
c->start = c->cache;
c->avail = r->buf. size;
retval = 1;
break;
case ST_CHKCOND:
e = r->sensebuf;
if ((e->errorcode & 0x70) == 0x70) {
info = 0;
if (e->errorcode & 0x80) {
info = e->info;
flip(info);
}
if (e->sensekey & (CTFILEMARK | CTEOM)) {
c->flags |= (e->sensekey & (CTFILEMARK | CTEOM));
c->start = c->cache;
c->avail = HAICACHESZ - (info * c->block);
retval = 1;
break;
}
}
printsense(r->dev, "Read failed", r->sensebuf);
u. u_error = EIO;
retval = 0;
break;
case ST_USRABRT:
u. u_error = EINTR;
c->start = c->cache;
c->avail = 0;
retval = 0;
break;
default:
devmsg(r->dev, "Read failed: status (0x%x)", r->status);
u. u_error = EIO;
retval = 0;
break;
}
return retval;
} /* fillcache() */
/***********************************************************************
* ctfbrd() -- Fixed block read handler. Reads from the tape
* drive through the cache when the tape drive is
* in fixed block mode.
*/
static void ctfbrd(c, iop)
register ctctrl_p c;
register IO *iop;
{
register size_t reqcount, /* Total bytes transfered toward request */
xfrsize; /* Current transfer size */
size_t total, /* System global memory total transfer size */
size; /* System global memory current transfer size */
if (!ctbusywait(c, CTFBRD))
return;
reqcount = 0;
while (iop->io_ioc) {
xfrsize = min(c->avail, iop->io_ioc);
if (xfrsize > 0) {
switch (iop->io_seg) {
case IOSYS:
memcpy(iop->io. vbase + reqcount, c->start, xfrsize);
break;
case IOUSR:
kucopy(c->start, iop->io. vbase + reqcount, xfrsize);
break;
case IOPHY:
total = 0;
while (total < xfrsize) {
size = min(xfrsize - total, NBPC);
xpcopy(c->start + total,
iop->io. pbase + reqcount + total,
size,
SEG_386_KD | SEG_VIRT);
total += size;
}
break;
}
c->start += xfrsize;
c->avail -= xfrsize;
reqcount += xfrsize;
iop->io_ioc -= xfrsize;
}
if (iop->io_ioc) {
if (c->flags & CTFILEMARK) {
c->flags &= ~CTFILEMARK;
break;
}
if (c->flags & CTEOM) {
u. u_error = EIO;
break;
}
if (!fillcache(c))
break;
}
} /* while */
c->state = CTIDLE;
} /* ctfbrd() */
/***********************************************************************
* ctvbrd() -- Variable block read entry point.
*/
static void ctvbrd(c, iop)
register ctctrl_p c;
IO *iop;
{
register srb_p r = &(c->srb);
register g0cmd_p g0 = &(r->cdb. g0);
size_t xfrsize;
extsense_p e;
int info;
if (!ctbusywait(c, CTVBRD))
return;
if (c->flags & CTEOM) {
u. u_error = EIO;
return;
}
if (c->flags & CTFILEMARK) {
c->flags &= ~CTFILEMARK;
return;
}
switch (iop->io_seg) {
case IOSYS:
r->buf. space = KRNL_ADDR;
r->buf. addr. caddr = iop->io. vbase;
break;
case IOUSR:
r->buf. space = USER_ADDR;
r->buf. addr. caddr = iop->io. vbase;
break;
case IOPHY:
r->buf. space = PHYS_ADDR;
r->buf. addr. paddr = iop->io. pbase;
break;
}
r->buf. size = xfrsize = iop->io_ioc;
r->xferdir = DMAREAD;
r->timeout = 30;
g0->opcode = READ;
g0->lun_lba = (r->lun << 5);
g0->lba_mid = ((unsigned char *) &xfrsize)[2];
g0->lba_low = ((unsigned char *) &xfrsize)[1];
g0->xfr_len = ((unsigned char *) &xfrsize)[0];
g0->control = 0;
doscsi(r, 1, pritape, slpriSigCatch, "ctvbrd");
switch (r->status) {
case ST_GOOD:
iop->io_ioc -= r->buf. size;
break;
case ST_CHKCOND:
e = r->sensebuf;
if ((e->errorcode & 0x70) == 0x70) {
info = 0;
if (e->errorcode & 0x80) {
info = (long) e->info;
flip(info);
}
if (e->sensekey & (CTFILEMARK | CTEOM)) {
c->flags |= (e->sensekey & (CTFILEMARK | CTEOM));
break;
}
else if (e->sensekey & CTILI) {
devmsg(r->dev,
"Read failed buffer size %d blocksize %d",
xfrsize,
xfrsize - info);
if (info > 0)
iop->io_ioc -= (xfrsize - info);
else
u. u_error = EIO;
break;
}
}
printsense(r->dev, "Read failed", r->sensebuf);
u. u_error = EIO;
break;
case ST_USRABRT:
break;
default:
devmsg(r->dev, "Read failed: status (0x%x)", r->status);
u. u_error = EIO;
break;
}
c->state = CTIDLE;
} /* ctvbrd() */
/***********************************************************************
* ctread() -- OS Read entry point.
*/
static void ctread(dev, iop)
dev_t dev;
register IO *iop;
{
register ctctrl_p c = ctdevs[tid(dev)];
if (!c) {
u. u_error = EINVAL;
return;
}
if (c->block)
ctfbrd(c, iop);
else
ctvbrd(c, iop);
} /* ctread() */
/***********************************************************************
* flushcache() -- flush the data in the cache to the tape.
*
* returns 0 and sets u. u_error on failure else returns 1.
*/
static int flushcache(c)
register ctctrl_p c;
{
register srb_p r = (&c->srb);
register g0cmd_p g0 = &(r->cdb. g0);
size_t xfrsize;
extsense_p e;
int info;
int retval = 0;
if (c->avail >= HAICACHESZ)
return 1;
r->buf. space = KRNL_ADDR;
r->buf. addr. caddr = (caddr_t) c->cache;
r->buf. size = xfrsize = HAICACHESZ - c->avail;
r->xferdir = DMAWRITE;
r->timeout = 30;
r->tries = 0;
g0->opcode = WRITE;
g0->lun_lba = (r->lun << 5);
if (c->block) {
g0->lun_lba |= 1;
xfrsize = (xfrsize + c->block - 1) / c->block;
}
g0->lba_mid = ((unsigned char *) &xfrsize)[2];
g0->lba_low = ((unsigned char *) &xfrsize)[1];
g0->xfr_len = ((unsigned char *) &xfrsize)[0];
g0->control = 0;
doscsi(r, 1, pritape, slpriSigCatch, "ctblkwrt");
switch (r->status) {
case ST_GOOD:
c->start = c->cache;
c->avail = HAICACHESZ;
retval = 1;
break;
case ST_CHKCOND:
e = r->sensebuf;
if ((e->errorcode & 0x70) == 0x70) {
info = 0;
if (e->errorcode & 0x80) {
info = e->info;
flip(info);
}
if (e->sensekey & CTEOM) {
c->flags |= CTEOM;
devmsg(r->dev, "End of tape on block write");
}
}
printsense(r->dev, "Read failed", r->sensebuf);
u. u_error = EIO;
retval = 0;
break;
case ST_USRABRT:
retval = 0;
break;
default:
devmsg(r->dev, "Read failed: status (0x%x)", r->status);
u. u_error = EIO;
retval = 0;
break;
}
return retval;
} /* flushcache() */
/***********************************************************************
* ctfbwrt() -- Fixed block write. This should be fast because
* it uses the tapes drives optimum setting and it
* goes through a cache.
*/
static void ctfbwrt(c, iop)
register ctctrl_p c;
register IO *iop;
{
register size_t reqcount, /* Total bytes transfered */
xfrsize; /* Current transfer size */
size_t total, /* System global memory total transfer size */
size; /* System global memory current transfer size */
if (!ctbusywait(c, CTFBWRT))
return;
reqcount = 0;
while (iop->io_ioc) {
xfrsize = min(c->avail, iop->io_ioc);
if (xfrsize) {
switch (iop->io_seg) {
case IOSYS:
memcpy(c->start, iop->io. vbase + reqcount, xfrsize);
break;
case IOUSR:
ukcopy(iop->io. vbase + reqcount, c->start, xfrsize);
break;
case IOPHY:
total = 0;
while (total < xfrsize) {
size = min(xfrsize - total, NBPC);
pxcopy(iop->io. pbase + reqcount + total,
c->start + total,
size,
SEG_386_KD | SEG_VIRT);
total += size;
}
break;
}
c->start += xfrsize;
c->avail -= xfrsize;
reqcount += xfrsize;
iop->io_ioc -= xfrsize;
}
if (iop->io_ioc) {
if (!flushcache(c))
break;
}
} /* while */
c->state = CTIDLE;
} /* ctfbwrt() */
/***********************************************************************
* ctvbwrt() -- Variable block writes.
*/
static void ctvbwrt(c, iop)
register ctctrl_p c;
register IO *iop;
{
register srb_p r = &(c->srb);
register g0cmd_p g0 = &(r->cdb. g0);
size_t xfrsize;
extsense_p e;
int info;
if (!ctbusywait(c, CTVBWRT))
return;
if (c->blmax && iop->io_ioc > c->blmax) {
devmsg(r->dev, "Tape Error: maximum read/write size is %d bytes.", c->blmax);
u. u_error = EIO;
return;
}
switch (iop->io_seg) {
case IOSYS:
r->buf. space = KRNL_ADDR;
r->buf. addr. caddr = iop->io. vbase;
break;
case IOUSR:
r->buf. space = USER_ADDR;
r->buf. addr. caddr = iop->io. vbase;
break;
case IOPHY:
r->buf. space = PHYS_ADDR;
r->buf. addr. paddr = iop->io. pbase;
break;
}
xfrsize = min(iop->io_ioc, c->blmin);
r->buf. size = xfrsize;
r->xferdir = DMAWRITE;
r->timeout = 30;
g0->opcode = WRITE;
g0->lun_lba = (r->lun << 5);
g0->lba_mid = ((unsigned char *) &xfrsize)[2];
g0->lba_low = ((unsigned char *) &xfrsize)[1];
g0->xfr_len = ((unsigned char *) &xfrsize)[0];
g0->control = 0;
doscsi(r, 1, pritape, slpriSigCatch, "ctvbwrt");
switch (r->status) {
case ST_GOOD:
iop->io_ioc -= r->buf. size;
break;
case ST_CHKCOND:
e = r->sensebuf;
if ((e->errorcode & 0x70) == 0x70) {
info = 0;
if (e->errorcode & 0x80) {
info = (long) e->info;
flip(info);
}
if (e->sensekey & CTEOM) {
c->flags |= CTEOM;
devmsg(r->dev, "End of tape");
}
}
printsense(r->dev, "Write failed", r->sensebuf);
u. u_error = EIO;
break;
case ST_USRABRT:
break;
default:
devmsg(r->dev, "Read failed: status (0x%x)", r->status);
u. u_error = EIO;
break;
}
c->state = CTIDLE;
} /* ctvbwrt() */
/***********************************************************************
* ctwrite() -- Write entry point for tape drive.
*/
static void ctwrite(dev, iop)
register dev_t dev;
register IO *iop;
{
register ctctrl_p c = ctdevs[tid(dev)];
if (!c) {
u. u_error = ENXIO;
return;
}
c->flags |= CTDIRTY;
if (c->block)
ctfbwrt(c, iop);
else
ctvbwrt(c, iop);
} /* ctwrite() */
/***********************************************************************
* ctioctl()
*
* I/O Control Entry point for Cartridge tape devices.
*
* This function had been modified to allow applications level programs
* to select modes and features for the tape drive. As stated above,
* the philosophy of this driver is to provide least common denominator
* support for all tape drives. I know that you spend big bucks to
* get that (insert your favorite drive brand/model). If I decide
* to support everything out there on the market then I won't be able
* to write network drivers, serial drivers, etc. So if you need
* to do something to the tape drive to make it work (mode sense/select)
* you can do it through this ctioctl as an applications program.
*/
static void ctioctl(dev, cmd, vec)
dev_t dev;
register int cmd;
char *vec;
{
register ctctrl_p c = ctdevs[tid(dev)];
register srb_p r = &(c->srb);
int s;
if (!c) {
u. u_error = EINVAL;
return;
}
switch (cmd) {
case MTREWIND: /* Rewind */
rewind(c, 1);
break;
case MTWEOF: /* Write end of file mark */
writefm(c, 1);
break;
case MTRSKIP: /* Record skip */
space(c, 1, SKIP_BLK);
break;
case MTFSKIP: /* File skip */
space(c, 1, SKIP_FM);
break;
case MTTENSE: /* Tension tape */
loadtape(c, RETENSION);
break;
case MTERASE: /* Erase tape */
erase(c, ERASE_TAPE);
break;
case MTDEC: /* DEC mode */
case MTIBM: /* IBM mode */
case MT800: /* 800 bpi */
case MT1600: /* 1600 bpi */
case MT6250: /* 6250 bpi */
return;
default:
if (!ctbusywait(c, CTIOCTL))
return;
s = sphi();
haiioctl(r, cmd, vec);
c->state = CTIDLE;
spl(s);
break;
}
} /* ctioctl() */
/* End of file */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.