📄 st.c
字号:
{ register struct scsi_device *devp; register struct scsi_tape *un; int rval = 0; if ((devp = stunits[MTUNIT(dev)]) == 0) { return (ENXIO); /* can't happen */ } un = UPTR; /* * the risk with doing only one space operation is that we * may accidentily jump in old data */ if (un->un_dp->options & ST_KNOWS_EOD) { if (stcmd(devp, SCMD_SPACE, Fmk(count), SYNC_CMD)) { rval = EIO; } } else { while (count > 0) { if (stcmd(devp, SCMD_SPACE, Fmk(1), SYNC_CMD)) { rval = EIO; break; } count -= 1; /* * read a block to see if we have reached * end of medium (double filemark for reel or * medium error for others) */ if (count > 0) { if (stcmd(devp, SCMD_SPACE, Blk(1), SYNC_CMD)) { rval = EIO; break; } if (un->un_eof >= ST_EOF_PENDING) { un->un_status = SUN_KEY_EOT; rval = EIO; break; } } } un->un_err_resid = count; } return (rval);}/* * this routine spaces to to EOM * it keeps track of the current filenumber and returns the * filenumber after the last successful space operation */#define MAX_SKIP 0x1000 /* somewhat arbitrary */static intfind_eom(dev)dev_t dev;{ register struct scsi_device *devp; register struct scsi_tape *un; int count, savefile = -1; if ((devp = stunits[MTUNIT(dev)]) == 0) return (savefile); /* this can't happen */ un = UPTR; savefile = un->un_fileno; /* * see if the drive is smart enough to do the skips in * one operation; 1/2" use two filemarks */ if (un->un_dp->options & ST_KNOWS_EOD) count = MAX_SKIP; else count = 1; while (stcmd(devp, SCMD_SPACE, Fmk(count), SYNC_CMD) == 0) { savefile = un->un_fileno; /* * If we're not EOM smart, space a record * to see whether we're now in the slot between * the two sequential filemarks that logical * EOM consists of (REEL) or hit nowhere land * (8mm). */ if ((un->un_dp->options & ST_KNOWS_EOD) == 0) { if (stcmd(devp, SCMD_SPACE, Blk((1)), SYNC_CMD)) break; else if (un->un_eof >= ST_EOF_PENDING) { un->un_status = KEY_BLANK_CHECK; un->un_fileno++; un->un_blkno = 0; break; } } else savefile = un->un_fileno; } if (un->un_dp->options & ST_KNOWS_EOD) { savefile = un->un_fileno; } DPRINTF_IOCTL(devp, "find_eom: %x\n", savefile); return (savefile);}/* * this routine is frequently used in ioctls below; * it determines whether we know the density and if not will * determine it * if we have written the tape before, one or more filemarks are written * * depending on the stepflag, the head is repositioned to where it was before * the filemarks were written in order not to confuse step counts */#define STEPBACK 0#define NO_STEPBACK 1static intcheck_density_or_wfm(dev, wfm, mode, stepflag)dev_t dev;int wfm, mode, stepflag;{ register struct scsi_device *devp; register struct scsi_tape *un; if ((devp = stunits[MTUNIT(dev)]) == 0) return (ENXIO); un = UPTR; /* * If we don't yet know the density of the tape we have inserted, * we have to either unconditionally set it (if we're 'writing'), * or we have to determine it. As side effects, check for any * write-protect errors, and for the need to put out any file-marks * before positioning a tape. * * If we are going to be spacing forward, and we haven't determined * the tape density yet, we have to do so now... */ if (un->un_state == ST_STATE_OPEN_PENDING_IO) { if (st_determine_density(devp, mode)) { return (EIO); } /* * Presumably we are at BOT. If we attempt to write, it will * either work okay, or bomb. We don't do a st_test_append * unless we're past BOT. */ un->un_laststate = un->un_state; un->un_state = ST_STATE_OPEN; } else if (un->un_fmneeded > 0 || (un->un_lastop == ST_OP_WRITE && wfm)) { int blkno = un->un_blkno; int fileno = un->un_fileno; /* * We need to write one or two filemarks. * In the case of the HP, we need to * position the head between the two * marks. */ if (un->un_fmneeded > 0) { wfm = un->un_fmneeded; un->un_fmneeded = 0; } if (st_write_fm(dev, wfm)) { un->un_fileno = -1; un->un_density_known = 0; return (EIO); } if (stepflag == STEPBACK) { if (stcmd(devp, SCMD_SPACE, Fmk((-wfm)), SYNC_CMD)) { return (EIO); } un->un_blkno = blkno; un->un_fileno = fileno; } } /* * Whatever we do at this point clears the state of the eof flag. */ un->un_eof = ST_NO_EOF; /* * If writing, let's check that we're positioned correctly * at the end of tape before issuing the next write. */ if (!un->un_read_only) { un->un_test_append = 1; } return (0);}/* * This routine implements the ioctl calls. It is called * from the device switch at normal priority. *//*ARGSUSED*/stioctl(dev, cmd, data, flag)dev_t dev;register int cmd;register caddr_t data;int flag;{ register struct scsi_device *devp; register struct scsi_tape *un; struct mtop *mtop; int savefile, tmp, rval; if ((devp = stunits[MTUNIT(dev)]) == 0) return (ENXIO); un = UPTR; /* * first and foremost, handle any ST_EOT_PENDING cases. * That is, if a logical eot is pending notice, notice it. */ mtop = (struct mtop *) data; DPRINTF_IOCTL(devp, "stioctl: mt_op=%x", mtop->mt_op); DPRINTF_IOCTL(devp, " fileno=%x, blkno=%x, un_eof=%x\n", un->un_fileno, un->un_blkno, un->un_eof); if (un->un_eof == ST_EOT_PENDING) { int resid = un->un_err_resid; int status = un->un_status; int lastop = un->un_lastop; if (stcmd(devp, SCMD_SPACE, Fmk((-1)), SYNC_CMD)) { return (EIO); } un->un_lastop = lastop; /* restore last operation */ if (status == SUN_KEY_EOF) un->un_status = SUN_KEY_EOT; else un->un_status = status; un->un_err_resid = resid; un->un_err_blkno = un->un_blkno = 0; /* fix up block number */ un->un_eof = ST_EOT; /* now we're at logical eot */ } /* * now, handle the rest of the situations */ if (cmd == MTIOCGET) { /* Get tape status */ struct mtget *mtget = (struct mtget *) data; mtget->mt_erreg = un->un_status; mtget->mt_resid = un->un_err_resid; mtget->mt_dsreg = un->un_retry_ct; mtget->mt_fileno = un->un_err_fileno; mtget->mt_blkno = un->un_err_blkno; mtget->mt_type = un->un_dp->type; mtget->mt_flags = MTF_SCSI | MTF_ASF; if (un->un_dp->options & ST_REEL) { mtget->mt_flags |= MTF_REEL; mtget->mt_bf = 20; } else { /* 1/4" cartridges */ switch (mtget->mt_type) { /* Emulex cartridge tape */ case MT_ISMT02: mtget->mt_bf = 40; break; default: mtget->mt_bf = 126; break; } } un->un_status = 0; /* Reset status */ un->un_err_resid = 0; return (0); } if (cmd != MTIOCTOP) { return (ENOTTY); } rval = 0; un->un_status = 0; switch (mtop->mt_op) { case MTERASE: /* * MTERASE rewinds the tape, erase it completely, and returns * to the beginning of the tape */ if (un->un_dp->options & ST_REEL) un->un_fmneeded = 2; if (un->un_mspl->wp || un->un_read_only) { un->un_status = KEY_WRITE_PROTECT; un->un_err_resid = mtop->mt_count; un->un_err_fileno = un->un_fileno; un->un_err_blkno = un->un_blkno; return (EACCES); } if (check_density_or_wfm(dev, 1, B_WRITE, NO_STEPBACK) || stcmd(devp, SCMD_REWIND, 0, SYNC_CMD) || stcmd(devp, SCMD_ERASE, 0, SYNC_CMD)) { un->un_fileno = -1; rval = EIO; } else { /* QIC and helical scan rewind after erase */ if (un->un_dp->options & ST_REEL) (void) stcmd(devp, SCMD_REWIND, 0, ASYNC_CMD); } break; case MTWEOF: /* * write an end-of-file record */ if (un->un_mspl->wp || un->un_read_only) { un->un_status = KEY_WRITE_PROTECT; un->un_err_resid = mtop->mt_count; un->un_err_fileno = un->un_fileno; un->un_err_blkno = un->un_blkno; return (EACCES); } if (mtop->mt_count == 0) return (0); un->un_eof = ST_NO_EOF; if (!un->un_read_only) { un->un_test_append = 1; } if (un->un_state == ST_STATE_OPEN_PENDING_IO) { if (st_determine_density(devp, B_WRITE)) return (EIO); } if (st_write_fm(dev, (int) mtop->mt_count)) { /* * Failure due to something other than illegal * request results in loss of state (stintr). */ rval = EIO; } else if (un->un_dp->options & ST_REEL) { /* * Check if user has written all the * filemarks we need. If so, * back up over the last one. */#ifdef NOTNEEDED if (un->un_fmneeded <= 0) { if (stcmd(devp, SCMD_SPACE, Fmk((-1)), SYNC_CMD)) { rval = EIO; } }#endif } break; case MTRETEN: /* * retension the tape * only for cartridge tape */ if ((un->un_dp->options & ST_QIC) == 0) return (ENOTTY); if (check_density_or_wfm(dev, 1, 0, NO_STEPBACK) || stcmd(devp, SCMD_LOAD, 3, SYNC_CMD)) { un->un_fileno = -1; rval = EIO; } break; case MTREW: /* * rewind the tape */ if (check_density_or_wfm(dev, 1, 0, NO_STEPBACK)) { return (EIO); } (void) stcmd(devp, SCMD_REWIND, 0, SYNC_CMD); break; case MTOFFL: /* * rewinds, and, if appropriate, takes the device offline by * unloading the tape */ if (check_density_or_wfm(dev, 1, 0, NO_STEPBACK)) { return (EIO); } (void) stcmd(devp, SCMD_REWIND, 0, SYNC_CMD); (void) stcmd(devp, SCMD_LOAD, 0, SYNC_CMD); un->un_eof = ST_NO_EOF; break; case MTNOP: un->un_status = 0; /* Reset status */ un->un_err_resid = 0; break; case MTEOM: /* * positions the tape at a location just after the last file * written on the tape. For cartridge and 8 mm, this after * the last file mark; for reel, this is inbetween the two * last 2 file marks */ if (un->un_eof >= ST_EOT) { /* * If the command wants to move to logical end * of media, and we're already there, we're done. * If we were at logical eot, we reset the state * to be *not* at logical eot. * * If we're at physical or logical eot, we prohibit * forward space operations (unconditionally). */ return (0); } if (check_density_or_wfm(dev, 1, B_READ, NO_STEPBACK)) { return (EIO); } /* * find_eom() returns the last fileno we knew about; */ savefile = find_eom(dev); if (un->un_status != KEY_BLANK_CHECK) { un->un_fileno = -1; rval = EIO; } else { /* * For 1/2" reel tapes assume logical EOT marked * by two file marks or we don't care that we may * be extending the last file on the tape. */ if (un->un_dp->options & ST_REEL) { if (stcmd(devp, SCMD_SPACE, Fmk((-1)), SYNC_CMD)) { un->un_fileno = -1; rval = EIO; break; } /* * Fix up the block number. */ un->un_blkno = 0; un->un_err_blkno = 0; } un->un_err_resid = 0; un->un_fileno = savefile; un->un_eof = ST_EOT; } un->un_status = 0; break; case MTFSF: /* * forward space over filemark * * For ASF we allow a count of 0 on fsf which means * we just want to go to beginning of current file. * Equivalent to "nbsf(0)" or "bsf(1) + fsf". */ if ((un->un_eof >= ST_EOT) && (mtop->mt_count > 0)) { /* we're at EOM */ un->un_err_resid = mtop->mt_count; /* XXX added XXX */ un->un_status = KEY_BLANK_CHECK; return (EIO); } /* * physical tape position may not be what we've been * telling the user; adjust the request accordingly */ if (mtop->mt_count && IN_EOF(un)) { un->un_fileno++; un->un_blkno = 0; /* * For positive direction case, we're now covered. * For zero or negative direction, we're covered * (almost) */ mtop->mt_count--; } if (check_density_or_wfm(dev, 1, B_READ, STEPBACK)) { return (EIO); } /* * Forward space file marks. * We leave ourselves at block zero * of the target file number. */ if (mtop->mt_count < 0) { mtop->mt_count = -mtop->mt_count; mtop->mt_op = MTNBSF; goto bspace; }fspace: if ((tmp = mtop->mt_count) == 0) { if (un->un_blkno == 0) { un->un_err_resid = 0; un->un_err_fileno = un->un_fileno; un->un_err_blkno = un->un_blkno; break; } else if (un->un_fileno == 0) { rval = stcmd(devp, SCMD_REWIND, 0, SYNC_CMD); } else if (un->un_dp->options & ST_BSF) { rval = (stcmd(devp, SCMD_SPACE, Fmk((-1)), SYNC_CMD) || stcmd(devp, SCMD_SPACE, Fmk(1), SYNC_CMD)); } else { tmp = un->un_fileno; rval = (stcmd(devp, SCMD_REWIND, 0, SYNC_CMD) || stcmd(devp, SCMD_SPACE, Fmk(tmp), SYNC_CMD)); } if (rval) { un->un_fileno = -1; rval = EIO; } } else { rval = space_fmks(dev, tmp); } break; case MTFSR: /* * forward space to inter-record gap * */ if (mtop->mt_count == 0) { return (0); } if ((un->un_eof >= ST_EOT) && (mtop->mt_count > 0)) { /* we're at EOM */ un->un_err_resid = mtop->mt_count; un->un_status = KEY_BLANK_CHECK; return (EIO); } /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -