Return to BSD News archive
Newsgroups: comp.unix.bsd Path: sserve!manuel!munnari.oz.au!network.ucsd.edu!swrinde!cs.utexas.edu!sun-barr!ames!agate!tfs.com!tfs.com!julian From: julian@tfs.com (Julian Elischer) Subject: New scsi system beta3 (part 4 of 10) Message-ID: <1992Oct3.040143.13676@tfs.com> Organization: TRW Financial Systems Date: Sat, 3 Oct 1992 04:01:43 GMT Lines: 1764 # 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/scsi_tape.h # scsi/st.c # echo x - scsi/scsi_tape.h sed 's/^X//' >scsi/scsi_tape.h << 'END-of-scsi/scsi_tape.h' X/* X * HISTORY X * $Log: scsi_tape.h,v $ X * X */ X X/* X * SCSI tape interface description X */ X X/* X * Written by Julian Elischer (julian@tfs.com) X * for TRW Financial Systems. 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 X/* X * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 X */ X X/* X * SCSI command format X */ X X Xstruct scsi_rw_tape X{ X u_char op_code; X u_char fixed:1; X u_char :4; X u_char lun:3; X u_char len[3]; X u_char link:1; X u_char flag:1; X u_char :6; X} rw_tape; X Xstruct scsi_space X{ X u_char op_code; X u_char code:2; X u_char :3; X u_char lun:3; X u_char number[3]; X u_char link:1; X u_char flag:1; X u_char :6; X} space; X#define SP_BLKS 0 X#define SP_FILEMARKS 1 X#define SP_SEQ_FILEMARKS 2 X#define SP_EOM 3 X Xstruct scsi_write_filemarks X{ X u_char op_code; X u_char :5; X u_char lun:3; X u_char number[3]; X u_char link:1; X u_char flag:1; X u_char :6; X} write_filemarks; X Xstruct scsi_rewind X{ X u_char op_code; X u_char immed:1; X u_char :4; X u_char lun:3; X u_char unused[3]; X u_char link:1; X u_char flag:1; X u_char :6; X} rewind; X Xstruct scsi_load X{ X u_char op_code; X u_char immed:1; X u_char :4; X u_char lun:3; X u_char unused[2]; X u_char load:1; X u_char reten:1; X u_char :6; X u_char link:1; X u_char flag:1; X u_char :6; X} load; X#define LD_UNLOAD 0 X#define LD_LOAD 1 X Xstruct scsi_blk_limits X{ X u_char op_code; X u_char :5; X u_char lun:3; X u_char unused[3]; X u_char link:1; X u_char flag:1; X u_char :6; X} blk_limits; X X/* X * Opcodes X */ X X#define REWIND 0x01 X#define READ_BLK_LIMITS 0x05 X#define READ_COMMAND_TAPE 0x08 X#define WRITE_COMMAND_TAPE 0x0a X#define WRITE_FILEMARKS 0x10 X#define SPACE 0x11 X#define LOAD_UNLOAD 0x1b /* same as above */ X X X Xstruct scsi_blk_limits_data X{ X u_char reserved; X u_char max_length_2; /* Most significant */ X u_char max_length_1; X u_char max_length_0; /* Least significant */ X u_char min_length_1; /* Most significant */ X u_char min_length_0; /* Least significant */ X}; X Xstruct scsi_mode_header_tape X{ X u_char data_length; /* Sense data length */ X u_char medium_type; X u_char speed:4; X u_char buf_mode:3; X u_char :1; X u_char blk_desc_len; X}; X END-of-scsi/scsi_tape.h 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 X/* X * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 X */ X X/* X * HISTORY X * $Log: st.c,v $ 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 X#define PAGESIZ 4096 X#define STQSIZE 4 X#define ST_RETRIES 4 X X X#define MODE(z) ( (minor(z) & 0x0F) ) X#define UNIT(z) ( (minor(z) >> 4) ) 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_info_valid[NST]; /* the info about the device is valid */ Xint st_initialized[NST] ; Xint st_debug = 0; X Xint stattach(); Xint st_done(); Xstruct st_data X{ X int flags; 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; /* out scsi lu */ X int blkmin; /* min blk size */ X int blkmax; /* max blk size */ X int numblks; /* nominal blocks capacity */ X int blksiz; /* nominal block size */ X}st_data[NST]; X#define ST_OPEN 0x01 X#define ST_NOREWIND 0x02 X#define ST_WRITTEN 0x04 X#define ST_FIXEDBLOCKS 0x10 X#define ST_AT_FILEMARK 0x20 X X#define ST_PER_ACTION ST_AT_FILEMARK X#define ST_PER_OPEN (ST_OPEN | ST_NOREWIND | 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 unsigned char *tbl; 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",(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 * 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 printf(" st%d: scsi tape drive, %d blocks of %d bytes\n", X unit, st->numblks, st->blksiz); X } X else X { X printf(" st%d: scsi tape 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 st_initialized[unit] = 1; X X#if defined(OSF) X st_window[unit] = (caddr_t)alloc_kva(SECSIZE*256+PAGESIZ); X#endif /* defined(OSF) */ X X return; X X} X X X X/*******************************************************\ X* open the device. * X\*******************************************************/ Xstopen(dev) X{ X int errcode = 0; X int unit,mode; X struct st_data *st; X unit = UNIT(dev); X mode = MODE(dev); X st = st_data + unit; X X /*******************************************************\ X * Check the unit is legal * X \*******************************************************/ X if ( unit >= NST ) X { X errcode = ENXIO; X return(errcode); X } X /*******************************************************\ X * Only allow one at a time * X \*******************************************************/ X if(st->flags & ST_OPEN) X { X errcode = ENXIO; X goto bad; X } X /*******************************************************\ X * Set up the mode flags according to the minor number * X * ensure all open flags are in a known state * X \*******************************************************/ X st->flags &= ~ST_PER_OPEN; X switch(mode) X { X case 2: X case 0: X st->flags &= ~ST_NOREWIND; X break; X case 3: X case 1: X st->flags |= ST_NOREWIND; X break; X default: X printf("st%d: Bad mode (minor number)%d\n",unit,mode); X return(EINVAL); 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 X if (!st_initialized[unit]) X return(ENXIO); X /*******************************************************\ X * Check that it is still responding and ok. * X \*******************************************************/ X X if(scsi_debug & TRACEOPENS) X printf("device is "); X if (!(st_req_sense(unit, 0))) X { X errcode = ENXIO; X if(scsi_debug & TRACEOPENS) X printf("not responding\n"); X goto bad; X } X if(scsi_debug & TRACEOPENS) X printf("ok\n"); X X if(!(st_test_ready(unit,0))) X { X printf("st%d not ready\n",unit); X return(EIO); X } X X if(!st_info_valid[unit]) /* is media new? */ X if(!st_load(unit,LD_LOAD,0)) X { X return(EIO); X } X X if(!st_rd_blk_lim(unit,0)) X { X return(EIO); X } X X if(!st_mode_sense(unit,0)) X { X return(EIO); X } X X if(!st_mode_select(unit,0)) X { X return(EIO); X } X X st_info_valid[unit] = TRUE; X X st_prevent(unit,PR_PREVENT,0); /* who cares if it fails? */ X X /*******************************************************\ X * Load the physical device parameters * X \*******************************************************/ X if(scsi_debug & TRACEOPENS) X printf("Params loaded "); X X st->flags |= ST_OPEN; X return(errcode); Xbad: X return(errcode); 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#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 goto done; X } X X /*******************************************************\ X * Odd sized request on fixed drives are verboten * X \*******************************************************/ X if((st_data[unit].flags & ST_FIXEDBLOCKS) X && bp->b_bcount % st_data[unit].blkmin) X { X printf("st%d: bad request, must be multiple of %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 \*******************************************************/ 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 } 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 return; 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->blkmin,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 bp->b_flags |= B_ERROR; X /* bp->b_error = retval; */ X } X bp->b_error = retval; X bp->b_resid = xs->resid; X if((st_data[unit].flags & ST_FIXEDBLOCKS)) X { X bp->b_resid *= st_data[unit].blkmin; X } X if(bp->b_resid == bp->b_bcount) X { /* we are reporting an EOF , lose flag */ X st_data[unit].flags &= ~ST_AT_FILEMARK; 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 /* -may not have a buf!- */ 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 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 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 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 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/*******************************************************\ 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 else X return(TRUE); X} X 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 return(FALSE); X } else 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_info_valid[unit]) goto done; 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_info_valid[unit] = FALSE; X return(FALSE); X } X if (st_debug) X { X printf(" (%d <= blksiz <= %d\n) ", X b2tol(scsi_blkl.min_length), X _3btol(&scsi_blkl.max_length_2)); X } X st->blkmin = b2tol(scsi_blkl.min_length); X st->blkmax = _3btol(&scsi_blkl.max_length_2); X Xdone: X if(st->blkmin && (st->blkmin == st->blkmax)) X { X st->flags |= ST_FIXEDBLOCKS; X } X return(TRUE); 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\*******************************************************/ Xst_mode_sense(unit, flags) Xint unit,flags; X{ X struct scsi_mode_sense scsi_cmd; X struct X { X struct scsi_mode_header_tape header; X struct blk_desc blk_desc; X }scsi_sense; X struct st_data *st = st_data + unit; X X /*******************************************************\ X * First check if we have it all loaded * X \*******************************************************/ X if (st_info_valid[unit]) return(TRUE); X /*******************************************************\ X * First do a mode sense * X \*******************************************************/ X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = MODE_SENSE; X scsi_cmd.length = sizeof(scsi_sense); X /*******************************************************\ X * do the command, but we don't need the results * X * just print them for our interest's sake * X \*******************************************************/ X if (st_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X &scsi_sense, X sizeof(scsi_sense), 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_info_valid[unit] = FALSE; X return(FALSE); X } X if (st_debug) X { X printf("unit %d: %d blocks, (%d bytes each)", X unit, X _3btol(&scsi_sense.blk_desc.nblocks), X _3btol(&scsi_sense.blk_desc.blklen)); X } X st->numblks = _3btol(&scsi_sense.blk_desc.nblocks); X st->blksiz = _3btol(&scsi_sense.blk_desc.blklen); X return(TRUE); 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\*******************************************************/ Xst_mode_select(unit, flags) Xint unit,flags; X{ X struct scsi_mode_select scsi_cmd; X struct X { X struct scsi_mode_header_tape header; X struct blk_desc blk_desc; X }dat; X struct st_data *st = st_data + unit; X /*******************************************************\ X * First check if we have it all loaded * X \*******************************************************/ X if (st_info_valid[unit]) return(TRUE); X /*******************************************************\ X * Set up for a mode select * X \*******************************************************/ X bzero(&dat, sizeof(dat)); X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = MODE_SELECT; X scsi_cmd.length = sizeof(dat); X dat.header.blk_desc_len = sizeof(struct blk_desc); X dat.header.buf_mode = 1; X if(st->flags & ST_FIXEDBLOCKS) X { X lto3b( st->blkmin , dat.blk_desc.blklen); X } X/* lto3b( st->numblks , dat.blk_desc.nblocks); use defaults!!!! X lto3b( st->blksiz , dat.blk_desc.blklen); X*/ X /*******************************************************\ X * do the command * X \*******************************************************/ X if (st_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X &dat, X sizeof(dat), 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_info_valid[unit] = FALSE; 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; 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_info_valid[unit] = FALSE; 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 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_info_valid[unit] = FALSE; 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 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 st_info_valid[unit] = FALSE; X return(FALSE); 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_info_valid[unit] = FALSE; 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 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_info_valid[unit] = FALSE; 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 while(!(xs->flags & ITSDONE)) X sleep(xs,PRIBIO+1); 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 st->flags &= ST_AT_FILEMARK; /* only useful for reads */ 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 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 key=sense->ext.extended.sense_key; X if(sense->ext.extended.ili) X { X if(!silent) X { X printf("length error "); X } X if(sense->valid) X { X xs->resid = ntohl(*((long *)sense->ext.extended.info)); X if(xs->bp) X { X xs->bp->b_flags |= B_ERROR; X return(ESUCCESS); X } X } X } X if(sense->ext.extended.eom) X if(!silent) printf("end of medium "); X X if(sense->ext.extended.filemark) X { X st_data[unit].flags |= ST_AT_FILEMARK; X X if(!silent) printf("filemark "); X } X X if(st_debug) X { 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 %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n" X ,sense->ext.extended.extra_bytes[0] X ,sense->ext.extended.extra_bytes[1] X ,sense->ext.extended.extra_bytes[2] X ,sense->ext.extended.extra_bytes[3] X ,sense->ext.extended.extra_bytes[4] X ,sense->ext.extended.extra_bytes[5] X ,sense->ext.extended.extra_bytes[6] X ,sense->ext.extended.extra_bytes[7] X ,sense->ext.extended.extra_bytes[8] X ,sense->ext.extended.extra_bytes[9] X ,sense->ext.extended.extra_bytes[10] X ,sense->ext.extended.extra_bytes[11] X ,sense->ext.extended.extra_bytes[12] X ,sense->ext.extended.extra_bytes[13] X ,sense->ext.extended.extra_bytes[14] X ,sense->ext.extended.extra_bytes[15]); X X } X switch(key) X { X case 0x0: 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 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_info_valid[unit] = FALSE; 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