* Written by Julian Elischer (julian@tfs.com)(now julian@DIALix.oz.au)
* for TRW Financial Systems for use under the MACH(2.5) operating system.
* TRW Financial Systems, in accordance with their agreement with Carnegie
* Mellon University, makes this software available to CMU to distribute
* or use in any manner that they see fit as long as this message is kept with
* the software. For this reason TFS also grants any other persons or
* organisations permission to use or modify this software.
* TFS supplies this software to be publicly redistributed
* on the understanding that TFS is not responsible for the correct
* functioning of this software in any circumstances.
* PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
* -------------------- ----- ----------------------
* CURRENT PATCH LEVEL: 1 00098
* -------------------- ----- ----------------------
* 16 Feb 93 Julian Elischer ADDED for SCSI system
* 1.15 is the last version to support MACH and OSF/1
* Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
* major changes by Julian Elischer (julian@jules.dialix.oz.au) May 1993
* $Id: st.c,v 1.16 1994/03/23 09:16:04 davidg Exp $
* work out some better way of guessing what a good timeout is going
* to be depending on whether we expect to retension or not.
#include <scsi/scsi_all.h>
#include <scsi/scsi_tape.h>
#include <scsi/scsiconf.h>
u_int32 ststrats
, stqueues
;
/* Defines for device specific stuff */
#define PAGE_0_SENSE_DATA_SIZE 12
#define DEF_FIXED_BSIZE 512
#define ST_RETRIES 4 /* only on non IO commands */
#define MODE(z) ( (minor(z) & 0x03) )
#define DSTY(z) ( ((minor(z) >> 2) & 0x03) )
#define UNIT(z) ( (minor(z) >> 4) )
#define SCSI_2_MAX_DENSITY_CODE 0x17 /* maximum density code specified
* Define various devices that we know mis-behave in some way,
* and note how they are bad, so we can correct for them
u_int32 quirks
; /* same definitions as in rogues */
u_int32 quirks
; /* valid for all modes */
/* define behaviour codes (quirks) */
#define ST_Q_NEEDS_PAGE_0 0x00001
#define ST_Q_FORCE_FIXED_MODE 0x00002
#define ST_Q_FORCE_VAR_MODE 0x00004
#define ST_Q_SNS_HLP 0x00008 /* must do READ for good MODE SENSE */
#define ST_Q_IGNORE_LOADS 0x00010
#define ST_Q_BLKSIZ 0x00020 /* variable-block media_blksiz > 0 */
static struct rogues gallery
[] = /* ends with an all-null entry */
{"Such an old device ", "pre-scsi", " unknown model ", "????",
{512, ST_Q_FORCE_FIXED_MODE
, 0}, /* minor 0,1,2,3 */
{512, ST_Q_FORCE_FIXED_MODE
, QIC_24
}, /* minor 4,5,6,7 */
{0, ST_Q_FORCE_VAR_MODE
, HALFINCH_1600
}, /* minor 8,9,10,11 */
{0, ST_Q_FORCE_VAR_MODE
, HALFINCH_6250
} /* minor 12,13,14,15 */
{"Tandberg tdc3600", "TANDBERG", " TDC 3600", "????",
{0, 0, 0}, /* minor 0,1,2,3 */
{0, ST_Q_FORCE_VAR_MODE
, QIC_525
}, /* minor 4,5,6,7 */
{0, 0, QIC_150
}, /* minor 8,9,10,11 */
{0, 0, QIC_120
} /* minor 12,13,14,15 */
{"Rev 5 of the Archive 2525", "ARCHIVE ", "VIPER 2525 25462", "-005",
{0, ST_Q_SNS_HLP
, 0}, /* minor 0,1,2,3 */
{0, ST_Q_SNS_HLP
, QIC_525
}, /* minor 4,5,6,7 */
{0, 0, QIC_150
}, /* minor 8,9,10,11 */
{0, 0, QIC_120
} /* minor 12,13,14,15 */
{"Archive Viper 150", "ARCHIVE ", "VIPER 150", "????",
{0, 0, 0}, /* minor 0,1,2,3 */
{0, 0, QIC_150
}, /* minor 4,5,6,7 */
{0, 0, QIC_120
}, /* minor 8,9,10,11 */
{0, 0, QIC_24
} /* minor 12,13,14,15 */
{"Wangtek 5525ES", "WANGTEK ", "5525ES SCSI REV7", "????",
{0, 0, 0}, /* minor 0,1,2,3 */
{0, ST_Q_BLKSIZ
, QIC_525
}, /* minor 4,5,6,7 */
{0, 0, QIC_150
}, /* minor 8,9,10,11 */
{0, 0, QIC_120
} /* minor 12,13,14,15 */
{"WangDAT model 1300", "WangDAT ", "Model 1300", "????",
{0, 0, 0}, /* minor 0,1,2,3 */
{512, ST_Q_FORCE_FIXED_MODE
, 0x13}, /* minor 4,5,6,7 */
{1024, ST_Q_FORCE_FIXED_MODE
, 0x13}, /* minor 8,9,10,11 */
{0, ST_Q_FORCE_VAR_MODE
, 0x13} /* minor 12,13,14,15 */
errval st_space
__P((u_int32 unit
, int32 number
, u_int32 what
, u_int32 flags
));
errval st_rewind
__P((u_int32 unit
, boolean immed
, u_int32 flags
));
errval st_mode_sense
__P((u_int32 unit
, u_int32 flags
));
errval st_decide_mode
__P((u_int32 unit
, boolean first_read
));
errval st_rd_blk_lim
__P((u_int32 unit
, u_int32 flags
));
errval st_touch_tape
__P((u_int32 unit
));
errval st_write_filemarks
__P((u_int32 unit
, int32 number
, u_int32 flags
));
errval st_load
__P((u_int32 unit
, u_int32 type
, u_int32 flags
));
errval st_mode_select
__P((u_int32 unit
, u_int32 flags
));
void st_identify_drive();
errval
st_interpret_sense();
struct scsi_device st_switch
=
st_interpret_sense
, /* check errors with us first */
ststart
, /* we have a queue, and this is how we service it */
NULL
, /* use the default 'done' routine */
/*--------------------present operating parameters, flags etc.----------------*/
u_int32 flags
; /* see below */
u_int32 blksiz
; /* blksiz we are using */
u_int32 density
; /* present density */
u_int32 quirks
; /* quirks for the open mode */
u_int32 last_dsty
; /* last density openned */
/*--------------------device/scsi parameters----------------------------------*/
struct scsi_link
*sc_link
; /* our link to the adpter etc. */
/*--------------------parameters reported by the device ----------------------*/
u_int32 blkmin
; /* min blk size */
u_int32 blkmax
; /* max blk size */
struct rogues
*rogues
; /* if we have a rogue entry */
/*--------------------parameters reported by the device for this media--------*/
u_int32 numblks
; /* nominal blocks capacity */
u_int32 media_blksiz
; /* 0 if not ST_FIXEDBLOCKS */
u_int32 media_density
; /* this is what it said when asked */
/*--------------------quirks for the whole drive------------------------------*/
u_int32 drive_quirks
; /* quirks of this drive */
/*--------------------How we should set up when openning each minor device----*/
struct modes modes
[4]; /* plus more for each mode */
u_int8 modeflags
[4]; /* flags for the modes */
#define DENSITY_SET_BY_USER 0x01
#define DENSITY_SET_BY_QUIRK 0x02
#define BLKSIZE_SET_BY_USER 0x04
#define BLKSIZE_SET_BY_QUIRK 0x08
/*--------------------storage for sense data returned by the drive------------*/
unsigned char sense_data
[12]; /*
* additional sense data needed
struct buf
*buf_queue
; /* the queue of pending IO operations */
struct scsi_xfer scsi_xfer
; /* scsi xfer struct for this drive */
u_int32 xfer_block_wait
; /* is a process waiting? */
#define ST_INITIALIZED 0x01
#define ST_INFO_VALID 0x02
#define ST_BLOCK_SET 0x08 /* block size, mode set by ioctl */
#define ST_WRITTEN 0x10 /* data have been written, EOD needed */
#define ST_FIXEDBLOCKS 0x20
#define ST_AT_FILEMARK 0x40
#define ST_EIO_PENDING 0x80 /* we couldn't report it then (had data) */
#define ST_NEW_MOUNT 0x100 /* still need to decide mode */
#define ST_READONLY 0x200 /* st_mode_sense says write protected */
#define ST_FM_WRITTEN 0x400 /*
* EOF file mark written -- used with
* ~ST_WRITTEN to indicate that multiple file
* marks have been written
#define ST_BLANK_READ 0x800 /* BLANK CHECK encountered already */
#define ST_2FM_AT_EOD 0x1000 /* write 2 file marks at EOD */
#define ST_MOUNTED 0x2000 /* Device is presently mounted */
#define ST_PER_ACTION (ST_AT_FILEMARK | ST_EIO_PENDING | ST_BLANK_READ)
#define ST_PER_MOUNT (ST_INFO_VALID | ST_BLOCK_SET | ST_WRITTEN | \
ST_FIXEDBLOCKS | ST_READONLY | \
ST_FM_WRITTEN | ST_2FM_AT_EOD | ST_PER_ACTION)
static u_int32 next_st_unit
= 0;
* The routine called by the low level scsi routine when it discovers
* A device suitable for this driver
struct scsi_link
*sc_link
;
SC_DEBUG(sc_link
, SDEV_DB2
, ("stattach: "));
* Check we have the resources for another drive
printf("Too many scsi tapes..(%d > %d) reconfigure kernel\n",
printf("st%d: Already has storage!\n", unit
);
sc_link
->device
= &st_switch
;
sc_link
->dev_unit
= unit
;
st
= st_data
[unit
] = malloc(sizeof(struct st_data
), M_DEVBUF
, M_NOWAIT
);
printf("st%d: malloc failed in st.c\n", unit
);
bzero(st
, sizeof(struct st_data
));
* Store information needed to contact our base driver
* Check if the drive is a known criminal and take
* Any steps needed to bring it into line
* Use the subdriver to request information regarding
* the drive. We cannot use interrupts yet, so the
* request must specify this.
st_rd_blk_lim(unit
, SCSI_NOSLEEP
| SCSI_NOMASK
| SCSI_SILENT
);
if (st_mode_sense(unit
, SCSI_NOSLEEP
| SCSI_NOMASK
| SCSI_SILENT
)) {
printf("st%d: drive offline\n", unit
);
printf("st%d: density code 0x%x, ", unit
, st
->media_density
);
if (!scsi_test_unit_ready(sc_link
, SCSI_NOSLEEP
| SCSI_NOMASK
| SCSI_SILENT
)) {
printf("%d-byte", st
->media_blksiz
);
printf("variable(%d->%d)",st
->blkmin
,st
->blkmax
);
printf(" blocks, write-%s\n",
(st
->flags
& ST_READONLY
) ? "protected" : "enabled");
printf(" drive empty\n");
* Forget if we've loaded the media,
* because sometimes things are unstable at boot time.
* We'll get it all again at the first open.
sc_link
->flags
&= ~SDEV_MEDIA_LOADED
;
* Set up the buf queue for this device
st
->flags
|= ST_INITIALIZED
;
* Use the inquiry routine in 'scsi_base' to get drive info so we can
* Further tailor our behaviour.
struct st_data
*st
= st_data
[unit
];
struct scsi_inquiry_data inqbuf
;
* Get the device type information
if (scsi_inquire(st
->sc_link
, &inqbuf
,
SCSI_NOSLEEP
| SCSI_NOMASK
| SCSI_SILENT
) != 0) {
printf("st%d: couldn't get device type, using default\n", unit
);
if ((inqbuf
.version
& SID_ANSII
) == 0) {
* If not advanced enough, use default values
strncpy(manu
, "pre-scsi", 8);
strncpy(model
, " unknown model ", 16);
strncpy(version
, "????", 4);
strncpy(manu
, inqbuf
.vendor
, 8);
strncpy(model
, inqbuf
.product
, 16);
strncpy(version
, inqbuf
.revision
, 4);
* Load the parameters for this kind of device, so we
* treat it as appropriate for each operating mode.
* Only check the number of characters in the array's
* model entry, not the entire model string returned.
while (finger
->model
[model_len
] && (model_len
< 32)) {
model2
[model_len
] = model
[model_len
];
if ((strcmp(manu
, finger
->manu
) == 0)
&& (strcmp(model2
, finger
->model
) == 0 ||
strcmp("????????????????", finger
->model
) == 0)
&& (strcmp(version
, finger
->version
) == 0 ||
strcmp("????", finger
->version
) == 0)) {
printf("st%d: %s is a known rogue\n", unit
, finger
->name
);
st
->drive_quirks
= finger
->quirks
;
st
->quirks
= finger
->quirks
; /*start value */
finger
++; /* go to next suspect */
* initialise the subdevices to the default (QUIRK) state.
* this will remove any setting made by the system operator or previous
mode
= st
->rogues
->modes
;
for (i
= 0; i
< 4; i
++) {
bzero(mode2
, sizeof(struct modes
));
st
->modeflags
[i
] &= ~(BLKSIZE_SET_BY_QUIRK
if (mode
->blksiz
&& ((mode
->quirks
| st
->drive_quirks
)
& (ST_Q_FORCE_FIXED_MODE
))) {
mode2
->blksiz
= mode
->blksiz
;
st
->modeflags
[i
] |= BLKSIZE_SET_BY_QUIRK
;
if ((mode
->quirks
| st
->drive_quirks
)
st
->modeflags
[i
] |= BLKSIZE_SET_BY_QUIRK
;
mode2
->density
= mode
->density
;
st
->modeflags
[i
] |= DENSITY_SET_BY_QUIRK
;
u_int32 unit
, mode
, dsty
;
struct scsi_link
*sc_link
;
* Check the unit is legal
* Make sure the device has been initialised
if ((st
== NULL
) || (!(st
->flags
& ST_INITIALIZED
)))
SC_DEBUG(sc_link
, SDEV_DB1
, ("open: dev=0x%x (unit %d (of %d))\n"
* Only allow one at a time
if (st
->flags
& ST_OPEN
) {
* Throw out a dummy instruction to catch 'Unit attention
* errors (the error handling will invalidate all our
* device info if we get one, but otherwise, ignore it)
scsi_test_unit_ready(sc_link
, SCSI_SILENT
);
sc_link
->flags
|= SDEV_OPEN
; /* unit attn are now errors */
* If the mode is 3 (e.g. minor = 3,7,11,15)
* then the device has been openned to set defaults
* This mode does NOT ALLOW I/O, only ioctls
* Check that the device is ready to use (media loaded?)
* This time take notice of the return result
if (errno
= (scsi_test_unit_ready(sc_link
, 0))) {
printf("st%d: not ready\n", unit
);
st_unmount(unit
, NOEJECT
);
* if it's a different mode, or if the media has been
* invalidated, unmount the tape from the previous
* session but continue with open processing
if ((st
->last_dsty
!= dsty
)
|| (!(sc_link
->flags
& SDEV_MEDIA_LOADED
))) {
st_unmount(unit
, NOEJECT
);
* If we are not mounted, then we should start a new
if (!(st
->flags
& ST_MOUNTED
)) {
st_mount_tape(dev
, flags
);
* Make sure that a tape opened in write-only mode will have
* file marks written on it when closed, even if not written to.
* This is for SUN compatibility
if ((flags
& O_ACCMODE
) == FWRITE
)
SC_DEBUG(sc_link
, SDEV_DB2
, ("Open complete\n"));
* close the device.. only called if we are the LAST
* occurence of an open device
unsigned char unit
, mode
;
struct scsi_link
*sc_link
;
SC_DEBUG(sc_link
, SDEV_DB1
, ("closing\n"));
if ((st
->flags
& (ST_WRITTEN
| ST_FM_WRITTEN
)) == ST_WRITTEN
)
st_write_filemarks(unit
, 1, 0);
st_unmount(unit
, NOEJECT
);
case 1: /*leave mounted unless media seems to have been removed */
if (!(sc_link
->flags
& SDEV_MEDIA_LOADED
)) {
st_unmount(unit
, NOEJECT
);
sc_link
->flags
&= ~SDEV_OPEN
;
* Start a new mount session.
* Copy in all the default parameters from the selected device mode.
* and try guess any that seem to be defaulted.
st_mount_tape(dev
, flags
)
u_int32 unit
, mode
, dsty
;
struct scsi_link
*sc_link
;
if (st
->flags
& ST_MOUNTED
)
SC_DEBUG(sc_link
, SDEV_DB1
, ("mounting\n "));
st
->flags
|= ST_NEW_MOUNT
;
st
->quirks
= st
->drive_quirks
| st
->modes
[dsty
].quirks
;
* If the media is new, then make sure we give it a chance to
* to do a 'load' instruction. ( We assume it is new)
if (errno
= st_load(unit
, LD_LOAD
, 0)) {
* Throw another dummy instruction to catch
* 'Unit attention' errors. Some drives appear to give
* these after doing a Load instruction.
* (noteably some DAT drives)
scsi_test_unit_ready(sc_link
, SCSI_SILENT
);
* Some devices can't tell you much until they have been
* asked to look at the media. This quirk does this.
if (st
->quirks
& ST_Q_SNS_HLP
) {
if (errno
= st_touch_tape(unit
))
* Load the physical device parameters
if (errno
= st_rd_blk_lim(unit
, 0)) {
* Load the media dependent parameters
* includes: media_blksiz,media_density,numblks
* As we have a tape in, it should be reflected here.
* If not you may need the "quirk" above.
if (errno
= st_mode_sense(unit
, 0)) {
* If we have gained a permanent density from somewhere,
* then use it in preference to the one supplied by
if (st
->modeflags
[dsty
] & (DENSITY_SET_BY_QUIRK
| DENSITY_SET_BY_USER
)) {
st
->density
= st
->modes
[dsty
].density
;
st
->density
= st
->media_density
;
* If we have gained a permanent blocksize
* then use it in preference to the one supplied by
st
->flags
&= ~ST_FIXEDBLOCKS
;
if (st
->modeflags
[dsty
] & (BLKSIZE_SET_BY_QUIRK
| BLKSIZE_SET_BY_USER
)) {
st
->blksiz
= st
->modes
[dsty
].blksiz
;
st
->flags
|= ST_FIXEDBLOCKS
;
if (errno
= st_decide_mode(unit
, FALSE
)) {
if (errno
= st_mode_select(unit
, 0)) {
printf("st%d: Cannot set selected mode", unit
);
scsi_prevent(sc_link
, PR_PREVENT
, 0); /* who cares if it fails? */
st
->flags
&= ~ST_NEW_MOUNT
;
sc_link
->flags
|= SDEV_MEDIA_LOADED
; /* move earlier? */
* End the present mount session.
* Rewind, and optionally eject the tape.
* Reset various flags to indicate that all new
* operations require another mount operation
st_unmount(int unit
, boolean eject
)
struct st_data
*st
= st_data
[unit
];
struct scsi_link
*sc_link
= st
->sc_link
;
if (!(st
->flags
& ST_MOUNTED
))
SC_DEBUG(sc_link
, SDEV_DB1
, ("unmounting\n"));
st_chkeod(unit
, FALSE
, &nmarks
, SCSI_SILENT
);
st_rewind(unit
, FALSE
, SCSI_SILENT
);
scsi_prevent(sc_link
, PR_ALLOW
, SCSI_SILENT
);
st_load(unit
, LD_UNLOAD
, SCSI_SILENT
);
st
->flags
&= ~(ST_MOUNTED
| ST_NEW_MOUNT
);
sc_link
->flags
&= ~SDEV_MEDIA_LOADED
;
* Given all we know about the device, media, mode, 'quirks' and
* initial operation, make a decision as to how we should be set
* to run (regarding blocking and EOD marks)
st_decide_mode(unit
, first_read
)
struct st_data
*st
= st_data
[unit
];
struct scsi_link
*sc_link
= st
->sc_link
;
SC_DEBUG(sc_link
, SDEV_DB2
, ("starting block mode decision\n"));
* If the user hasn't already specified fixed or variable-length
* blocks and the block size (zero if variable-length), we'll
* have to try to figure them out ourselves.
* Our first shot at a method is, "The quirks made me do it!"
switch ((int)(st
->quirks
& (ST_Q_FORCE_FIXED_MODE
| ST_Q_FORCE_VAR_MODE
))) {
case (ST_Q_FORCE_FIXED_MODE
| ST_Q_FORCE_VAR_MODE
):
printf("st%d: bad quirks\n", unit
);
case ST_Q_FORCE_FIXED_MODE
: /*specified fixed, but not what size */
st
->flags
|= ST_FIXEDBLOCKS
;
if (st
->blkmin
&& (st
->blkmin
== st
->blkmax
))
else if (st
->media_blksiz
> 0)
st
->blksiz
= st
->media_blksiz
;
st
->blksiz
= DEF_FIXED_BSIZE
;
SC_DEBUG(sc_link
, SDEV_DB3
, ("Quirks force fixed mode(%d)\n",
case ST_Q_FORCE_VAR_MODE
:
st
->flags
&= ~ST_FIXEDBLOCKS
;
SC_DEBUG(sc_link
, SDEV_DB3
, ("Quirks force variable mode\n"));
* If the drive can only handle fixed-length blocks and only at
* one size, perhaps we should just do that.
if (st
->blkmin
&& (st
->blkmin
== st
->blkmax
)) {
st
->flags
|= ST_FIXEDBLOCKS
;
SC_DEBUG(sc_link
, SDEV_DB3
,
("blkmin == blkmax of %d\n", st
->blkmin
));
* If the tape density mandates (or even suggests) use of fixed
* or variable-length blocks, comply.
switch ((int)st
->density
) {
st
->flags
&= ~ST_FIXEDBLOCKS
;
SC_DEBUG(sc_link
, SDEV_DB3
, ("density specified variable\n"));
st
->flags
|= ST_FIXEDBLOCKS
;
if (st
->media_blksiz
> 0) {
st
->blksiz
= st
->media_blksiz
;
st
->blksiz
= DEF_FIXED_BSIZE
;
SC_DEBUG(sc_link
, SDEV_DB3
, ("density specified fixed\n"));
* If we're about to read the tape, perhaps we should choose
* fixed or variable-length blocks and block size according to
* what the drive found on the tape.
&& (!(st
->quirks
& ST_Q_BLKSIZ
)
|| (st
->media_blksiz
== 0)
|| (st
->media_blksiz
== DEF_FIXED_BSIZE
)
|| (st
->media_blksiz
== 1024))) {
if (st
->media_blksiz
== 0) {
st
->flags
&= ~ST_FIXEDBLOCKS
;
st
->flags
|= ST_FIXEDBLOCKS
;
st
->blksiz
= st
->media_blksiz
;
SC_DEBUG(sc_link
, SDEV_DB3
,
("Used media_blksiz of %d\n", st
->media_blksiz
));
* We're getting no hints from any direction. Choose variable-
* length blocks arbitrarily.
st
->flags
&= ~ST_FIXEDBLOCKS
;
SC_DEBUG(sc_link
, SDEV_DB3
, ("Give up and default to variable mode\n"));
* Decide whether or not to write two file marks to signify end-
* of-data. Make the decision as a function of density. If
* the decision is not to use a second file mark, the SCSI BLANK
* CHECK condition code will be recognized as end-of-data when
* (I think this should be a by-product of fixed/variable..julian)
switch ((int)st
->density
) {
/* case 8 mm: What is the SCSI density code for 8 mm, anyway? */
st
->flags
&= ~ST_2FM_AT_EOD
;
st
->flags
|= ST_2FM_AT_EOD
;
* trim the size of the transfer if needed,
* basically the smaller of our min and the scsi driver's
(*(st_data
[UNIT(bp
->b_dev
)]->sc_link
->adapter
->scsi_minphys
)) (bp
);
* Actually translate the requested transfer into
* one the physical driver can understand
* The transfer is described by a buf and will include
* only one physical transfer.
unit
= UNIT((bp
->b_dev
));
SC_DEBUG(st
->sc_link
, SDEV_DB1
,
(" strategy: %d bytes @ blk%d\n", bp
->b_bcount
, bp
->b_blkno
));
* If it's a null transfer, return immediatly
* Odd sized request on fixed drives are verboten
if (st
->flags
& ST_FIXEDBLOCKS
) {
if (bp
->b_bcount
% st
->blksiz
) {
printf("st%d: bad request, must be multiple of %d\n",
* as are out-of-range requests on variable drives.
else if (bp
->b_bcount
< st
->blkmin
|| bp
->b_bcount
> st
->blkmax
) {
printf("st%d: bad request, must be between %d and %d\n",
unit
, st
->blkmin
, st
->blkmax
);
* Use a bounce buffer if necessary
if (st
->sc_link
->flags
& SDEV_BOUNCE
)
* Place it in the queue of activities for this tape
* at the end (a bit silly because we only have on user..
* (but it could fork() ))
* Tell the device to get going on the transfer if it's
* not doing anything, otherwise just wait for completion
* (All a bit silly if we're only allowing 1 open but..)
* Correctly set the buf to indicate a completed xfer
* ststart looks to see if there is a buf waiting for the device
* and that the device is not already busy. If both are true,
* It dequeues the buf and creates a scsi command to perform the
* transfer required. The transfer request will call scsi_done
* on completion, which will in turn call this routine again
* so that the next queued transfer is performed.
* The bufs are queued by the strategy routine (ststrategy)
* This routine is also called after other non-queued requests
* have been made of the scsi driver, to ensure that the queue
* continues to be drained.
* ststart() is called at splbio
struct st_data
*st
= st_data
[unit
];
struct scsi_link
*sc_link
= st
->sc_link
;
register struct buf
*bp
= 0;
SC_DEBUG(sc_link
, SDEV_DB2
, ("ststart "));
* See if there is a buf to do and we are not already
while (sc_link
->opennings
!= 0) {
/* if a special awaits, let it proceed first */
if (sc_link
->flags
& SDEV_WAITING
) {
sc_link
->flags
&= ~SDEV_WAITING
;
wakeup((caddr_t
)sc_link
);
if ((bp
= st
->buf_queue
) == NULL
) {
return; /* no work to bother with */
st
->buf_queue
= bp
->b_actf
;
* if the device has been unmounted byt the user
* then throw away all requests until done
if ((!(st
->flags
& ST_MOUNTED
))
|| (!(sc_link
->flags
& SDEV_MEDIA_LOADED
))) {
/* make sure that one implies the other.. */
sc_link
->flags
&= ~SDEV_MEDIA_LOADED
;
* only FIXEDBLOCK devices have pending operations
if (st
->flags
& ST_FIXEDBLOCKS
) {
* If we are at a filemark but have not reported it yet
* then we should report it now
if (st
->flags
& ST_AT_FILEMARK
) {
if ((bp
->b_flags
& B_READ
) == B_WRITE
) {
* Handling of ST_AT_FILEMARK in
* st_space will fill in the right file
if (st_space(unit
, 0, SP_FILEMARKS
, 0) !=
bp
->b_resid
= bp
->b_bcount
;
st
->flags
&= ~ST_AT_FILEMARK
;
continue; /* seek more work */
* If we are at EIO (e.g. EOM) but have not reported it
* yet then we should report it now
if (st
->flags
& ST_EIO_PENDING
) {
bp
->b_resid
= bp
->b_bcount
;
st
->flags
&= ~ST_EIO_PENDING
;
continue; /* seek more work */
* Fill out the scsi command
bzero(&cmd
, sizeof(cmd
));
if ((bp
->b_flags
& B_READ
) == B_WRITE
) {
cmd
.op_code
= WRITE_COMMAND_TAPE
;
st
->flags
&= ~ST_FM_WRITTEN
;
cmd
.op_code
= READ_COMMAND_TAPE
;
* Handle "fixed-block-mode" tape drives by using the
* block count instead of the length.
if (st
->flags
& ST_FIXEDBLOCKS
) {
lto3b(bp
->b_bcount
/ st
->blksiz
, cmd
.len
);
lto3b(bp
->b_bcount
, cmd
.len
);
* go ask the adapter to do all this for us
if (scsi_scsi_cmd(sc_link
,
(struct scsi_generic
*) &cmd
,
(u_char
*) bp
->b_un
.b_addr
,
0, /* can't retry a read on a tape really */
flags
| SCSI_NOSLEEP
) == SUCCESSFULLY_QUEUED
) {
printf("st%d: oops not queued\n", unit
);
} /* go back and see if we can cram more work in.. */
* Perform special action on behalf of the user;
* knows about the internals of this device
stioctl(dev
, cmd
, arg
, flag
)
u_int32 number
, flags
, dsty
;
struct mtop
*mt
= (struct mtop
*) arg
;
* Find the device that the user is talking about
flags
= 0; /* give error messages, act on errors etc. */
hold_blksiz
= st
->blksiz
;
hold_density
= st
->density
;
struct mtget
*g
= (struct mtget
*) arg
;
SC_DEBUG(st
->sc_link
, SDEV_DB1
, ("[ioctl: get status]\n"));
bzero(g
, sizeof(struct mtget
));
g
->mt_type
= 0x7; /* Ultrix compat *//*? */
g
->mt_density
= st
->density
;
g
->mt_blksiz
= st
->blksiz
;
g
->mt_density0
= st
->modes
[0].density
;
g
->mt_density1
= st
->modes
[1].density
;
g
->mt_density2
= st
->modes
[2].density
;
g
->mt_density3
= st
->modes
[3].density
;
g
->mt_blksiz0
= st
->modes
[0].blksiz
;
g
->mt_blksiz1
= st
->modes
[1].blksiz
;
g
->mt_blksiz2
= st
->modes
[2].blksiz
;
g
->mt_blksiz3
= st
->modes
[3].blksiz
;
SC_DEBUG(st
->sc_link
, SDEV_DB1
, ("[ioctl: op=0x%x count=0x%x]\n",
mt
->mt_op
, mt
->mt_count
));
/* compat: in U*x it is a short */
switch ((short) (mt
->mt_op
)) {
case MTWEOF
: /* write an end-of-file record */
errcode
= st_write_filemarks(unit
, number
, flags
);
case MTBSF
: /* backward space file */
case MTFSF
: /* forward space file */
errcode
= st_chkeod(unit
, FALSE
, &nmarks
, flags
);
errcode
= st_space(unit
, number
- nmarks
,
case MTBSR
: /* backward space record */
case MTFSR
: /* forward space record */
errcode
= st_chkeod(unit
, TRUE
, &nmarks
, flags
);
errcode
= st_space(unit
, number
, SP_BLKS
, flags
);
errcode
= st_rewind(unit
, FALSE
, flags
);
case MTOFFL
: /* rewind and put the drive offline */
case MTNOP
: /* no operation, sets status only */
case MTCACHE
: /* enable controller cache */
case MTNOCACHE
: /* disable controller cache */
case MTSETBSIZ
: /* Set block size for device */
if (!(st
->flags
& ST_NEW_MOUNT
)) {
uprintf("re-mount tape before changing blocksize");
st
->flags
&= ~ST_FIXEDBLOCKS
;
if ((st
->blkmin
|| st
->blkmax
) /* they exist */
|| number
> st
->blkmax
))) {
st
->flags
|= ST_FIXEDBLOCKS
;
st
->flags
|= ST_BLOCK_SET
; /*XXX */
case MTSETDNSTY
: /* Set density for device and mode */
if (number
> SCSI_2_MAX_DENSITY_CODE
) {
errcode
= scsi_do_ioctl(st
->sc_link
,cmd
,arg
,flag
);
/*-----------------------------*/
* Check that the mode being asked for is aggreeable to the
* drive. If not, put it back the way it was.
if (errcode
= st_mode_select(unit
, 0)) { /* put it back as it was */
printf("st%d: Cannot set selected mode", unit
);
st
->density
= hold_density
;
st
->blksiz
= hold_blksiz
;
st
->flags
|= ST_FIXEDBLOCKS
;
st
->flags
&= ~ST_FIXEDBLOCKS
;
* As the drive liked it, if we are setting a new default,
* set it into the structures as such.
* The means for deciding this are not finalised yet
switch ((short) (mt
->mt_op
)) {
st
->modes
[dsty
].blksiz
= st
->blksiz
;
st
->modeflags
[dsty
] |= BLKSIZE_SET_BY_USER
;
st
->modes
[dsty
].density
= st
->density
;
st
->modeflags
[dsty
] |= DENSITY_SET_BY_USER
;
st_read(unit
, buf
, size
, flags
)
u_int32 unit
, size
, flags
;
struct scsi_rw_tape scsi_cmd
;
struct st_data
*st
= st_data
[unit
];
* If it's a null transfer, return immediatly
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= READ_COMMAND_TAPE
;
if (st
->flags
& ST_FIXEDBLOCKS
) {
scsi_cmd
.byte2
|= SRWT_FIXED
;
lto3b(size
/ (st
->blksiz
? st
->blksiz
: DEF_FIXED_BSIZE
),
lto3b(size
, scsi_cmd
.len
);
return (scsi_scsi_cmd(st
->sc_link
,
(struct scsi_generic
*) &scsi_cmd
,
0, /* not on io commands */
#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 )
* Ask the drive what it's min and max blk sizes are.
st_rd_blk_lim(unit
, flags
)
struct scsi_blk_limits scsi_cmd
;
struct scsi_blk_limits_data scsi_blkl
;
struct st_data
*st
= st_data
[unit
];
struct scsi_link
*sc_link
= st
->sc_link
;
* First check if we have it all loaded
if ((sc_link
->flags
& SDEV_MEDIA_LOADED
))
* do a 'Read Block Limits'
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= READ_BLK_LIMITS
;
* do the command, update the global values
if (errno
= scsi_scsi_cmd(sc_link
,
(struct scsi_generic
*) &scsi_cmd
,
st
->blkmin
= b2tol(scsi_blkl
.min_length
);
st
->blkmax
= _3btol(&scsi_blkl
.max_length_2
);
SC_DEBUG(sc_link
, SDEV_DB3
,
("(%d <= blksiz <= %d)\n", st
->blkmin
, st
->blkmax
));
* Get the scsi driver to send a full inquiry to the
* device and use the results to fill out the global
* ioctl (to reset original blksize)
st_mode_sense(unit
, flags
)
struct scsi_mode_sense scsi_cmd
;
struct scsi_mode_header header
;
struct blk_desc blk_desc
;
struct scsi_sense_page_0
{
struct scsi_mode_header header
;
struct blk_desc blk_desc
;
unsigned char sense_data
[PAGE_0_SENSE_DATA_SIZE
];
/* Tandberg tape drives returns page 00
* with the sense data, whether or not
* you want it( ie the don't like you
* saying you want anything less!!!!!
* They also expect page 00
* back when you issue a mode select
struct st_data
*st
= st_data
[unit
];
struct scsi_link
*sc_link
= st
->sc_link
;
* Define what sort of structure we're working with
if (st
->quirks
& ST_Q_NEEDS_PAGE_0
) {
scsi_sense_len
= sizeof(scsi_sense_page_0
);
scsi_sense_ptr
= (char *) &scsi_sense_page_0
;
scsi_sense_len
= sizeof(scsi_sense
);
scsi_sense_ptr
= (char *) &scsi_sense
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= MODE_SENSE
;
scsi_cmd
.length
= scsi_sense_len
;
* do the command, but we don't need the results
* just print them for our interest's sake, if asked,
* or if we need it as a template for the mode select
if (errno
= scsi_scsi_cmd(sc_link
,
(struct scsi_generic
*) &scsi_cmd
,
(u_char
*) scsi_sense_ptr
,
st
->numblks
= _3btol(((struct scsi_sense
*)scsi_sense_ptr
)->blk_desc
.nblocks
);
st
->media_blksiz
= _3btol(((struct scsi_sense
*)scsi_sense_ptr
)->blk_desc
.blklen
);
st
->media_density
= ((struct scsi_sense
*) scsi_sense_ptr
)->blk_desc
.density
;
if (((struct scsi_sense
*) scsi_sense_ptr
)->header
.dev_spec
&
st
->flags
|= ST_READONLY
;
SC_DEBUG(sc_link
, SDEV_DB3
,
("density code 0x%x, %d-byte blocks, write-%s, ",
st
->media_density
, st
->media_blksiz
,
st
->flags
& ST_READONLY
? "protected" : "enabled"));
SC_DEBUG(sc_link
, SDEV_DB3
,
((struct scsi_sense
*) scsi_sense_ptr
)->header
.dev_spec
& SMH_DSP_BUFF_MODE
? "" : "un"));
if (st
->quirks
& ST_Q_NEEDS_PAGE_0
) {
bcopy(((struct scsi_sense_page_0
*) scsi_sense_ptr
)->sense_data
,
sizeof(((struct scsi_sense_page_0
*) scsi_sense_ptr
)->sense_data
));
sc_link
->flags
|= SDEV_MEDIA_LOADED
;
* Send a filled out parameter structure to the drive to
* set it into the desire modes etc.
st_mode_select(unit
, flags
)
struct scsi_mode_select scsi_cmd
;
struct scsi_mode_header header
;
struct blk_desc blk_desc
;
struct scsi_mode_header header
;
struct blk_desc blk_desc
;
unsigned char sense_data
[PAGE_0_SENSE_DATA_SIZE
];
struct st_data
*st
= st_data
[unit
];
* Define what sort of structure we're working with
if (st
->quirks
& ST_Q_NEEDS_PAGE_0
) {
dat_len
= sizeof(dat_page_0
);
dat_ptr
= (char *) &dat_page_0
;
* Set up for a mode select
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= MODE_SELECT
;
scsi_cmd
.length
= dat_len
;
((struct dat
*) dat_ptr
)->header
.blk_desc_len
= sizeof(struct blk_desc
);
((struct dat
*) dat_ptr
)->header
.dev_spec
|= SMH_DSP_BUFF_MODE_ON
;
((struct dat
*) dat_ptr
)->blk_desc
.density
= st
->density
;
if (st
->flags
& ST_FIXEDBLOCKS
) {
lto3b(st
->blksiz
, ((struct dat
*) dat_ptr
)->blk_desc
.blklen
);
if (st
->quirks
& ST_Q_NEEDS_PAGE_0
) {
bcopy(st
->sense_data
, ((struct dat_page_0
*) dat_ptr
)->sense_data
,
sizeof(((struct dat_page_0
*) dat_ptr
)->sense_data
));
/* the Tandberg tapes need the block size to */
/* be set on each mode sense/select. */
return (scsi_scsi_cmd(st
->sc_link
,
(struct scsi_generic
*) &scsi_cmd
,
* skip N blocks/filemarks/seq filemarks/eom
st_space(unit
, number
, what
, flags
)
u_int32 unit
, what
, flags
;
struct scsi_space scsi_cmd
;
struct st_data
*st
= st_data
[unit
];
if (st
->flags
& ST_PER_ACTION
) {
st
->flags
&= ~ST_PER_ACTION
;
if (st
->flags
& ST_AT_FILEMARK
) {
* Handling of ST_AT_FILEMARK
* in st_space will fill in the
error
= st_space(unit
, 0, SP_FILEMARKS
,
if (st
->flags
& ST_BLANK_READ
) {
st
->flags
&= ~ST_BLANK_READ
;
st
->flags
&= ~ST_EIO_PENDING
;
if (st
->flags
& ST_EIO_PENDING
) {
if (number
> 0) { /* pretend we just discover the error */
st
->flags
&= ~ST_EIO_PENDING
;
} else if (number
< 0) { /* back away from the error */
st
->flags
&= ~ST_EIO_PENDING
;
if (st
->flags
& ST_AT_FILEMARK
) {
st
->flags
&= ~ST_AT_FILEMARK
;
if ((st
->flags
& ST_BLANK_READ
) && (number
< 0)) { /* back away from unwritten tape */
st
->flags
&= ~ST_BLANK_READ
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= SPACE
;
scsi_cmd
.byte2
= what
& SS_CODE
;
lto3b(number
, scsi_cmd
.number
);
return (scsi_scsi_cmd(st
->sc_link
,
(struct scsi_generic
*) &scsi_cmd
,
0, /* no retries please , just fail */
600000, /* 10 mins enough? */
st_write_filemarks(unit
, number
, flags
)
struct scsi_write_filemarks scsi_cmd
;
struct st_data
*st
= st_data
[unit
];
* It's hard to write a negative number of file marks.
case 0: /* really a command to sync the drive's buffers */
if (st
->flags
& ST_FM_WRITTEN
) { /* already have one down */
st
->flags
&= ~ST_WRITTEN
;
st
->flags
|= ST_FM_WRITTEN
;
st
->flags
&= ~ST_PER_ACTION
;
st
->flags
&= ~(ST_PER_ACTION
| ST_WRITTEN
);
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= WRITE_FILEMARKS
;
lto3b(number
, scsi_cmd
.number
);
return scsi_scsi_cmd(st
->sc_link
,
(struct scsi_generic
*) &scsi_cmd
,
0, /* no retries, just fail */
100000, /* 10 secs.. (may need to repos head ) */
* Make sure the right number of file marks is on tape if the
* tape has been written. If the position argument is true,
* leave the tape positioned where it was originally.
* nmarks returns the number of marks to skip (or, if position
* true, which were skipped) to get back original position.
st_chkeod(unit
, position
, nmarks
, flags
)
struct st_data
*st
= st_data
[unit
];
switch ((int)(st
->flags
& (ST_WRITTEN
| ST_FM_WRITTEN
| ST_2FM_AT_EOD
))) {
case ST_WRITTEN
| ST_FM_WRITTEN
| ST_2FM_AT_EOD
:
case ST_WRITTEN
| ST_2FM_AT_EOD
:
error
= st_write_filemarks(unit
, *nmarks
, flags
);
if (position
&& (error
== ESUCCESS
))
error
= st_space(unit
, -*nmarks
, SP_FILEMARKS
, flags
);
* load/unload (with retension if true)
st_load(unit
, type
, flags
)
u_int32 unit
, type
, flags
;
struct scsi_load scsi_cmd
;
struct st_data
*st
= st_data
[unit
];
struct scsi_link
*sc_link
= st
->sc_link
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
error
= st_chkeod(unit
, FALSE
, &nmarks
, flags
);
sc_link
->flags
&= ~SDEV_MEDIA_LOADED
;
if (st
->quirks
& ST_Q_IGNORE_LOADS
)
scsi_cmd
.op_code
= LOAD_UNLOAD
;
return (scsi_scsi_cmd(st
->sc_link
,
(struct scsi_generic
*) &scsi_cmd
,
st_rewind(unit
, immed
, flags
)
struct scsi_rewind scsi_cmd
;
struct st_data
*st
= st_data
[unit
];
error
= st_chkeod(unit
, FALSE
, &nmarks
, flags
);
st
->flags
&= ~ST_PER_ACTION
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= REWIND
;
scsi_cmd
.byte2
= immed
? SR_IMMED
: 0;
return (scsi_scsi_cmd(st
->sc_link
,
(struct scsi_generic
*) &scsi_cmd
,
immed
? 5000 : 300000, /* 5 sec or 5 min */
#define SIGNAL_SHORT_READ
#define SIGNAL_SHORT_READ bp->b_flags |= B_ERROR;
* Look at the returned sense and act on the error and detirmine
* The unix error number to pass back... (0 = report no error)
* (-1 = continue processing)
struct scsi_link
*sc_link
= xs
->sc_link
;
struct scsi_sense_data
*sense
= &(xs
->sense
);
boolean silent
= xs
->flags
& SCSI_SILENT
;
u_int32 unit
= sc_link
->dev_unit
;
struct st_data
*st
= st_data
[unit
];
* Get the sense fields and work out what code
if (sense
->error_code
& SSD_ERRCODE_VALID
) {
info
= ntohl(*((int32
*) sense
->ext
.extended
.info
));
info
= xs
->datalen
; /* bad choice if fixed blocks */
if ((sense
->error_code
& SSD_ERRCODE
) != 0x70) {
return (-1); /* let the generic code handle it */
if (st
->flags
& ST_FIXEDBLOCKS
) {
xs
->resid
= info
* st
->blksiz
;
if (sense
->ext
.extended
.flags
& SSD_EOM
) {
st
->flags
|= ST_EIO_PENDING
;
if (sense
->ext
.extended
.flags
& SSD_FILEMARK
) {
st
->flags
|= ST_AT_FILEMARK
;
if (sense
->ext
.extended
.flags
& SSD_ILI
) {
st
->flags
|= ST_EIO_PENDING
;
if (sense
->error_code
& SSD_ERRCODE_VALID
&&
printf("st%d: block wrong size"
", %d blocks residual\n", unit
* This quirk code helps the drive read
* the first tape block, regardless of
* format. That is required for these
* drives to return proper MODE SENSE
if ((st
->quirks
& ST_Q_SNS_HLP
) &&
!(sc_link
->flags
& SDEV_MEDIA_LOADED
)) {
* If no data was tranfered, do it immediatly
if (xs
->resid
>= xs
->datalen
) {
if (st
->flags
& ST_EIO_PENDING
) {
if (st
->flags
& ST_AT_FILEMARK
) {
} else { /* must be variable mode */
xs
->resid
= xs
->datalen
; /* to be sure */
if (sense
->ext
.extended
.flags
& SSD_EOM
) {
if (sense
->ext
.extended
.flags
& SSD_FILEMARK
) {
bp
->b_resid
= bp
->b_bcount
;
if (sense
->ext
.extended
.flags
& SSD_ILI
) {
* the record was bigger than the read
printf("st%d: %d-byte record "
key
= sense
->ext
.extended
.flags
& SSD_KEY
;
* This quirk code helps the drive read the
* first tape block, regardless of format. That
* is required for these drives to return proper
* MODE SENSE information.
if ((st
->quirks
& ST_Q_SNS_HLP
) &&
!(sc_link
->flags
& SDEV_MEDIA_LOADED
)) { /* still starting */
} else if (!(st
->flags
& (ST_2FM_AT_EOD
| ST_BLANK_READ
))) {
st
->flags
|= ST_BLANK_READ
;
return (-1); /* let the default/generic handler handle it */
* The quirk here is that the drive returns some value to st_mode_sense
* incorrectly until the tape has actually passed by the head.
* The method is to set the drive to large fixed-block state (user-specified
* density and 1024-byte blocks), then read and rewind to get it to sense the
* tape. If that doesn't work, try 512-byte fixed blocks. If that doesn't
* work, as a last resort, try variable- length blocks. The result will be
* the ability to do an accurate st_mode_sense.
* We know we can do a rewind because we just did a load, which implies rewind.
* Rewind seems preferable to space backward if we have a virgin tape.
* The rest of the code for this quirk is in ILI processing and BLANK CHECK
* error processing, both part of st_interpret_sense.
struct st_data
*st
= st_data
[unit
];
buf
= malloc(1024, M_TEMP
, M_NOWAIT
);
if (errno
= st_mode_sense(unit
, 0)) {
switch ((int)st
->blksiz
) {
st
->flags
|= ST_FIXEDBLOCKS
;
st
->flags
&= ~ST_FIXEDBLOCKS
;
} if (errno
= st_mode_select(unit
, 0)) {
st_read(unit
, buf
, readsiz
, SCSI_SILENT
);
if (errno
= st_rewind(unit
, FALSE
, 0)) {
} while (readsiz
!= 1 && readsiz
> st
->blksiz
);