Return to BSD News archive
Newsgroups: comp.os.386bsd.bugs Path: sserve!newshost.anu.edu.au!munnari.oz.au!news.Hawaii.Edu!ames!saimiri.primate.wisc.edu!usenet.coe.montana.edu!nate From: nate@cs.montana.edu (Nate Williams) Subject: FWD: new scsi/st.c Message-ID: <1993May26.185105.212@coe.montana.edu> Sender: usenet@coe.montana.edu (USENET News System) Organization: CS Date: Wed, 26 May 1993 18:51:05 GMT Lines: 2179 ------------------- From: Julian Elischer <julian@jules.dialix.oz.au> This is a new st.c (scsi tape driver). I will also be sending out some changed .h files and a version of mt that can manipulate this driver. julian # This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # scsi/st.c # echo x - scsi/st.c sed 's/^X//' >scsi/st.c << 'END-of-scsi/st.c' X/* X * Written by Julian Elischer (julian@tfs.com) X * for TRW Financial Systems for use under the MACH(2.5) operating system. X * X * TRW Financial Systems, in accordance with their agreement with Carnegie X * Mellon University, makes this software available to CMU to distribute X * or use in any manner that they see fit as long as this message is kept with X * the software. For this reason TFS also grants any other persons or X * organisations permission to use or modify this software. X * X * TFS supplies this software to be publicly redistributed X * on the understanding that TFS is not responsible for the correct X * functioning of this software in any circumstances. X * X * X * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE X * -------------------- ----- ---------------------- X * CURRENT PATCH LEVEL: 1 00098 X * -------------------- ----- ---------------------- X * X * 16 Feb 93 Julian Elischer ADDED for SCSI system X */ X X/* X * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 X * major changes by Julian Elischer (julian@jules.dialix.oz.au) May 1993 X */ X X X/* X * To do: X * work out some better way of guessing what a good timeout is going X * to be depending on whether we expect to retension or not. X * X */ X X#include <sys/types.h> X#include <st.h> X X#include <sys/param.h> X#include <sys/systm.h> X X#include <sys/errno.h> X#include <sys/ioctl.h> X#include <sys/buf.h> X#include <sys/proc.h> X#include <sys/user.h> X#include <sys/mtio.h> X X#if defined(OSF) X#define SECSIZE 512 X#endif /* defined(OSF) */ X X#include <scsi/scsi_all.h> X#include <scsi/scsi_tape.h> X#include <scsi/scsiconf.h> X X Xlong int ststrats,stqueues; X X/* Defines for device specific stuff */ X#define PAGE_0_SENSE_DATA_SIZE 12 X#define PAGESIZ 4096 X#define DEF_FIXED_BSIZE 512 X#define STQSIZE 4 X#define ST_RETRIES 4 X X X#define MODE(z) ( (minor(z) & 0x03) ) X#define DSTY(z) ( ((minor(z) >> 2) & 0x03) ) X#define UNIT(z) ( (minor(z) >> 4) ) X X#define LOW_DSTY 3 X#define MED_DSTY 2 X#define HIGH_DSTY 1 X X#define SCSI_2_MAX_DENSITY_CODE 0x17 /* maximum density code specified X in SCSI II spec. */ X/***************************************************************\ X* Define various devices that we know mis-behave in some way, * X* and note how they are bad, so we can correct for them * X\***************************************************************/ Xstruct modes X{ X int quirks; /* same definitions as in rogues */ X char density; X char spare[3]; X}; Xstruct rogues X{ X char *name; X char *manu; X char *model; X char *version; X int quirks; /* valid for all modes */ X struct modes modes[4]; X}; X X/* define behaviour codes (quirks) */ X#define ST_Q_NEEDS_PAGE_0 0x00001 X#define ST_Q_FORCE_FIXED_MODE 0x00002 X#define ST_Q_FORCE_VAR_MODE 0x00004 X Xstatic struct rogues gallery[] = /* ends with an all null entry */ X{ X { "Such an old device ", "pre-scsi", " unknown model ","????", X 0, X { {ST_Q_FORCE_FIXED_MODE,0}, /* minor 0,1,2,3 */ X {ST_Q_FORCE_FIXED_MODE,QIC_24}, /* minor 4,5,6,7 */ X {ST_Q_FORCE_VAR_MODE,HALFINCH_1600}, /* minor 8,9,10,11*/ X {ST_Q_FORCE_VAR_MODE,HALFINCH_6250} /* minor 12,13,14,15*/ X } X }, X { "Tandberg tdc3600", "TANDBERG", " TDC 3600 ","????", X ST_Q_NEEDS_PAGE_0, X { {0,0}, /* minor 0,1,2,3*/ X {ST_Q_FORCE_VAR_MODE,QIC_525}, /* minor 4,5,6,7*/ X {0,QIC_150}, /* minor 8,9,10,11*/ X {0,QIC_120} /* minor 12,13,14,15*/ X } X }, X { "Archive Viper 150", "ARCHIVE ", "VIPER 150 21247","????", X ST_Q_NEEDS_PAGE_0, X { {0,0}, /* minor 0,1,2,3*/ X {0,QIC_150}, /* minor 4,5,6,7*/ X {0,QIC_120}, /* minor 8,9,10,11*/ X {0,QIC_24} /* minor 12,13,14,15*/ X } X }, X {(char *)0} X}; X X X#ifndef __386BSD__ Xstruct buf stbuf[NST][STQSIZE]; /* buffer for raw io (one per device) */ Xstruct buf *stbuf_free[NST]; /* queue of free buffers for raw io */ X#endif __386BSD__ Xstruct buf st_buf_queue[NST]; Xint ststrategy(); Xvoid stminphys(); Xstruct scsi_xfer st_scsi_xfer[NST]; Xint st_xfer_block_wait[NST]; X X#if defined(OSF) Xcaddr_t st_window[NST]; X#endif /* defined(OSF) */ X#ifndef MACH X#define ESUCCESS 0 X#endif MACH X Xint st_debug = 0; X Xint stattach(); Xint st_done(); X Xstruct st_data X{ X/*--------------------present operating parameters, flags etc.----------------*/ X int flags; /* see below */ X int blksiz; /* blksiz we are using */ X int density; /* present density */ X int quirks; /* quirks for the open mode */ X int last_dsty; /* last density used */ X/*--------------------device/scsi parameters----------------------------------*/ X struct scsi_switch *sc_sw; /* address of scsi low level switch */ X int ctlr; /* so they know which one we want */ X int targ; /* our scsi target ID */ X int lu; /* our scsi lu */ X/*--------------------parameters reported by the device ----------------------*/ X int blkmin; /* min blk size */ X int blkmax; /* max blk size */ X/*--------------------parameters reported by the device for this media--------*/ X int numblks; /* nominal blocks capacity */ X int media_blksiz; /* 0 if not ST_FIXEDBLOCKS */ X int media_density; /* this is what it said when asked */ X/*--------------------quirks for the whole drive------------------------------*/ X int drive_quirks; /* quirks of this drive */ X/*--------------------How we should set up when openning each minor device----*/ X struct modes modes[4]; /* plus more for each mode */ X/*--------------------storage for sense data returned by the drive------------*/ X unsigned char sense_data[12]; /* additional sense data needed */ X /* for mode sense/select. */ X}st_data[NST]; X#define ST_INITIALIZED 0x01 X#define ST_INFO_VALID 0x02 X#define ST_OPEN 0x04 X#define ST_WRITTEN 0x10 X#define ST_FIXEDBLOCKS 0x20 X#define ST_AT_FILEMARK 0x40 X#define ST_AT_EOM 0x80 X X#define ST_PER_ACTION (ST_AT_FILEMARK | ST_AT_EOM) X#define ST_PER_OPEN (ST_OPEN | ST_WRITTEN | ST_PER_ACTION) X#define ST_PER_MEDIA ST_FIXEDBLOCKS X Xstatic int next_st_unit = 0; X/***********************************************************************\ X* The routine called by the low level scsi routine when it discovers * X* A device suitable for this driver * X\***********************************************************************/ X Xint stattach(ctlr,targ,lu,scsi_switch) Xstruct scsi_switch *scsi_switch; X{ X int unit,i; X struct st_data *st; X X if(scsi_debug & PRINTROUTINES) printf("stattach: "); X /*******************************************************\ X * Check we have the resources for another drive * X \*******************************************************/ X unit = next_st_unit++; X if( unit >= NST) X { X printf("Too many scsi tapes..(%d > %d) reconfigure kernel", X (unit + 1),NST); X return(0); X } X st = st_data + unit; X /*******************************************************\ X * Store information needed to contact our base driver * X \*******************************************************/ X st->sc_sw = scsi_switch; X st->ctlr = ctlr; X st->targ = targ; X st->lu = lu; X X /*******************************************************\ X * Store information about default densities * X \*******************************************************/ X st->modes[HIGH_DSTY].density = QIC_525; X st->modes[MED_DSTY].density = QIC_150; X st->modes[LOW_DSTY].density = QIC_120; X X /*******************************************************\ X * Check if the drive is a known criminal and take * X * Any steps needed to bring it into line * X \*******************************************************/ X st_identify_drive(unit); X X /*******************************************************\ X * Use the subdriver to request information regarding * X * the drive. We cannot use interrupts yet, so the * X * request must specify this. * X \*******************************************************/ X if(st_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) X { X if(st_test_ready(unit,SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) X { X printf("\tst%d: tape present: %d blocks of %d bytes\n", X unit, st->numblks, st->media_blksiz); X } X else X { X printf("\tst%d: drive empty\n", unit); X } X } X else X { X printf("\tst%d: drive offline\n", unit); X } X /*******************************************************\ X * Set up the bufs for this device * X \*******************************************************/ X#ifndef __386BSD__ X stbuf_free[unit] = (struct buf *)0; X for (i = 1; i < STQSIZE; i++) X { X stbuf[unit][i].b_forw = stbuf_free[unit]; X stbuf_free[unit]=&stbuf[unit][i]; X } X#endif __386BSD__ X st_buf_queue[unit].b_active = 0; X st_buf_queue[unit].b_actf = st_buf_queue[unit].b_actl = 0; X X#if defined(OSF) X st_window[unit] = (caddr_t)alloc_kva(SECSIZE*256+PAGESIZ); X#endif /* defined(OSF) */ X X st->flags |= ST_INITIALIZED; X return; X X} X X/***********************************************************************\ X* Use the identify routine in 'scsiconf' to get drive info so we can * X* Further tailor our behaviour. * X\***********************************************************************/ X Xst_identify_drive(unit) Xint unit; X{ X X struct st_data *st; X struct scsi_inquiry_data inqbuf; X struct rogues *finger; X char manu[32]; X char model[32]; X char version[32]; X X X st = st_data + unit; X /*******************************************************\ X * Get the device type information * X \*******************************************************/ X if (scsi_inquire(st->ctlr, st->targ, st->lu, st->sc_sw, &inqbuf, X SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT) != COMPLETE) X { X printf(" st%d: couldn't get device type, using default\n", unit); X return; X } X if(inqbuf.ansii_version == 0) X { X /***********************************************\ X * If not advanced enough, use default values * X \***********************************************/ X strncpy(manu,"pre-scsi",8);manu[8]=0; X strncpy(model," unknown model ",16);model[16]=0; X strncpy(version,"????",4);version[4]=0; X } X else X { X strncpy(manu,inqbuf.vendor,8);manu[8]=0; X strncpy(model,inqbuf.product,16);model[16]=0; X strncpy(version,inqbuf.revision,4);version[4]=0; X } X X /*******************************************************\ X * Load the parameters for this kind of device, so we * X * treat it as appropriate for each operating mode * X \*******************************************************/ X finger = gallery; X while(finger->name) X { X if ((strcmp(manu, finger->manu) == 0 ) X && (strcmp(model, finger->model) == 0 )) X { X printf(" st%d: %s is a known rogue\n", unit,finger->name); X st->modes[0] = finger->modes[0]; X st->modes[1] = finger->modes[1]; X st->modes[2] = finger->modes[2]; X st->modes[3] = finger->modes[3]; X st->drive_quirks= finger->quirks; X st->quirks = finger->quirks; /*start value*/ X break; X } X else X { X finger++; /* go to next suspect */ X } X } X} X X/*******************************************************\ X* open the device. * X\*******************************************************/ Xstopen(dev) X{ X int unit,mode,dsty; X struct st_data *st; X unit = UNIT(dev); X mode = MODE(dev); X dsty = DSTY(dev); X st = st_data + unit; X X /*******************************************************\ X * Check the unit is legal * X \*******************************************************/ X if ( unit >= NST ) X { X return(ENXIO); X } X /*******************************************************\ X * Only allow one at a time * X \*******************************************************/ X if(st->flags & ST_OPEN) X { X return(ENXIO); X } X /*******************************************************\ X * Set up the mode flags according to the minor number * X * ensure all open flags are in a known state * X * if it's a different mode, dump all cached parameters * X \*******************************************************/ X if(st->last_dsty != dsty) X st->flags &= ~ST_INFO_VALID; X st->last_dsty = dsty; X st->flags &= ~ST_PER_OPEN; X st->quirks = st->drive_quirks | st->modes[dsty].quirks; X st->density = st->modes[dsty].density; X X if(scsi_debug & (PRINTROUTINES | TRACEOPENS)) X printf("stopen: dev=0x%x (unit %d (of %d))\n" X , dev, unit, NST); X /*******************************************************\ X * Make sure the device has been initialised * X \*******************************************************/ X if (!(st->flags & ST_INITIALIZED)) X return(ENXIO); X X /*******************************************************\ X * Check that it is still responding and ok. * X \*******************************************************/ X#ifdef removing_this X if(scsi_debug & TRACEOPENS) X printf("device is "); X if (!(st_req_sense(unit, 0))) /* may get a 'unit attention' if new */ X { X if(scsi_debug & TRACEOPENS) X printf("not responding\n"); X return(ENXIO); X } X if(scsi_debug & TRACEOPENS) X printf("ok\n"); X#endif X if(!(st_test_ready(unit,0))) X { X printf("st%d not ready\n",unit); X return(EIO); X } X if(!(st_test_ready(unit,0))) /* first may get 'unit attn' */ X { X printf("st%d not ready\n",unit); X return(EIO); X } X X /***************************************************************\ X * If the media is new, then make sure we give it a chance to * X * to do a 'load' instruction. Possibly the test ready * X * may not read true until this is done.. check this! XXX * X \***************************************************************/ X if(!(st->flags & ST_INFO_VALID)) /* is media new? */ X { X if(!st_load(unit,LD_LOAD,0)) X { X return(EIO); X } X } X X /*******************************************************\ X * Load the physical device parameters * X * loads: blkmin, blkmax * X \*******************************************************/ X if(!st_rd_blk_lim(unit,0)) X { X return(EIO); X } X X /*******************************************************\ X * Load the media dependent parameters * X * includes: media_blksiz,media_density,numblks * X \*******************************************************/ X if(!st_mode_sense(unit,0)) X { X return(EIO); X } X X /*******************************************************\ X * From media parameters, device parameters and quirks, * X * work out how we should be setting outselves up * X \*******************************************************/ X if(! st_decide_mode(unit)) X return(ENXIO); X X if(!st_mode_select(unit,0,st->density)) X { X return(EIO); X } X X st->flags |= ST_INFO_VALID; X X st_prevent(unit,PR_PREVENT,0); /* who cares if it fails? */ X X if(scsi_debug & TRACEOPENS) X printf("Params loaded "); X X X st->flags |= ST_OPEN; X return(0); X} X X/*******************************************************\ X* close the device.. only called if we are the LAST * X* occurence of an open device * X\*******************************************************/ Xstclose(dev) X{ X unsigned char unit,mode; X struct st_data *st; X X unit = UNIT(dev); X mode = MODE(dev); X st = st_data + unit; X X if(scsi_debug & TRACEOPENS) X printf("Closing device"); X if(st->flags & ST_WRITTEN) X { X st_write_filemarks(unit,1,0); X } X st->flags &= ~ST_WRITTEN; X switch(mode) X { X case 0: X st_rewind(unit,FALSE,SCSI_SILENT); X st_prevent(unit,PR_ALLOW,SCSI_SILENT); X break; X case 1: X st_prevent(unit,PR_ALLOW,SCSI_SILENT); X break; X case 2: X st_rewind(unit,FALSE,SCSI_SILENT); X st_prevent(unit,PR_ALLOW,SCSI_SILENT); X st_load(unit,LD_UNLOAD,SCSI_SILENT); X break; X case 3: X st_prevent(unit,PR_ALLOW,SCSI_SILENT); X st_load(unit,LD_UNLOAD,SCSI_SILENT); X break; X default: X printf("st%d:close: Bad mode (minor number)%d how's it open?\n" X ,unit,mode); X return(EINVAL); X } X st->flags &= ~ST_PER_OPEN; X return(0); X} X X/***************************************************************\ X* Given all we know about the device, media, mode and 'quirks', * X* make a decision as to how we should be set up. * X\***************************************************************/ Xst_decide_mode(unit) Xint unit; X{ X struct st_data *st = st_data + unit; X X if(st->flags & ST_INFO_VALID) return TRUE; X X switch(st->quirks & (ST_Q_FORCE_FIXED_MODE | ST_Q_FORCE_VAR_MODE)) X { X case (ST_Q_FORCE_FIXED_MODE | ST_Q_FORCE_VAR_MODE): X printf("st%d: bad quirks\n",unit); X return FALSE; X case 0: X switch(st->density) X { X case QIC_120: X case QIC_150: X goto fixed; X case HALFINCH_800: X case HALFINCH_1600: X case HALFINCH_6250: X case QIC_525: X goto var; X default: X break; X } X if((st->blkmin && (st->blkmin == st->blkmax)) X || (st->media_blksiz)) X goto fixed; X else X goto var; X X case ST_Q_FORCE_FIXED_MODE: Xfixed: X st->flags |= ST_FIXEDBLOCKS; X if(st->media_blksiz) X { X st->blksiz = st->media_blksiz; X } X else X { X if(st->blkmin) X { X st->blksiz = st->blkmin; /* just to make sure */ X } X else X { X st->blksiz = DEF_FIXED_BSIZE; X } X } X break; X case ST_Q_FORCE_VAR_MODE: Xvar: X st->flags &= ~ST_FIXEDBLOCKS; X st->blksiz = 0; X break; X } X return(TRUE); X} X#ifndef __386BSD__ X/*******************************************************\ X* Get ownership of this unit's buf * X* If need be, sleep on it, until it comes free * X\*******************************************************/ Xstruct buf * Xst_get_buf(unit) { X struct buf *rc; X X while (!(rc = stbuf_free[unit])) X sleep((caddr_t)&stbuf_free[unit], PRIBIO+1); X stbuf_free[unit] = stbuf_free[unit]->b_forw; X rc->b_error = 0; X rc->b_resid = 0; X rc->b_flags = 0; X return(rc); X} X X/*******************************************************\ X* Free this unit's buf, wake processes waiting for it * X\*******************************************************/ Xst_free_buf(unit,bp) Xstruct buf *bp; X{ X if (!stbuf_free[unit]) X wakeup((caddr_t)&stbuf_free[unit]); X bp->b_forw = stbuf_free[unit]; X stbuf_free[unit] = bp; X} X X X/*******************************************************\ X* Get the buf for this unit and use physio to do it * X\*******************************************************/ Xstread(dev,uio) Xregister short dev; Xstruct uio *uio; X{ X int unit = UNIT(dev); X struct buf *bp = st_get_buf(unit); X int rc; X rc = physio(ststrategy, bp, dev, B_READ, stminphys, uio); X st_free_buf(unit,bp); X return(rc); X} X X/*******************************************************\ X* Get the buf for this unit and use physio to do it * X\*******************************************************/ Xstwrite(dev,uio) Xdev_t dev; Xstruct uio *uio; X{ X int unit = UNIT(dev); X struct buf *bp = st_get_buf(unit); X int rc; X X rc = physio(ststrategy, bp, dev, B_WRITE, stminphys, uio); X st_free_buf(unit,bp); X return(rc); X} X X X#endif __386BSD__ X/*******************************************************\ X* trim the size of the transfer if needed, * X* called by physio * X* basically the smaller of our min and the scsi driver's* X* minphys * X\*******************************************************/ Xvoid stminphys(bp) Xstruct buf *bp; X{ X (*(st_data[UNIT(bp->b_dev)].sc_sw->scsi_minphys))(bp); X} X X/*******************************************************\ X* Actually translate the requested transfer into * X* one the physical driver can understand * X* The transfer is described by a buf and will include * X* only one physical transfer. * X\*******************************************************/ X Xint ststrategy(bp) Xstruct buf *bp; X{ X struct buf *dp; X unsigned char unit; X unsigned int opri; X X ststrats++; X unit = UNIT((bp->b_dev)); X if(scsi_debug & PRINTROUTINES) printf("\nststrategy "); X if(scsi_debug & SHOWREQUESTS) printf("st%d: %d bytes @ blk%d\n", X unit,bp->b_bcount,bp->b_blkno); X /*******************************************************\ X * If it's a null transfer, return immediatly * X \*******************************************************/ X if (bp->b_bcount == 0) X { X goto done; X } X X /*******************************************************\ X * Odd sized request on fixed drives are verboten * X \*******************************************************/ X if(st_data[unit].flags & ST_FIXEDBLOCKS) X { X if(bp->b_bcount % st_data[unit].blksiz) X { X printf("st%d: bad request, must be multiple of %d\n", X unit, st_data[unit].blksiz); X bp->b_error = EIO; X goto bad; X } X } X /*******************************************************\ X * as are too-short requests on variable length drives. * X \*******************************************************/ X else if(bp->b_bcount < st_data[unit].blkmin) X { X printf("st%d: bad request, must not be less than %d\n", X unit, st_data[unit].blkmin); X bp->b_error = EIO; X goto bad; X } X X#ifdef __386BSD__ X stminphys(bp); X#endif __386BSD__ X opri = splbio(); X dp = &st_buf_queue[unit]; X X /*******************************************************\ X * Place it in the queue of disk activities for this tape* X * at the end * X \*******************************************************/ X while ( dp->b_actf) X { X dp = dp->b_actf; X } X dp->b_actf = bp; X bp->b_actf = NULL; X X /*******************************************************\ X * Tell the device to get going on the transfer if it's * X * not doing anything, otherwise just wait for completion* X * (All a bit silly if we're only allowing 1 open but..) * X \*******************************************************/ X ststart(unit); X X splx(opri); X return; Xbad: X bp->b_flags |= B_ERROR; Xdone: X /*******************************************************\ X * Correctly set the buf to indicate a completed xfer * X \*******************************************************/ X iodone(bp); X return; X} X X X/***************************************************************\ X* ststart looks to see if there is a buf waiting for the device * X* and that the device is not already busy. If both are true, * X* It deques the buf and creates a scsi command to perform the * X* transfer in the buf. The transfer request will call st_done * X* on completion, which will in turn call this routine again * X* so that the next queued transfer is performed. * X* The bufs are queued by the strategy routine (ststrategy) * X* * X* This routine is also called after other non-queued requests * X* have been made of the scsi driver, to ensure that the queue * X* continues to be drained. * X\***************************************************************/ X/* ststart() is called at splbio */ Xststart(unit) X{ X int drivecount; X register struct buf *bp = 0; X register struct buf *dp; X struct scsi_xfer *xs; X struct scsi_rw_tape cmd; X int blkno, nblk; X struct st_data *st; X X X st = st_data + unit; X X if(scsi_debug & PRINTROUTINES) printf("ststart%d ",unit); X /*******************************************************\ X * See if there is a buf to do and we are not already * X * doing one * X \*******************************************************/ X xs=&st_scsi_xfer[unit]; X if(xs->flags & INUSE) X { X return; /* unit already underway */ X } Xtrynext: X if(st_xfer_block_wait[unit]) /* a special awaits, let it proceed first */ X { X wakeup(&st_xfer_block_wait[unit]); X return; X } X X dp = &st_buf_queue[unit]; X if ((bp = dp->b_actf) != NULL) X { X dp->b_actf = bp->b_actf; X } X else /* no work to do */ X { X return; X } X xs->flags = INUSE; /* Now ours */ X X X /*******************************************************\ X * We have a buf, now we should move the data into * X * a scsi_xfer definition and try start it * X \*******************************************************/ X X /*******************************************************\ X * If we are at a filemark but have not reported it yet * X * then we should report it now * X \*******************************************************/ X if(st->flags & ST_AT_FILEMARK) X { X bp->b_error = 0; X bp->b_flags |= B_ERROR; /* EOF*/ X st->flags &= ~ST_AT_FILEMARK; X biodone(bp); X xs->flags = 0; /* won't need it now */ X goto trynext; X } X /*******************************************************\ X * If we are at EOM but have not reported it yet * X * then we should report it now * X \*******************************************************/ X if(st->flags & ST_AT_EOM) X { X bp->b_error = EIO; X bp->b_flags |= B_ERROR; X st->flags &= ~ST_AT_EOM; X biodone(bp); X xs->flags = 0; /* won't need it now */ X goto trynext; X } X /*******************************************************\ X * Fill out the scsi command * X \*******************************************************/ X bzero(&cmd, sizeof(cmd)); X if((bp->b_flags & B_READ) == B_WRITE) X { X st->flags |= ST_WRITTEN; X xs->flags |= SCSI_DATA_OUT; X } X else X { X xs->flags |= SCSI_DATA_IN; X } X cmd.op_code = (bp->b_flags & B_READ) X ? READ_COMMAND_TAPE X : WRITE_COMMAND_TAPE; X X /*******************************************************\ X * Handle "fixed-block-mode" tape drives by using the * X * block count instead of the length. * X \*******************************************************/ X if(st->flags & ST_FIXEDBLOCKS) X { X cmd.fixed = 1; X lto3b(bp->b_bcount/st->blksiz,cmd.len); X } X else X { X lto3b(bp->b_bcount,cmd.len); X } X X /*******************************************************\ X * Fill out the scsi_xfer structure * X * Note: we cannot sleep as we may be an interrupt * X \*******************************************************/ X xs->flags |= SCSI_NOSLEEP; X xs->adapter = st->ctlr; X xs->targ = st->targ; X xs->lu = st->lu; X xs->retries = 1; /* can't retry on tape*/ X xs->timeout = 100000; /* allow 100 secs for retension */ X xs->cmd = (struct scsi_generic *)&cmd; X xs->cmdlen = sizeof(cmd); X xs->data = (u_char *)bp->b_un.b_addr; X xs->datalen = bp->b_bcount; X xs->resid = bp->b_bcount; X xs->when_done = st_done; X xs->done_arg = unit; X xs->done_arg2 = (int)xs; X xs->error = XS_NOERROR; X xs->bp = bp; X /*******************************************************\ X * Pass all this info to the scsi driver. * X \*******************************************************/ X X X#if defined(OSF)||defined(FIX_ME) X if (bp->b_flags & B_PHYS) { X xs->data = (u_char*)map_pva_kva(bp->b_proc, bp->b_un.b_addr, X bp->b_bcount, st_window[unit], X (bp->b_flags&B_READ)?B_WRITE:B_READ); X } else { X xs->data = (u_char*)bp->b_un.b_addr; X } X#endif /* defined(OSF) */ X X if ( (*(st->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED) X { X printf("st%d: oops not queued",unit); X xs->error = XS_DRIVER_STUFFUP; X st_done(unit,xs); X } X stqueues++; X} X X/*******************************************************\ X* This routine is called by the scsi interrupt when * X* the transfer is complete. X\*******************************************************/ Xint st_done(unit,xs) Xint unit; Xstruct scsi_xfer *xs; X{ X struct buf *bp; X int retval; X X if(scsi_debug & PRINTROUTINES) printf("st_done%d ",unit); X if (! (xs->flags & INUSE)) X panic("scsi_xfer not in use!"); X if(bp = xs->bp) X { X switch(xs->error) X { X case XS_NOERROR: X bp->b_flags &= ~B_ERROR; X bp->b_error = 0; X bp->b_resid = 0; X break; X case XS_SENSE: X retval = (st_interpret_sense(unit,xs)); X if(retval) X { X /***************************************\ X * We have a real error, the bit should * X * be set to indicate this. The return * X * value will contain the unix error code* X * that the error interpretation routine * X * thought was suitable, so pass this * X * value back in the buf structure. * X * Furthermore we return information * X * saying that no data was transferred * X \***************************************/ X bp->b_flags |= B_ERROR; X bp->b_error = retval; X bp->b_resid = bp->b_bcount; X st_data[unit].flags X &= ~(ST_AT_FILEMARK|ST_AT_EOM); X } X else X { X /***********************************************\ X * The error interpretation code has declared * X * that it wasn't a real error, or at least that * X * we should be ignoring it if it was. * X \***********************************************/ X if(xs->resid && ( xs->resid != xs->datalen )) X { X /***************************************\ X * Here we have the tricky part.. * X * We successfully read less data than * X * we requested. (but not 0) * X *------for variable blocksize tapes:----* X * UNDER 386BSD: * X * We should legitimatly have the error * X * bit set, with the error value set to * X * zero.. This is to indicate to the * X * physio code that while we didn't get * X * as much information as was requested, * X * we did reach the end of the record * X * and so physio should not call us * X * again for more data... we have it all * X * SO SET THE ERROR BIT! * X * * X * UNDER MACH:(CMU) * X * To indicate the same as above, we * X * need only have a non 0 resid that is * X * less than the b_bcount, but the * X * ERROR BIT MUST BE CLEAR! (sigh) * X * * X * UNDER OSF1: * X * To indicate the same as above, we * X * need to have a non 0 resid that is * X * less than the b_bcount, but the * X * ERROR BIT MUST BE SET! (gasp)(sigh) * X * * X *-------for fixed blocksize device------* X * We could have read some successful * X * records before hitting * X * the EOF or EOT. These must be passed * X * to the user, before we report the * X * EOx. Only if there is no data for the * X * user do we report it now. (via an EIO * X * for EOM and resid == count for EOF). * X * We will report the EOx NEXT time.. * X \***************************************/ X#ifdef MACH /*osf and cmu varieties */ X#ifdef OSF X bp->b_flags |= B_ERROR; X#else OSF X bp->b_flags &= ~B_ERROR; X#endif OSF X#endif MACH X#ifdef __386BSD__ X bp->b_flags |= B_ERROR; X#endif __386BSD__ X bp->b_error = 0; X bp->b_resid = xs->resid; X if((st_data[unit].flags & ST_FIXEDBLOCKS)) X { X bp->b_resid *= st_data[unit].blksiz; X if( (st_data[unit].flags & ST_AT_EOM) X && (bp->b_resid == bp->b_bcount)) X { X bp->b_error = EIO; X st_data[unit].flags X &= ~ST_AT_EOM; X } X } X xs->error = XS_NOERROR; X break; X } X else X { X /***************************************\ X * We have come out of the error handler * X * with no error code.. we have also * X * not had an ili (would have gone to * X * the previous clause). Now we need to * X * distiguish between succesful read of * X * no data (EOF or EOM) and successfull * X * read of all requested data. * X * At least all o/s agree that: * X * 0 bytes read with no error is EOF * X * 0 bytes read with an EIO is EOM * X \***************************************/ X X bp->b_resid = bp->b_bcount; X if(st_data[unit].flags & ST_AT_FILEMARK) X { X st_data[unit].flags &= ~ST_AT_FILEMARK; X bp->b_flags &= ~B_ERROR; X bp->b_error = 0; X break; X } X if(st_data[unit].flags & ST_AT_EOM) X { X bp->b_flags |= B_ERROR; X bp->b_error = EIO; X st_data[unit].flags &= ~ST_AT_EOM; X break; X } X printf("st%d:error ignored\n" ,unit); X } X } X break; X X case XS_TIMEOUT: X printf("st%d timeout\n",unit); X break; X X case XS_BUSY: /* should retry */ /* how? */ X /************************************************/ X /* SHOULD put buf back at head of queue */ X /* and decrement retry count in (*xs) */ X /* HOWEVER, this should work as a kludge */ X /************************************************/ X if(xs->retries--) X { X xs->flags &= ~ITSDONE; X xs->error = XS_NOERROR; X if ( (*(st_data[unit].sc_sw->scsi_cmd))(xs) X == SUCCESSFULLY_QUEUED) X { /* don't wake the job, ok? */ X return; X } X printf("device busy"); X xs->flags |= ITSDONE; X } X X case XS_DRIVER_STUFFUP: X bp->b_flags |= B_ERROR; X bp->b_error = EIO; X break; X default: X printf("st%d: unknown error category from scsi driver\n" X ,unit); X } X biodone(bp); X xs->flags = 0; /* no longer in use */ X ststart(unit); /* If there's another waiting.. do it */ X } X else X { X wakeup(xs); X } X} X/*******************************************************\ X* Perform special action on behalf of the user * X* Knows about the internals of this device * X\*******************************************************/ Xstioctl(dev, cmd, arg, mode) Xdev_t dev; Xint cmd; Xcaddr_t arg; X{ X register i,j; X unsigned int opri; X int errcode = 0; X unsigned char unit; X int number,flags,ret; X struct st_data *st; X X /*******************************************************\ X * Find the device that the user is talking about * X \*******************************************************/ X flags = 0; /* give error messages, act on errors etc. */ X unit = UNIT(dev); X st = st_data + unit; X X switch(cmd) X { X X case MTIOCGET: X { X struct mtget *g = (struct mtget *) arg; X X bzero(g, sizeof(struct mtget)); X g->mt_type = 0x7; /* Ultrix compat */ /*?*/ X if (st->flags & ST_FIXEDBLOCKS) { X g->mt_bsiz = st->blksiz; X } else { X g->mt_bsiz = 0; X } X g->mt_dns_high = st->modes[HIGH_DSTY].density; X g->mt_dns_medium = st->modes[MED_DSTY].density; X g->mt_dns_low = st->modes[LOW_DSTY].density; X ret=TRUE; X break; X } X X X case MTIOCTOP: X { X struct mtop *mt = (struct mtop *) arg; X X if (st_debug) X printf("[sctape_sstatus: %x %x]\n", X mt->mt_op, mt->mt_count); X X X X /* compat: in U*x it is a short */ X number = mt->mt_count; X switch ((short)(mt->mt_op)) X { X case MTWEOF: /* write an end-of-file record */ X ret = st_write_filemarks(unit,number,flags); X st_data[unit].flags &= ~ST_WRITTEN; X break; X case MTFSF: /* forward space file */ X ret = st_space(unit,number,SP_FILEMARKS,flags); X break; X case MTBSF: /* backward space file */ X ret = st_space(unit,-number,SP_FILEMARKS,flags); X break; X case MTFSR: /* forward space record */ X ret = st_space(unit,number,SP_BLKS,flags); X break; X case MTBSR: /* backward space record */ X ret = st_space(unit,-number,SP_BLKS,flags); X break; X case MTREW: /* rewind */ X ret = st_rewind(unit,FALSE,flags); X break; X case MTOFFL: /* rewind and put the drive offline */ X if((ret = st_rewind(unit,FALSE,flags))) X { X st_prevent(unit,PR_ALLOW,0); X ret = st_load(unit,LD_UNLOAD,flags); X } X else X { X printf("rewind failed, unit still loaded\n"); X } X break; X case MTNOP: /* no operation, sets status only */ X case MTCACHE: /* enable controller cache */ X case MTNOCACHE: /* disable controller cache */ X ret = TRUE;; X break; X case MTSETBSIZ: /* Set block size for device */ X if (st->blkmin == st->blkmax) X { X /* This doesn't make sense for a */ X /* real fixed block device */ X ret = FALSE; X } X else X { X if ( number == 0 ) X { X /* Restoring original block size */ X st->flags &= ~ST_FIXEDBLOCKS; /*XXX*/ X st->blksiz = st->media_blksiz; X } X else X { X if (number < st->blkmin || number > st->blkmax) X { ret = FALSE; X ret = FALSE; X } X else X { X st->blksiz = number; X st->flags |= ST_FIXEDBLOCKS; X ret = TRUE; X } X } X } X break; X X /* How do we check that the drive can handle X the requested density ? */ X X case MTSETHDNSTY: /* Set high density defaults for device */ X if (number < 0 || number > SCSI_2_MAX_DENSITY_CODE) X { X ret = EINVAL; X } X else X { X st->modes[HIGH_DSTY].density = number; X ret = TRUE; X } X break; X X case MTSETMDNSTY: /* Set medium density defaults for device */ X if (number < 0 || number > SCSI_2_MAX_DENSITY_CODE) X { X ret = EINVAL; X } X else X { X st->modes[MED_DSTY].density = number; X ret = TRUE; X } X break; X X case MTSETLDNSTY: /* Set low density defaults for device */ X if (number < 0 || number > SCSI_2_MAX_DENSITY_CODE) X { X ret = FALSE; X } X else X { X st->modes[LOW_DSTY].density = number; X ret = TRUE; X } X break; X X default: X return EINVAL; X } X break; X } X case MTIOCIEOT: X case MTIOCEEOT: X ret=TRUE; X break; X } X X return(ret?ESUCCESS:EIO); X} X X X#ifdef removing_this X/*******************************************************\ X* Check with the device that it is ok, (via scsi driver)* X\*******************************************************/ Xst_req_sense(unit, flags) Xint flags; X{ X struct scsi_sense_data sense; X struct scsi_sense scsi_cmd; X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = REQUEST_SENSE; X scsi_cmd.length = sizeof(sense); X X if (st_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X &sense, X sizeof(sense), X 100000, X flags | SCSI_DATA_IN) != 0) X { X return(FALSE); X } X return(TRUE); X} X X#endif X/*******************************************************\ X* Get scsi driver to send a "are you ready" command * X\*******************************************************/ Xst_test_ready(unit,flags) Xint unit,flags; X{ X struct scsi_test_unit_ready scsi_cmd; X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = TEST_UNIT_READY; X X if (st_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X 0, X 0, X 100000, X flags) != 0) X { X return(FALSE); X } X return(TRUE); X} X X X#ifdef __STDC__ X#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) X#else X#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 ) X#endif X X/*******************************************************\ X* Ask the drive what it's min and max blk sizes are. * X\*******************************************************/ Xst_rd_blk_lim(unit, flags) Xint unit,flags; X{ X struct scsi_blk_limits scsi_cmd; X struct scsi_blk_limits_data scsi_blkl; X struct st_data *st = st_data + unit; X /*******************************************************\ X * First check if we have it all loaded * X \*******************************************************/ X if ((st->flags & ST_INFO_VALID)) return TRUE; X X /*******************************************************\ X * do a 'Read Block Limits' * X \*******************************************************/ X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = READ_BLK_LIMITS; X X /*******************************************************\ X * do the command, update the global values * X \*******************************************************/ X if (st_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X &scsi_blkl, X sizeof(scsi_blkl), X 5000, X flags | SCSI_DATA_IN) != 0) X { X if(!(flags & SCSI_SILENT)) X printf("could not get blk limits for unit %d\n", unit); X st->flags &= ~ST_INFO_VALID; X return(FALSE); X } X st->blkmin = b2tol(scsi_blkl.min_length); X st->blkmax = _3btol(&scsi_blkl.max_length_2); X X if (st_debug) X { X printf(" (%d <= blksiz <= %d\n) ",st->blkmin,st->blkmax); X } X} X X/*******************************************************\ X* Get the scsi driver to send a full inquiry to the * X* device and use the results to fill out the global * X* parameter structure. * X* * X* called from: * X* attach * X* open * X* ioctl (to reset original blksize) * X\*******************************************************/ Xst_mode_sense(unit, flags) Xint unit,flags; X{ X int scsi_sense_len; X char *scsi_sense_ptr; X struct scsi_mode_sense scsi_cmd; X struct scsi_sense X { X struct scsi_mode_header_tape header; X struct blk_desc blk_desc; X }scsi_sense; X X struct scsi_sense_page_0 X { X struct scsi_mode_header_tape header; X struct blk_desc blk_desc; X unsigned char sense_data[PAGE_0_SENSE_DATA_SIZE]; X /* Tandberg tape drives returns page 00 */ X /* with the sense data, whether or not */ X /* you want it( ie the don't like you */ X /* saying you want anything less!!!!! */ X /* They also expect page 00 */ X /* back when you issue a mode select */ X }scsi_sense_page_0; X struct st_data *st = st_data + unit; X X /*******************************************************\ X * First check if we have it all loaded * X \*******************************************************/ X if ((st->flags & ST_INFO_VALID)) return(TRUE); X X /*******************************************************\ X * Define what sort of structure we're working with * X \*******************************************************/ X if (st->quirks & ST_Q_NEEDS_PAGE_0) X { X scsi_sense_len = sizeof(scsi_sense_page_0); X scsi_sense_ptr = (char *) &scsi_sense_page_0; X } X else X { X scsi_sense_len = sizeof(scsi_sense); X scsi_sense_ptr = (char *) &scsi_sense; X } X X /*******************************************************\ X * Set up a mode sense * X \*******************************************************/ X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = MODE_SENSE; X scsi_cmd.length = scsi_sense_len; X X /*******************************************************\ X * do the command, but we don't need the results * X * just print them for our interest's sake, if asked, * X * or if we need it as a template for the mode select * X * store it away. * X \*******************************************************/ X if (st_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X scsi_sense_ptr, X scsi_sense_len, X 5000, X flags | SCSI_DATA_IN) != 0) X { X if(!(flags & SCSI_SILENT)) X printf("could not mode sense for unit %d\n", unit); X st->flags &= ~ST_INFO_VALID; X return(FALSE); X } X st->numblks = _3btol(&(((struct scsi_sense *)scsi_sense_ptr)->blk_desc.nblocks)); X st->media_blksiz = _3btol(&(((struct scsi_sense *)scsi_sense_ptr)->blk_desc.blklen)); X st->media_density = ((struct scsi_sense *)scsi_sense_ptr)->blk_desc.density; X if (st_debug) X { X printf("unit %d: %d blocks of %d bytes, write %s, %sbuffered", X unit, X st->numblks, X st->media_blksiz, X (((struct scsi_sense *)scsi_sense_ptr)->header.write_protected ? X "protected" : "enabled"), X (((struct scsi_sense *)scsi_sense_ptr)->header.buf_mode ? X "" : "un") X ); X } X if (st->quirks & ST_Q_NEEDS_PAGE_0) X { X bcopy(((struct scsi_sense_page_0 *)scsi_sense_ptr)->sense_data, X st->sense_data, X sizeof(((struct scsi_sense_page_0 *)scsi_sense_ptr)->sense_data)); X } X return(TRUE); X} X X/*******************************************************\ X* Send a filled out parameter structure to the drive to * X* set it into the desire modes etc. * X\*******************************************************/ Xst_mode_select(unit, flags, dsty_code) Xint unit,flags,dsty_code; X{ X int dat_len; X char *dat_ptr; X struct scsi_mode_select scsi_cmd; X struct dat X { X struct scsi_mode_header_tape header; X struct blk_desc blk_desc; X }dat; X struct dat_page_0 X { X struct scsi_mode_header_tape header; X struct blk_desc blk_desc; X unsigned char sense_data[PAGE_0_SENSE_DATA_SIZE]; X }dat_page_0; X struct st_data *st = st_data + unit; X X /*******************************************************\ X * Define what sort of structure we're working with * X \*******************************************************/ X if (st->quirks & ST_Q_NEEDS_PAGE_0) X { X dat_len = sizeof(dat_page_0); X dat_ptr = (char *) &dat_page_0; X } X else X { X dat_len = sizeof(dat); X dat_ptr = (char *) &dat; X } X X /*******************************************************\ X * Set up for a mode select * X \*******************************************************/ X bzero(dat_ptr, dat_len); X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = MODE_SELECT; X scsi_cmd.length = dat_len; X ((struct dat *)dat_ptr)->header.blk_desc_len = sizeof(struct blk_desc); X ((struct dat *)dat_ptr)->header.buf_mode = 1; X ((struct dat *)dat_ptr)->blk_desc.density = dsty_code; X if(st->flags & ST_FIXEDBLOCKS) X { X lto3b( st->blksiz , ((struct dat *)dat_ptr)->blk_desc.blklen); X } X if (st->quirks & ST_Q_NEEDS_PAGE_0) X { X bcopy(st->sense_data, ((struct dat_page_0 *)dat_ptr)->sense_data, X sizeof(((struct dat_page_0 *)dat_ptr)->sense_data)); X /* the Tandberg tapes need the block size to */ X /* be set on each mode sense/select. */ X } X /*******************************************************\ X * do the command * X \*******************************************************/ X if (st_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X dat_ptr, X dat_len, X 5000, X flags | SCSI_DATA_OUT) != 0) X { X if(!(flags & SCSI_SILENT)) X printf("could not mode select for unit %d\n", unit); X st->flags &= ~ST_INFO_VALID; X return(FALSE); X } X return(TRUE); X} X X/*******************************************************\ X* skip N blocks/filemarks/seq filemarks/eom * X\*******************************************************/ Xst_space(unit,number,what,flags) Xint unit,number,what,flags; X{ X struct scsi_space scsi_cmd; X X /* if we are at a filemark now, we soon won't be*/ X st_data[unit].flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = SPACE; X scsi_cmd.code = what; X lto3b(number,scsi_cmd.number); X if (st_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X 0, X 0, X 600000, /* 10 mins enough? */ X flags) != 0) X { X if(!(flags & SCSI_SILENT)) X printf("could not space st%d\n", unit); X st_data[unit].flags &= ~ST_INFO_VALID; X return(FALSE); X } X return(TRUE); X} X/*******************************************************\ X* write N filemarks * X\*******************************************************/ Xst_write_filemarks(unit,number,flags) Xint unit,number,flags; X{ X struct scsi_write_filemarks scsi_cmd; X X st_data[unit].flags &= ~(ST_AT_FILEMARK); X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = WRITE_FILEMARKS; X lto3b(number,scsi_cmd.number); X if (st_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X 0, X 0, X 100000, /* 10 secs.. (may need to repos head )*/ X flags) != 0) X { X if(!(flags & SCSI_SILENT)) X printf("could not write_filemarks st%d\n", unit); X st_data[unit].flags &= ~ST_INFO_VALID; X return(FALSE); X } X return(TRUE); X} X/*******************************************************\ X* load /unload (with retension if true) * X\*******************************************************/ Xst_load(unit,type,flags) Xint unit,type,flags; X{ X struct scsi_load scsi_cmd; X X st_data[unit].flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = LOAD_UNLOAD; X scsi_cmd.load=type; X if (type == LD_LOAD) X { X /*scsi_cmd.reten=TRUE;*/ X scsi_cmd.reten=FALSE; X } X else X { X scsi_cmd.reten=FALSE; X } X if (st_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X 0, X 0, X 30000, /* 30 secs */ X flags) != 0) X { X if(!(flags & SCSI_SILENT)) X printf("cannot load/unload st%d\n", unit); X return(FALSE); X st_data[unit].flags &= ~ST_INFO_VALID; X } X return(TRUE); X} X/*******************************************************\ X* Prevent or allow the user to remove the tape * X\*******************************************************/ Xst_prevent(unit,type,flags) Xint unit,type,flags; X{ X struct scsi_prevent scsi_cmd; X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = PREVENT_ALLOW; X scsi_cmd.prevent=type; X if (st_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X 0, X 0, X 5000, X flags) != 0) X { X if(!(flags & SCSI_SILENT)) X printf("cannot prevent/allow on st%d\n", unit); X st_data[unit].flags &= ~ST_INFO_VALID; X return(FALSE); X } X return(TRUE); X} X/*******************************************************\ X* Rewind the device * X\*******************************************************/ Xst_rewind(unit,immed,flags) Xint unit,immed,flags; X{ X struct scsi_rewind scsi_cmd; X X st_data[unit].flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = REWIND; X scsi_cmd.immed=immed; X if (st_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X 0, X 0, X immed?5000:300000, /* 5 sec or 5 min */ X flags) != 0) X { X if(!(flags & SCSI_SILENT)) X printf("could not rewind st%d\n", unit); X st_data[unit].flags &= ~ST_INFO_VALID; X return(FALSE); X } X return(TRUE); X} X/*******************************************************\ X* ask the scsi driver to perform a command for us. * X* Call it through the switch table, and tell it which * X* sub-unit we want, and what target and lu we wish to * X* talk to. Also tell it where to find the command * X* how long int is. * X* Also tell it where to read/write the data, and how * X* long the data is supposed to be * X\*******************************************************/ Xint st_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags) X Xint unit,flags; Xstruct scsi_generic *scsi_cmd; Xint cmdlen; Xint timeout; Xu_char *data_addr; Xint datalen; X{ X struct scsi_xfer *xs; X int retval; X int s; X struct st_data *st = st_data + unit; X X if(scsi_debug & PRINTROUTINES) printf("\nst_scsi_cmd%d ",unit); X if(st->sc_sw) /* If we have a scsi driver */ X { X X xs = &(st_scsi_xfer[unit]); X if(!(flags & SCSI_NOMASK)) X s = splbio(); X st_xfer_block_wait[unit]++; /* there is someone waiting */ X while (xs->flags & INUSE) X { X sleep(&st_xfer_block_wait[unit],PRIBIO+1); X } X st_xfer_block_wait[unit]--; X xs->flags = INUSE; X if(!(flags & SCSI_NOMASK)) X splx(s); X X /*******************************************************\ X * Fill out the scsi_xfer structure * X \*******************************************************/ X xs->flags |= flags; X xs->adapter = st->ctlr; X xs->targ = st->targ; X xs->lu = st->lu; X xs->retries = ST_RETRIES; X xs->timeout = timeout; X xs->cmd = scsi_cmd; X xs->cmdlen = cmdlen; X xs->data = data_addr; X xs->datalen = datalen; X xs->resid = datalen; X xs->when_done = (flags & SCSI_NOMASK) X ?(int (*)())0 X :st_done; X xs->done_arg = unit; X xs->done_arg2 = (int)xs; Xretry: xs->error = XS_NOERROR; X xs->bp = 0; X retval = (*(st->sc_sw->scsi_cmd))(xs); X switch(retval) X { X case SUCCESSFULLY_QUEUED: X s = splbio(); X while(!(xs->flags & ITSDONE)) X sleep(xs,PRIBIO+1); X splx(s); X X case HAD_ERROR: X case COMPLETE: X switch(xs->error) X { X case XS_NOERROR: X retval = ESUCCESS; X break; X case XS_SENSE: X retval = (st_interpret_sense(unit,xs)); X /* only useful for reads */ X if (retval) X { /* error... don't care about filemarks */ X st->flags &= ~(ST_AT_FILEMARK X | ST_AT_EOM); X } X else X { X xs->error = XS_NOERROR; X retval = ESUCCESS; X } X break; X case XS_DRIVER_STUFFUP: X retval = EIO; X break; X case XS_TIMEOUT: X if(xs->retries-- ) X { X xs->flags &= ~ITSDONE; X goto retry; X } X retval = EIO; X break; X case XS_BUSY: X if(xs->retries-- ) X { X xs->flags &= ~ITSDONE; X goto retry; X } X retval = EIO; X break; X default: X retval = EIO; X printf("st%d: unknown error category from scsi driver\n" X ,unit); X break; X } X break; X case TRY_AGAIN_LATER: X if(xs->retries-- ) X { X xs->flags &= ~ITSDONE; X goto retry; X } X retval = EIO; X break; X default: X retval = EIO; X } X xs->flags = 0; /* it's free! */ X ststart(unit); X } X else X { X printf("st%d: not set up\n",unit); X return(EINVAL); X } X return(retval); X} X/***************************************************************\ X* Look at the returned sense and act on the error and detirmine * X* The unix error number to pass back... (0 = report no error) * X\***************************************************************/ X Xint st_interpret_sense(unit,xs) Xint unit; Xstruct scsi_xfer *xs; X{ X struct scsi_sense_data *sense; X int key; X int silent = xs->flags & SCSI_SILENT; X X /***************************************************************\ X * If errors are ok, report a success * X \***************************************************************/ X if(xs->flags & SCSI_ERR_OK) return(ESUCCESS); X X /***************************************************************\ X * Get the sense fields and work out what CLASS * X \***************************************************************/ X sense = &(xs->sense); X if(st_debug) X { X int count = 0; X printf("code%x class%x valid%x\n" X ,sense->error_code X ,sense->error_class X ,sense->valid); X printf("seg%x key%x ili%x eom%x fmark%x\n" X ,sense->ext.extended.segment X ,sense->ext.extended.sense_key X ,sense->ext.extended.ili X ,sense->ext.extended.eom X ,sense->ext.extended.filemark); X printf("info: %x %x %x %x followed by %d extra bytes\n" X ,sense->ext.extended.info[0] X ,sense->ext.extended.info[1] X ,sense->ext.extended.info[2] X ,sense->ext.extended.info[3] X ,sense->ext.extended.extra_len); X printf("extra: "); X while(count < sense->ext.extended.extra_len) X { X printf ("%x ",sense->ext.extended.extra_bytes[count++]); X } X printf("\n"); X } X switch(sense->error_class) X { X /***************************************************************\ X * If it's class 7, use the extended stuff and interpret the key * X \***************************************************************/ X case 7: X { X if(sense->ext.extended.eom) X { X st_data[unit].flags |= ST_AT_EOM; X } X X if(sense->ext.extended.filemark) X { X st_data[unit].flags |= ST_AT_FILEMARK; X } X X if(sense->ext.extended.ili) X { X if(sense->valid) X { X /*******************************\ X * In all ili cases, note that * X * the resid is non-0 AND not * X * unchanged. * X \*******************************/ X xs->resid X = ntohl(*((long *)sense->ext.extended.info)); X if(xs->bp) X { X if(xs->resid < 0) X { /* never on block devices */ X /***********************\ X * it's only really bad * X * if we have lost data * X * (the record was * X * bigger than the read) * X \***********************/ X return(EIO); X } X } X } X else X { /* makes no sense.. complain */ X printf("BAD length error?"); X } X }/* there may be some other error. check the rest */ X X key=sense->ext.extended.sense_key; X switch(key) X { X case 0x0: X xs->resid = 0; /* XXX check this */ X return(ESUCCESS); X case 0x1: X if(!silent) X { X printf("st%d: soft error(corrected) ", unit); X if(sense->valid) X { X printf("block no. %d (decimal)\n", X (sense->ext.extended.info[0] <<24)| X (sense->ext.extended.info[1] <<16)| X (sense->ext.extended.info[2] <<8)| X (sense->ext.extended.info[3] )); X } X else X { X printf("\n"); X } X } X xs->resid = 0; /* XXX check this */ X return(ESUCCESS); X case 0x2: X if(!silent) printf("st%d: not ready\n ", unit); X return(ENODEV); X case 0x3: X if(!silent) X { X printf("st%d: medium error ", unit); X if(sense->valid) X { X printf("block no. %d (decimal)\n", X (sense->ext.extended.info[0] <<24)| X (sense->ext.extended.info[1] <<16)| X (sense->ext.extended.info[2] <<8)| X (sense->ext.extended.info[3] )); X } X else X { X printf("\n"); X } X } X return(EIO); X case 0x4: X if(!silent) printf("st%d: non-media hardware failure\n ", X unit); X return(EIO); X case 0x5: X if(!silent) printf("st%d: illegal request\n ", unit); X return(EINVAL); X case 0x6: X if(!silent) printf("st%d: Unit attention.\n ", unit); X st_data[unit].flags &= ~(ST_AT_FILEMARK|ST_AT_EOM); X st_data[unit].flags &= ~ST_INFO_VALID; X if (st_data[unit].flags & ST_OPEN) /* TEMP!!!! */ X return(EIO); X else X return(ESUCCESS); X case 0x7: X if(!silent) X { X printf("st%d: attempted protection violation " X , unit); X if(sense->valid) X { X printf("block no. %d (decimal)\n", X (sense->ext.extended.info[0] <<24)| X (sense->ext.extended.info[1] <<16)| X (sense->ext.extended.info[2] <<8)| X (sense->ext.extended.info[3] )); X } X else X { X printf("\n"); X } X } X return(EACCES); X case 0x8: X if(!silent) X { X printf("st%d: block wrong state (worm)\n " X , unit); X if(sense->valid) X { X printf("block no. %d (decimal)\n", X (sense->ext.extended.info[0] <<24)| X (sense->ext.extended.info[1] <<16)| X (sense->ext.extended.info[2] <<8)| X (sense->ext.extended.info[3] )); X } X else X { X printf("\n"); X } X } X return(EIO); X case 0x9: X if(!silent) printf("st%d: vendor unique\n", X unit); X return(EIO); X case 0xa: X if(!silent) printf("st%d: copy aborted\n ", X unit); X return(EIO); X case 0xb: X if(!silent) printf("st%d: command aborted\n ", X unit); X return(EIO); X case 0xc: X if(!silent) X { X printf("st%d: search returned\n ", unit); X if(sense->valid) X { X printf("block no. %d (decimal)\n", X (sense->ext.extended.info[0] <<24)| X (sense->ext.extended.info[1] <<16)| X (sense->ext.extended.info[2] <<8)| X (sense->ext.extended.info[3] )); X } X else X { X printf("\n"); X } X } X return(ESUCCESS); X case 0xd: X if(!silent) printf("st%d: volume overflow\n ", X unit); X return(ENOSPC); X case 0xe: X if(!silent) X { X printf("st%d: verify miscompare\n ", unit); X if(sense->valid) X { X printf("block no. %d (decimal)\n", X (sense->ext.extended.info[0] <<24)| X (sense->ext.extended.info[1] <<16)| X (sense->ext.extended.info[2] <<8)| X (sense->ext.extended.info[3] )); X } X else X { X printf("\n"); X } X } X return(EIO); X case 0xf: X if(!silent) printf("st%d: unknown error key\n ", X unit); X return(EIO); X } X break; X } X /***************************************************************\ X * If it's NOT class 7, just report it. * X \***************************************************************/ X case 0: X case 1: X case 2: X case 3: X case 4: X case 5: X case 6: X { X if(!silent) printf("st%d: error class %d code %d\n", X unit, X sense->error_class, X sense->error_code); X if(sense->valid) X if(!silent) printf("block no. %d (decimal)\n", X (sense->ext.unextended.blockhi <<16), X + (sense->ext.unextended.blockmed <<8), X + (sense->ext.unextended.blocklow )); X } X return(EIO); X } X} X X#if defined(OSF) X Xstsize(dev_t dev) X{ X printf("stsize() -- not implemented\n"); X return(0); X} X Xstdump() X{ X printf("stdump() -- not implemented\n"); X return(-1); X} X X#endif /* defined(OSF) */ X END-of-scsi/st.c exit -- osynw@terra.oscs.montana.edu | Still trying to find a good reason for nate@cs.montana.edu | these 'computer' things. Personally, work #: (406) 994-4836 | I don't think they'll catch on - home #: (406) 586-0579 | Don Hammerstrom