/* * Written by Julian Elischer (julian@tfs.com) * for TRW Financial Systems for use under the MACH(2.5) operating system. * Hacked by Theo de Raadt * * TRW Financial Systems, in accordance with their agreement with Carnegie * Mellon University, makes this software available to CMU to distribute * or use in any manner that they see fit as long as this message is kept with * the software. For this reason TFS also grants any other persons or * organisations permission to use or modify this software. * * TFS supplies this software to be publicly redistributed * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * * $Id: st.c,v 1.14.2.1 1993/07/26 21:54:41 briggs Exp $ */ /* * To do: * work out some better way of guessing what a good timeout is going * to be depending on whether we expect to retension or not. * */ #include "st.h" #include "sys/types.h" #include "sys/param.h" #include "sys/systm.h" #include "sys/errno.h" #include "sys/malloc.h" #include "sys/ioctl.h" #include "sys/buf.h" #include "sys/proc.h" #include "sys/user.h" #include "sys/mtio.h" #include "sys/dkbad.h" #include "sys/disklabel.h" #include "scsi/scsi_all.h" #include "scsi/scsi_tape.h" #include "scsi/scsiconf.h" #include "scsi/stdefs.h" long int ststrats, stqueues; #define ST_RETRIES 4 #define UNITSHIFT 4 #define MODE(z) ((minor(z) & 0x03)) #define DSTY(z) (((minor(z) >> 2) & 0x03)) #define UNIT(z) ((minor(z) >> UNITSHIFT)) #undef NST #define NST ( makedev(1,0) >> UNITSHIFT) #define DSTY_QIC120 3 #define DSTY_QIC150 2 #define DSTY_QIC525 1 #define QIC120 0x0f #define QIC150 0x10 #define QIC525 0x11 #define ESUCCESS 0 int st_debug = 0; struct st_data *st_data[NST]; static int next_st_unit = 0; /* * The routine called by the low level scsi routine when it discovers * A device suitable for this driver */ int stattach(int masunit, struct scsi_switch *sw, int physid, int *unit) { struct st_data *st; unsigned char *tbl; int targ, lun, i; targ = physid >> 3; lun = physid & 7; /*printf("stattach: st%d at %s%d target %d lun %d\n", *unit, sw->name, masunit, targ, lun);*/ if(*unit==-1) { for(i=0; i= NST || *unit==-1) return 0; if(st_data[*unit]) return 0; st = st_data[*unit] = (struct st_data *)malloc(sizeof *st, M_TEMP, M_NOWAIT); bzero(st, sizeof *st); st->sc_sw = sw; st->ctlr = masunit; st->targ = targ; st->lu = lun; /* * Use the subdriver to request information regarding * the drive. We cannot use interrupts yet, so the * request must specify this. */ if( st_mode_sense(*unit, SCSI_NOSLEEP | SCSI_NOMASK | SCSI_SILENT)) printf("st%d at %s%d targ %d lun %d: %d blocks of %d bytes\n", *unit, sw->name, masunit, targ, lun, st->numblks, st->blksiz); else printf("st%d at %s%d targ %d lun %d: offline\n", *unit, sw->name, masunit, targ, lun); /* * Set up the bufs for this device */ st->buf_queue.b_active = 0; st->buf_queue.b_actf = 0; st->buf_queue.b_actl = 0; st->initialized = 1; return 1; } /* * open the device. */ int stopen(dev_t dev) { int errcode = 0; int unit, mode, dsty; int dsty_code; struct st_data *st; unit = UNIT(dev); mode = MODE(dev); dsty = DSTY(dev); st = st_data[unit]; /* * Check the unit is legal */ if( unit >= NST ) return ENXIO; if(!st) return ENXIO; /* * Only allow one at a time */ if(st->flags & ST_OPEN) { errcode = EBUSY; goto bad; } /* * Set up the mode flags according to the minor number * ensure all open flags are in a known state */ st->flags &= ~ST_PER_OPEN; switch(mode) { case 2: case 0: st->flags &= ~ST_NOREWIND; break; case 3: case 1: st->flags |= ST_NOREWIND; break; default: printf("st%d: bad mode (minor number) %d\n", unit, mode); return EINVAL; } /* * Check density code: 0 is drive default */ switch(dsty) { case 0: dsty_code = 0; break; case DSTY_QIC120: dsty_code = QIC120; break; case DSTY_QIC150: dsty_code = QIC150; break; case DSTY_QIC525: dsty_code = QIC525; break; default: printf("st%d: bad density (minor number) %d\n", unit, dsty); return EINVAL; } if(scsi_debug & (PRINTROUTINES | TRACEOPENS)) printf("st%d: open dev=0x%x (unit %d (of %d))\n", unit, dev, NST); /* * Make sure the device has been initialised */ if (!st->initialized) { /*printf("st%d: uninitialized\n", unit);*/ return ENXIO; } /* * Check that it is still responding and ok. */ if (!(st_req_sense(unit, 0))) { errcode = EIO; if(scsi_debug & TRACEOPENS) printf("st%d: not responding\n", unit); goto bad; } if(scsi_debug & TRACEOPENS) printf("st%d: is responding\n", unit); if(!(st_test_ready(unit, 0))) { printf("st%d: not ready\n", unit); return EIO; } if(!st->info_valid) /* is media new? */ if(!st_load(unit, LD_LOAD, 0)) return EIO; if(!st_rd_blk_lim(unit, 0)) return EIO; if(!st_mode_sense(unit, 0)) return EIO; if(!st_mode_select(unit, 0, dsty_code)) return EIO; st->info_valid = TRUE; st_prevent(unit, PR_PREVENT, 0); /* who cares if it fails? */ /* * Load the physical device parameters */ if(scsi_debug & TRACEOPENS) printf("st%d: params ", unit); st->flags |= ST_OPEN; bad: return errcode; } /* * close the device.. only called if we are the LAST * occurence of an open device */ int stclose(dev_t dev) { unsigned char unit, mode; struct st_data *st; unit = UNIT(dev); mode = MODE(dev); st = st_data[unit]; if(scsi_debug & TRACEOPENS) printf("st%d: close\n", unit); if(st->flags & ST_WRITTEN) st_write_filemarks(unit, 1, 0); st->flags &= ~ST_WRITTEN; switch(mode) { case 0: st_rewind(unit, FALSE, SCSI_SILENT); st_prevent(unit, PR_ALLOW, SCSI_SILENT); break; case 1: st_prevent(unit, PR_ALLOW, SCSI_SILENT); break; case 2: st_rewind(unit, FALSE, SCSI_SILENT); st_prevent(unit, PR_ALLOW, SCSI_SILENT); st_load(unit, LD_UNLOAD, SCSI_SILENT); break; case 3: st_prevent(unit, PR_ALLOW, SCSI_SILENT); st_load(unit, LD_UNLOAD, SCSI_SILENT); break; default: printf("st%d: bad mode (minor number) %d (how's it open?)\n", unit, mode); return EINVAL; } st->flags &= ~ST_PER_OPEN; return 0; } /* * trim the size of the transfer if needed, * called by physio * basically the smaller of our min and the scsi driver's* * minphys */ void stminphys(struct buf *bp) { (*(st_data[UNIT(bp->b_dev)]->sc_sw->scsi_minphys))(bp); } /* * Actually translate the requested transfer into * one the physical driver can understand * The transfer is described by a buf and will include * only one physical transfer. */ int ststrategy(struct buf *bp) { struct st_data *st; struct buf *dp; unsigned char unit; unsigned int opri; if (bp->b_bcount == 0) goto done; ststrats++; unit = UNIT((bp->b_dev)); st = st_data[unit]; if(scsi_debug & PRINTROUTINES) printf("\nststrategy "); if(scsi_debug & SHOWREQUESTS) printf("st%d: %d bytes @ blk%d\n", unit, bp->b_bcount, bp->b_blkno); /* * Odd sized request on fixed drives are verboten */ if((st->flags & ST_FIXEDBLOCKS) && bp->b_bcount % st->blkmin) { printf("st%d: bad request, must be multiple of %d\n", unit, st->blkmin); bp->b_error = EIO; goto bad; } stminphys(bp); opri = splbio(); dp = &st->buf_queue; /* * Place it in the queue of disk activities for this tape* * at the end */ while ( dp->b_actf) dp = dp->b_actf; dp->b_actf = bp; bp->b_actf = NULL; /* * Tell the device to get going on the transfer if it's * not doing anything, otherwise just wait for completion* */ ststart(unit); splx(opri); return; bad: bp->b_flags |= B_ERROR; done: /* * Correctly set the buf to indicate a completed xfer */ iodone(bp); return; } /* * ststart looks to see if there is a buf waiting for the device * and that the device is not already busy. If both are true, * It deques the buf and creates a scsi command to perform the * transfer in the buf. The transfer request will call st_done * on completion, which will in turn call this routine again * so that the next queued transfer is performed. * The bufs are queued by the strategy routine (ststrategy) * * This routine is also called after other non-queued requests * have been made of the scsi driver, to ensure that the queue * continues to be drained. */ /* ststart() is called at splbio */ int ststart(int unit) { struct st_data *st = st_data[unit]; register struct buf *bp = 0, *dp; struct scsi_rw_tape cmd; struct scsi_xfer *xs; int drivecount, blkno, nblk; if(scsi_debug & PRINTROUTINES) printf("st%d: start\n", unit); /* * See if there is a buf to do and we are not already * doing one */ xs = &st->scsi_xfer; if(xs->flags & INUSE) return; /* unit already underway */ trynext: if(st->blockwait) { wakeup((caddr_t)&st->blockwait); return; } dp = &st->buf_queue; if ((bp = dp->b_actf) != NULL) dp->b_actf = bp->b_actf; else return; xs->flags = INUSE; /* Now ours */ /* * We have a buf, now we should move the data into * a scsi_xfer definition and try start it */ /* * If we are at a filemark but have not reported it yet * then we should report it now */ if(st->flags & ST_AT_FILEMARK) { bp->b_error = 0; bp->b_flags |= B_ERROR; /* EOF*/ st->flags &= ~ST_AT_FILEMARK; biodone(bp); xs->flags = 0; /* won't need it now */ goto trynext; } /* * If we are at EOM but have not reported it yet * then we should report it now */ if(st->flags & ST_AT_EOM) { bp->b_error = EIO; bp->b_flags |= B_ERROR; st->flags &= ~ST_AT_EOM; biodone(bp); xs->flags = 0; /* won't need it now */ goto trynext; } /* * Fill out the scsi command */ bzero(&cmd, sizeof(cmd)); if((bp->b_flags & B_READ) == B_WRITE) { st->flags |= ST_WRITTEN; xs->flags |= SCSI_DATA_OUT; } else xs->flags |= SCSI_DATA_IN; cmd.op_code = (bp->b_flags & B_READ) ? READ_COMMAND_TAPE : WRITE_COMMAND_TAPE; /* * Handle "fixed-block-mode" tape drives by using the * * block count instead of the length. */ if(st->flags & ST_FIXEDBLOCKS) { cmd.fixed = 1; lto3b(bp->b_bcount/st->blkmin, cmd.len); } else lto3b(bp->b_bcount, cmd.len); /* * Fill out the scsi_xfer structure * Note: we cannot sleep as we may be an interrupt */ xs->flags |= SCSI_NOSLEEP; xs->adapter = st->ctlr; xs->targ = st->targ; xs->lu = st->lu; xs->retries = 1; /* can't retry on tape*/ xs->timeout = 200000; /* allow 200 secs for retension */ xs->cmd = (struct scsi_generic *)&cmd; xs->cmdlen = sizeof(cmd); xs->data = (u_char *)bp->b_un.b_addr; xs->datalen = bp->b_bcount; xs->resid = bp->b_bcount; xs->when_done = st_done; xs->done_arg = unit; xs->done_arg2 = (int)xs; xs->error = XS_NOERROR; xs->bp = bp; #if defined(OSF) || defined(FIX_ME) if (bp->b_flags & B_PHYS) { xs->data = (u_char*)map_pva_kva(bp->b_proc, bp->b_un.b_addr, bp->b_bcount, st_window[unit], (bp->b_flags&B_READ)?B_WRITE:B_READ); } else xs->data = (u_char*)bp->b_un.b_addr; #endif /* defined(OSF) */ if ( (*(st->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED) { printf("st%d: oops not queued", unit); xs->error = XS_DRIVER_STUFFUP; st_done(unit, xs); } stqueues++; } /* * This routine is called by the scsi interrupt when * the transfer is complete. */ int st_done(int unit, struct scsi_xfer *xs) { struct st_data *st = st_data[unit]; struct buf *bp; int retval; if(scsi_debug & PRINTROUTINES) printf("st%d: done\n", unit); if (! (xs->flags & INUSE)) panic("scsi_xfer not in use!"); if(bp = xs->bp) { switch(xs->error) { case XS_NOERROR: bp->b_flags &= ~B_ERROR; bp->b_error = 0; bp->b_resid = 0; break; case XS_SENSE: retval = st_interpret_sense(unit, xs); if(retval) { /* * We have a real error, the bit should * be set to indicate this. The return * value will contain the unix error code* * that the error interpretation routine * thought was suitable, so pass this * value back in the buf structure. * Furthermore we return information * saying that no data was transferred */ bp->b_flags |= B_ERROR; bp->b_error = retval; bp->b_resid = bp->b_bcount; st->flags &= ~(ST_AT_FILEMARK|ST_AT_EOM); } else if(xs->resid && ( xs->resid != xs->datalen )) { /* * Here we have the tricky part.. * We successfully read less data than * we requested. (but not 0) *------for variable blocksize tapes:----* * UNDER 386BSD: * We should legitimatly have the error * bit set, with the error value set to * zero.. This is to indicate to the * physio code that while we didn't get * as much information as was requested, * we did reach the end of the record * and so physio should not call us * again for more data... we have it all * SO SET THE ERROR BIT! * * UNDER MACH (CMU) and NetBSD: * To indicate the same as above, we * need only have a non 0 resid that is * less than the b_bcount, but the * ERROR BIT MUST BE CLEAR! (sigh) * * UNDER OSF1: * To indicate the same as above, we * need to have a non 0 resid that is * less than the b_bcount, but the * ERROR BIT MUST BE SET! (gasp)(sigh) * *-------for fixed blocksize device------* * We could have read some successful * records before hitting * the EOF or EOT. These must be passed * to the user, before we report the * EOx. Only if there is no data for the * user do we report it now. (via an EIO * for EOM and resid == count for EOF). * We will report the EOx NEXT time.. */ bp->b_flags &= ~B_ERROR; bp->b_error = 0; bp->b_resid = xs->resid; if((st->flags & ST_FIXEDBLOCKS)) { bp->b_resid *= st->blkmin; if( (st->flags & ST_AT_EOM) && (bp->b_resid == bp->b_bcount)) { bp->b_error = EIO; st->flags &= ~ST_AT_EOM; } } xs->error = XS_NOERROR; break; } else { /* * We have come out of the error handler * with no error code.. we have also * not had an ili (would have gone to * the previous clause). Now we need to * distiguish between succesful read of * no data (EOF or EOM) and successfull * read of all requested data. * At least all o/s agree that: * 0 bytes read with no error is EOF * 0 bytes read with an EIO is EOM */ bp->b_resid = bp->b_bcount; if(st->flags & ST_AT_FILEMARK) { st->flags &= ~ST_AT_FILEMARK; bp->b_flags &= ~B_ERROR; bp->b_error = 0; break; } if(st->flags & ST_AT_EOM) { bp->b_flags |= B_ERROR; bp->b_error = EIO; st->flags &= ~ST_AT_EOM; break; } printf("st%d: error ignored\n", unit); } break; case XS_TIMEOUT: printf("st%d: timeout\n", unit); break; case XS_BUSY: /* should retry -- how? */ /* * SHOULD put buf back at head of queue * and decrement retry count in (*xs) * HOWEVER, this should work as a kludge */ if(xs->retries--) { xs->flags &= ~ITSDONE; xs->error = XS_NOERROR; if ( (*(st->sc_sw->scsi_cmd))(xs) == SUCCESSFULLY_QUEUED) { /* don't wake the job, ok? */ return; } printf("st%d: device busy\n"); xs->flags |= ITSDONE; } case XS_DRIVER_STUFFUP: bp->b_flags |= B_ERROR; bp->b_error = EIO; break; default: printf("st%d: unknown error category %d from scsi driver\n", unit, xs->error); } biodone(bp); xs->flags = 0; /* no longer in use */ ststart(unit); /* If there's another waiting.. do it */ } else wakeup((caddr_t)xs); } /* * Perform special action on behalf of the user * Knows about the internals of this device */ int stioctl(dev_t dev, int cmd, caddr_t arg, int mode) { struct st_data *st; struct mtop *mt; struct mtget *g; unsigned int opri; unsigned char unit; register i, j; int errcode=0, number, flags, ret; /* * Find the device that the user is talking about */ flags = 0; /* give error messages, act on errors etc. */ unit = UNIT(dev); st = st_data[unit]; if(unit >= NST) return ENXIO; if(!st) return ENXIO; switch(cmd) { default: return EINVAL; case MTIOCGET: g = (struct mtget *)arg; bzero(g, sizeof *g); g->mt_type = 0x7; /* Ultrix compat */ ret=TRUE; break; case MTIOCTOP: mt = (struct mtop *)arg; if (st_debug) printf("[sctape_sstatus: %x %x]\n", mt->mt_op, mt->mt_count); /* compat: in U*x it is a short */ number = mt->mt_count; switch ((short)(mt->mt_op)) { case MTWEOF: /* write an end-of-file record */ ret = st_write_filemarks(unit, number, flags); st->flags &= ~ST_WRITTEN; break; case MTFSF: /* forward space file */ ret = st_space(unit, number, SP_FILEMARKS, flags); break; case MTBSF: /* backward space file */ ret = st_space(unit, -number, SP_FILEMARKS, flags); break; case MTFSR: /* forward space record */ ret = st_space(unit, number, SP_BLKS, flags); break; case MTBSR: /* backward space record */ ret = st_space(unit, -number, SP_BLKS, flags); break; case MTREW: /* rewind */ ret = st_rewind(unit, FALSE, flags); break; case MTOFFL: /* rewind and put the drive offline */ if((ret = st_rewind(unit, FALSE, flags))) { st_prevent(unit, PR_ALLOW, 0); ret = st_load(unit, LD_UNLOAD, flags); } else printf("st%d: rewind failed; unit still loaded\n"); break; case MTNOP: /* no operation, sets status only */ case MTCACHE: /* enable controller cache */ case MTNOCACHE: /* disable controller cache */ ret = TRUE; break; default: return EINVAL; } break; case MTIOCIEOT: case MTIOCEEOT: ret=TRUE; break; } return ret ? ESUCCESS : EIO; } /* * Check with the device that it is ok, (via scsi driver)* */ int st_req_sense(int unit, int flags) { struct scsi_sense_data sense; struct scsi_sense scsi_cmd; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = REQUEST_SENSE; scsi_cmd.length = sizeof(sense); if (st_scsi_cmd(unit, (struct scsi_generic *)&scsi_cmd, sizeof(scsi_cmd), (u_char *)&sense, sizeof(sense), 100000, flags | SCSI_DATA_IN) != 0) return FALSE; else return TRUE; } /* * Get scsi driver to send a "are you ready" command */ int st_test_ready(int unit, int flags) { struct scsi_test_unit_ready scsi_cmd; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = TEST_UNIT_READY; if (st_scsi_cmd(unit, (struct scsi_generic *)&scsi_cmd, sizeof(scsi_cmd), (u_char *)0, 0, 100000, flags) != 0) return FALSE; else return TRUE; } #ifdef __STDC__ #define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) #else #define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 ) #endif /* * Ask the drive what it's min and max blk sizes are. */ int st_rd_blk_lim(int unit, int flags) { struct st_data *st = st_data[unit]; struct scsi_blk_limits scsi_cmd; struct scsi_blk_limits_data scsi_blkl; st = st_data[unit]; /* * First check if we have it all loaded */ if (st->info_valid) goto done; /* * do a 'Read Block Limits' */ bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = READ_BLK_LIMITS; /* * do the command, update the global values */ if (st_scsi_cmd(unit, (struct scsi_generic *)&scsi_cmd, sizeof(scsi_cmd), (u_char *)&scsi_blkl, sizeof(scsi_blkl), 5000, flags | SCSI_DATA_IN) != 0) { if(!(flags & SCSI_SILENT)) printf("st%d: read block limits failed\n", unit); st->info_valid = FALSE; return FALSE; } if (st_debug) printf("st%d: block size min %d max %d\n", unit, b2tol(scsi_blkl.min_length), _3btol(&scsi_blkl.max_length_2)); st->blkmin = b2tol(scsi_blkl.min_length); st->blkmax = _3btol(&scsi_blkl.max_length_2); done: if(st->blkmin && (st->blkmin == st->blkmax)) st->flags |= ST_FIXEDBLOCKS; return TRUE; } /* * Get the scsi driver to send a full inquiry to the * device and use the results to fill out the global * parameter structure. */ int st_mode_sense(int unit, int flags) { struct st_data *st = st_data[unit]; struct scsi_mode_sense scsi_cmd; struct { struct scsi_mode_header_tape header; struct blk_desc blk_desc; } scsi_s; /* * First check if we have it all loaded */ if(st->info_valid) return TRUE; /* * First do a mode sense */ bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = MODE_SENSE; scsi_cmd.length = sizeof(scsi_s); /* * do the command, but we don't need the results * just print them for our interest's sake */ if (st_scsi_cmd(unit, (struct scsi_generic *)&scsi_cmd, sizeof(scsi_cmd), (u_char *)&scsi_s, sizeof(scsi_s), 5000, flags | SCSI_DATA_IN) != 0) { if(!(flags & SCSI_SILENT)) printf("st%d: mode sense failed\n", unit); st->info_valid = FALSE; return FALSE; } if (st_debug) printf("st%d: %d blocks of %d bytes, write %s, %sbuffered\n", unit, _3btol((u_char *)&scsi_s.blk_desc.nblocks), _3btol((u_char *)&scsi_s.blk_desc.blklen), scsi_s.header.write_protected ? "protected" : "enabled", scsi_s.header.buf_mode ? "" : "un"); st->numblks = _3btol((u_char *)&scsi_s.blk_desc.nblocks); st->blksiz = _3btol((u_char *)&scsi_s.blk_desc.blklen); return TRUE; } /* * Get the scsi driver to send a full inquiry to the * device and use the results to fill out the global * parameter structure. */ int st_mode_select(int unit, int flags, int dsty_code) { struct st_data *st = st_data[unit]; struct scsi_mode_select scsi_cmd; struct { struct scsi_mode_header_tape header; struct blk_desc blk_desc; } dat; /* * Set up for a mode select */ bzero(&dat, sizeof(dat)); bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = MODE_SELECT; scsi_cmd.length = sizeof(dat); dat.header.blk_desc_len = sizeof(struct blk_desc); dat.header.buf_mode = 1; dat.blk_desc.density = dsty_code; if(st->flags & ST_FIXEDBLOCKS) lto3b(st->blkmin, dat.blk_desc.blklen); /* lto3b( st->numblks, dat.blk_desc.nblocks); use defaults!!!! lto3b( st->blksiz, dat.blk_desc.blklen); */ /* * do the command */ if (st_scsi_cmd(unit, (struct scsi_generic *)&scsi_cmd, sizeof(scsi_cmd), (u_char *)&dat, sizeof(dat), 5000, flags | SCSI_DATA_OUT) != 0) { if(!(flags & SCSI_SILENT)) printf("st%d: mode select failed\n", unit); #if 0 st->info_valid = FALSE; return FALSE; #endif } return TRUE; } /* * skip N blocks/filemarks/seq filemarks/eom */ int st_space(int unit, int number, int what, int flags) { struct st_data *st = st_data[unit]; struct scsi_space scsi_cmd; /* if we are at a filemark now, we soon won't be*/ st->flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = SPACE; scsi_cmd.code = what; lto3b(number, scsi_cmd.number); if (st_scsi_cmd(unit, (struct scsi_generic *)&scsi_cmd, sizeof(scsi_cmd), (u_char *)0, 0, 600000, flags) != 0) { if(!(flags & SCSI_SILENT)) printf("st%d: %s space failed\n", unit, (number > 0) ? "forward" : "backward"); st->info_valid = FALSE; return FALSE; } return TRUE; } /* * write N filemarks */ int st_write_filemarks(int unit, int number, int flags) { struct st_data *st = st_data[unit]; struct scsi_write_filemarks scsi_cmd; st->flags &= ~(ST_AT_FILEMARK); bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = WRITE_FILEMARKS; lto3b(number, scsi_cmd.number); if (st_scsi_cmd(unit, (struct scsi_generic *)&scsi_cmd, sizeof(scsi_cmd), (u_char *)0, 0, 100000, flags) != 0) { if(!(flags & SCSI_SILENT)) printf("st%d: write file marks failed\n", unit); st->info_valid = FALSE; return FALSE; } return TRUE; } /* * load /unload (with retension if true) */ int st_load(int unit, int type, int flags) { struct st_data *st = st_data[unit]; struct scsi_load scsi_cmd; st->flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = LOAD_UNLOAD; scsi_cmd.load=type; if (type == LD_LOAD) { /*scsi_cmd.reten=TRUE;*/ scsi_cmd.reten=FALSE; } else { scsi_cmd.reten=FALSE; } if (st_scsi_cmd(unit, (struct scsi_generic *)&scsi_cmd, sizeof(scsi_cmd), (u_char *)0, 0, 30000, flags) != 0) { if(!(flags & SCSI_SILENT)) printf("st%d: %s failed\n", unit, type == LD_LOAD ? "load" : "unload"); st->info_valid = FALSE; return FALSE; } return TRUE; } /* * Prevent or allow the user to remove the tape */ int st_prevent(int unit, int type, int flags) { struct st_data *st = st_data[unit]; struct scsi_prevent scsi_cmd; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = PREVENT_ALLOW; scsi_cmd.prevent=type; if (st_scsi_cmd(unit, (struct scsi_generic *)&scsi_cmd, sizeof(scsi_cmd), (u_char *)0, 0, 5000, flags) != 0) { if(!(flags & SCSI_SILENT)) printf("st%d: %s failed\n", unit, type == PR_PREVENT ? "prevent" : "allow"); st->info_valid = FALSE; return FALSE; } return TRUE; } /* * Rewind the device */ int st_rewind(int unit, int immed, int flags) { struct st_data *st = st_data[unit]; struct scsi_rewind scsi_cmd; st->flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.op_code = REWIND; scsi_cmd.immed=immed; if (st_scsi_cmd(unit, (struct scsi_generic *)&scsi_cmd, sizeof(scsi_cmd), (u_char *)0, 0, immed?5000:300000, flags) != 0) { if(!(flags & SCSI_SILENT)) printf("st%d: rewind failed\n", unit); st->info_valid = FALSE; return FALSE; } return TRUE; } /* * ask the scsi driver to perform a command for us. * Call it through the switch table, and tell it which * sub-unit we want, and what target and lu we wish to * talk to. Also tell it where to find the command * how long int is. * Also tell it where to read/write the data, and how * long the data is supposed to be */ int st_scsi_cmd(int unit, struct scsi_generic *scsi_cmd, int cmdlen, u_char *data_addr, int datalen, int timeout, int flags) { struct st_data *st = st_data[unit]; struct scsi_xfer *xs; int retval, s; if(scsi_debug & PRINTROUTINES) printf("\nst_scsi_cmd%d ", unit); if(!st->sc_sw) { printf("st%d: not set up\n", unit); return EINVAL; } xs = &st->scsi_xfer; if(!(flags & SCSI_NOMASK)) s = splbio(); st->blockwait++; /* there is someone waiting */ while (xs->flags & INUSE) sleep((caddr_t)&st->blockwait, PRIBIO+1); st->blockwait--; xs->flags = INUSE; if(!(flags & SCSI_NOMASK)) splx(s); /* * Fill out the scsi_xfer structure */ xs->flags |= flags; xs->adapter = st->ctlr; xs->targ = st->targ; xs->lu = st->lu; xs->retries = ST_RETRIES; xs->timeout = timeout; xs->cmd = scsi_cmd; xs->cmdlen = cmdlen; xs->data = data_addr; xs->datalen = datalen; xs->resid = datalen; xs->when_done = (flags & SCSI_NOMASK) ? (int (*)())0 : st_done; xs->done_arg = unit; xs->done_arg2 = (int)xs; retry: xs->error = XS_NOERROR; xs->bp = 0; retval = (*(st->sc_sw->scsi_cmd))(xs); switch(retval) { case SUCCESSFULLY_QUEUED: s = splbio(); while(!(xs->flags & ITSDONE)) sleep((caddr_t)xs,PRIBIO+1); splx(s); case HAD_ERROR: case COMPLETE: switch(xs->error) { case XS_NOERROR: retval = ESUCCESS; break; case XS_SENSE: retval = st_interpret_sense(unit, xs); /* only useful for reads */ if (retval) st->flags &= ~(ST_AT_FILEMARK | ST_AT_EOM); else { xs->error = XS_NOERROR; retval = ESUCCESS; } break; case XS_DRIVER_STUFFUP: retval = EIO; break; case XS_TIMEOUT: case XS_BUSY: if(xs->retries-- ) { xs->flags &= ~ITSDONE; goto retry; } retval = EIO; break; default: retval = EIO; printf("st%d: unknown error category %d from scsi driver\n", unit, xs->error); break; } break; case TRY_AGAIN_LATER: if(xs->retries--) { xs->flags &= ~ITSDONE; goto retry; } retval = EIO; break; default: retval = EIO; } xs->flags = 0; /* it's free! */ ststart(unit); return retval; } /* * Look at the returned sense and act on the error and detirmine * The unix error number to pass back... (0 = report no error) */ int st_interpret_sense(int unit, struct scsi_xfer *xs) { struct st_data *st = st_data[unit]; struct scsi_sense_data *sense; int silent = xs->flags & SCSI_SILENT, key; /* * If errors are ok, report a success */ if(xs->flags & SCSI_ERR_OK) return ESUCCESS; /* * Get the sense fields and work out what CLASS */ sense = &(xs->sense); if(st_debug) { int count = 0; printf("code%x class%x valid%x\n", sense->error_code, sense->error_class, sense->valid); printf("seg%x key%x ili%x eom%x fmark%x\n", sense->ext.extended.segment, sense->ext.extended.sense_key, sense->ext.extended.ili, sense->ext.extended.eom, sense->ext.extended.filemark); printf("info: %x %x %x %x followed by %d extra bytes\n", sense->ext.extended.info[0], sense->ext.extended.info[1], sense->ext.extended.info[2], sense->ext.extended.info[3], sense->ext.extended.extra_len); printf("extra: "); while(count < sense->ext.extended.extra_len) printf("%x ", sense->ext.extended.extra_bytes[count++]); printf("\n"); } switch(sense->error_class) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: if(!silent) { printf("st%d: error class %d code %d\n", unit, sense->error_class, sense->error_code); if(sense->valid) printf("block no. %d (decimal)\n", (sense->ext.unextended.blockhi <<16), + (sense->ext.unextended.blockmed <<8), + (sense->ext.unextended.blocklow )); } return EIO; case 7: /* * If it's class 7, use the extended stuff and interpret * the key */ if(sense->ext.extended.eom) st->flags |= ST_AT_EOM; if(sense->ext.extended.filemark) st->flags |= ST_AT_FILEMARK; if(sense->ext.extended.ili) { if(sense->valid) { /* * In all ili cases, note that * the resid is non-0 AND not * unchanged. */ xs->resid = ntohl(*((long *)sense->ext.extended.info)); if(xs->bp) { if(xs->resid < 0) { /* never on block devices */ /* * it's only really bad * if we have lost data * (the record was * bigger than the read) */ return EIO; } } } else printf("st%d: bad length error?", unit); } key = sense->ext.extended.sense_key; switch(key) { case 0x0: return ESUCCESS; case 0x1: if(!silent) { printf("st%d: soft error (corrected)", unit); if(sense->valid) { printf(" block %d\n", (sense->ext.extended.info[0] <<24)| (sense->ext.extended.info[1] <<16)| (sense->ext.extended.info[2] <<8)| (sense->ext.extended.info[3] )); } else printf("\n"); } return ESUCCESS; case 0x2: if(!silent) printf("st%d: not ready\n", unit); return ENODEV; case 0x3: if(!silent) { printf("st%d: medium error", unit); if(sense->valid) { printf(" block %d\n", (sense->ext.extended.info[0] <<24)| (sense->ext.extended.info[1] <<16)| (sense->ext.extended.info[2] <<8)| (sense->ext.extended.info[3] )); } else printf("\n"); } return EIO; case 0x4: if(!silent) printf("st%d: component failure\n", unit); return EIO; case 0x5: if(!silent) printf("st%d: illegal request\n", unit); return EINVAL; case 0x6: if(!silent) printf("st%d: media change\n", unit); st->flags &= ~(ST_AT_FILEMARK|ST_AT_EOM); st->info_valid = FALSE; if (st->flags & ST_OPEN) /* TEMP!!!! */ return EIO; return ESUCCESS; case 0x7: if(!silent) { printf("st%d: attempted protection violation", unit); if(sense->valid) { printf(" block %d\n", (sense->ext.extended.info[0] <<24)| (sense->ext.extended.info[1] <<16)| (sense->ext.extended.info[2] <<8)| (sense->ext.extended.info[3] )); } else printf("\n"); } return EACCES; case 0x8: if(!silent) { printf("st%d: block wrong state (worm)", unit); if(sense->valid) { printf(" block %d\n", (sense->ext.extended.info[0] <<24)| (sense->ext.extended.info[1] <<16)| (sense->ext.extended.info[2] <<8)| (sense->ext.extended.info[3] )); } else printf("\n"); } return EIO; case 0x9: if(!silent) printf("st%d: vendor unique\n", unit); return EIO; case 0xa: if(!silent) printf("st%d: copy aborted\n", unit); return EIO; case 0xb: if(!silent) printf("st%d: command aborted\n", unit); return EIO; case 0xc: if(!silent) { printf("st%d: search returned", unit); if(sense->valid) { printf(" block %d\n", (sense->ext.extended.info[0] <<24)| (sense->ext.extended.info[1] <<16)| (sense->ext.extended.info[2] <<8)| (sense->ext.extended.info[3] )); } else printf("\n"); } return ESUCCESS; case 0xd: if(!silent) printf("st%d: volume overflow\n", unit); return ENOSPC; case 0xe: if(!silent) { printf("st%d: verify miscompare\n", unit); if(sense->valid) { printf("block no. %d (decimal)\n", (sense->ext.extended.info[0] <<24)| (sense->ext.extended.info[1] <<16)| (sense->ext.extended.info[2] <<8)| (sense->ext.extended.info[3] )); } else printf("\n"); } return EIO; case 0xf: if(!silent) printf("st%d: unknown error key\n", unit); return EIO; } break; } return 0; }