📄 tpqic02.c
字号:
status_eom_detected = YES; /* I don't know whether drive always reports EOF at or before EOM. */ status_eof_detected = YES; } /** if (err & TP_UDA) "Unrecoverable data error" **/ /** if (err & TP_BNL) "Bad block not located" **/ if (err & TP_FIL) { gs |= GMT_EOF(-1); status_eof_detected = YES; } } if (err & TP_ST1) { /** if (err & TP_ILL) "Illegal command" **/ /** if (err & TP_NDT) "No data detected" **/ /** if (err & TP_MBD) "Marginal block detected" **/ if (err & TP_BOM) gs |= GMT_BOT(-1); /* beginning of tape */ } ioctl_status.mt_gstat = gs; ioctl_status.mt_dsreg = tperror.exs; /* "drive status" */ ioctl_status.mt_erreg = tperror.dec; /* "sense key error" */ if (err & (TP_ST0|TP_ST1)) { /* My Wangtek occasionally reports `status' 1212 which should be ignored. */ exnr = decode_qic_exception_nr(err); handle_qic_exception(exnr, err); /* update driver state wrt drive status */ report_qic_exception(exnr); } err &= ~ignore; /* mask unwanted errors -- not the correct way, use exception nrs?? */ if (((err & TP_ST0) && (err & REPORT_ERR0)) || ((err & TP_ST1) && (err & REPORT_ERR1))) return TE_ERR; return TE_OK;} /* tp_sense *//* Wait for a wind or rewind operation to finish or * to time-out. (May take very long). */static int wait_for_rewind(time_t timeout){ int stat; stat = inb(QIC02_STAT_PORT) & QIC02_STAT_MASK; if (TPQDBG(REWIND)) printk(TPQIC02_NAME ": Waiting for (re-)wind to finish: stat=0x%x\n", stat); stat = wait_for_ready(timeout); if (stat != TE_OK) { tpqputs(TPQD_ALWAYS, "(re-) winding failed\n"); } return stat;} /* wait_for_rewind *//* Perform a full QIC02 command, and wait for completion, * check status when done. Complain about exceptions. * * This function should return an OS error code when * something goes wrong, 0 otherwise. */static int ll_do_qic_cmd(int cmd, time_t timeout){ int stat; if (status_dead == YES) { tpqputs(TPQD_ALWAYS, "Drive is dead. Do a `mt reset`."); return -ENXIO; /* User should do an MTRESET. */ } stat = wait_for_ready(timeout); /* wait for ready or exception */ if (stat == TE_EX) { if (tp_sense(TP_WRP|TP_BOM|TP_EOM|TP_FIL)!=TE_OK) return -EIO; /* else nothing to worry about, I hope */ stat = TE_OK; } if (stat != TE_OK) { printk(TPQIC02_NAME ": ll_do_qic_cmd(%x, %ld) failed\n", cmd, (long) timeout); return -EIO; }#if OBSOLETE /* wait for ready since it may not be active immediately after reading status */ while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) != 0);#endif stat = send_qic02_cmd(cmd, timeout, 0); /* (checks for exceptions) */ if (cmd==QCMD_RD_FM) { status_eof_detected = NO; ioctl_status.mt_fileno++; /* Should update block count as well, but can't. * Can do a `read address' for some drives, when MTNOP is done. */ } else if (cmd==QCMD_WRT_FM) { status_eof_detected = NO; ioctl_status.mt_fileno++; } else if ((cmd==QCMD_REWIND) || (cmd==QCMD_ERASE) || (cmd==QCMD_RETEN)) { status_eof_detected = NO; status_eom_detected = NO; status_eot_detected = NO; need_rewind = NO; ioctl_status.mt_fileno = ioctl_status.mt_blkno = 0; extra_blocks_left = BLOCKS_BEYOND_EW; return_write_eof = NO; return_read_eof = NO; reported_read_eof = NO; reported_write_eof = NO; } /* sense() will set eof/eom as required */ if (stat==TE_EX) { if (tp_sense(TP_WRP|TP_BOM|TP_EOM|TP_FIL)!=TE_OK) { printk(TPQIC02_NAME ": Exception persist in ll_do_qic_cmd[1](%x, %ld)", cmd, (long) timeout); status_dead = YES; return -ENXIO; /* if rdstatus fails too, we're in trouble */ } } else if (stat!=TE_OK) { printk(TPQIC02_NAME ": ll_do_qic_cmd: send_qic02_cmd failed, stat = 0x%x\n", stat); return -EIO; /*** -EIO is probably not always appropriate */ } if (timeout == TIM_R) stat = wait_for_rewind(timeout); else stat = wait_for_ready(timeout); if (stat==TE_EX) { if (tp_sense((cmd==QCMD_SEEK_EOD ? /*****************************/ TP_EOR|TP_NDT|TP_UDA|TP_BNL|TP_WRP|TP_BOM|TP_EOM|TP_FIL : TP_WRP|TP_BOM|TP_EOM|TP_FIL))!=TE_OK) { printk(TPQIC02_NAME ": Exception persist in ll_do_qic_cmd[2](%x, %ld)\n", cmd, (long) timeout); if (cmd!=QCMD_RD_FM) status_dead = YES; return -ENXIO; /* if rdstatus fails too, we're in trouble */ } } else if (stat!=TE_OK) { printk(TPQIC02_NAME ": ll_do_qic_cmd %x: wait failed, stat == 0x%x\n", cmd, stat); return -EIO; } return 0;} /* ll_do_qic_cmd *//* * Problem: What to do when the user cancels a read/write operation * in-progress? * * "Deactivating ONLINE during a READ also causes the" * "tape to be rewound to BOT." Ditto for WRITEs, except * a FM is written first. "The host may alternatively terminate * the READ/WRITE command by issuing a RFM/WFM command." * * For READs: * Neither option will leave the tape positioned where it was. * Another (better?) solution is to terminate the READ by two * subsequent sense() operations, the first to stop the current * READ cycle, the second to clear the `Illegal command' exception, * because the QIC-02 specs didn't anticipate this. This is * delayed until actually needed, so a tar listing can be aborted * by the user and continued later. * If anybody has a better solution, let me know! [Also, let me * know if your drive (mine is a Wangtek5150EQ) does not accept * this sequence for canceling the read-cycle.] * * For WRITEs it's simple: Just do a WRITE_FM, leaving the tape * positioned after the FM. */static void terminate_read(int cmd){ if (doing_read == YES) { doing_read = NO; if (cmd != QCMD_RD_FM) { /* if the command is a RFM, there is no need to do this * because a RFM will legally terminate the read-cycle. */ tpqputs(TPQD_ALWAYS, "terminating pending read-cycle"); /* I'm not too sure about this part -- hhb */ if (QIC02_TAPE_IFC == MOUNTAIN) { /* Mountain reference says can terminate by de-asserting online */ ctlbits &= ~MTN_QIC02_CTL_ONLINE; } if (tp_sense(TP_FIL|TP_EOM|TP_WRP) != TE_OK) { tpqputs(TPQD_ALWAYS, "finish_rw[read1]: ignore the 2 lines above"); if (is_exception()) { if (tp_sense(TP_ILL|TP_FIL|TP_EOM|TP_WRP) != TE_OK) tpqputs(TPQD_ALWAYS, "finish_rw[read2]: read cycle error"); } } } }} /* terminate_read */static void terminate_write(int cmd){ int stat; if (doing_write == YES) { doing_write = NO; /* Finish writing by appending a FileMark at the end. */ if (cmd != QCMD_WRT_FM) { /* finish off write cycle */ stat = ll_do_qic_cmd(QCMD_WRT_FM, TIM_M); if (stat != TE_OK) tpqputs(TPQD_ALWAYS, "Couldn't finish write cycle properly"); (void) tp_sense(0); } /* If there is an EOF token waiting to be returned to * the (writing) application, discard it now. * We could be at EOT, so don't reset return_write_eof. */ reported_write_eof=YES; }} /* terminate_write *//* terminate read or write cycle because of command `cmd' */static void finish_rw(int cmd){ if (wait_for_ready(TIM_S) != TE_OK) { tpqputs(TPQD_ALWAYS, "error: drive not ready in finish_rw() !"); return; } terminate_read(cmd); terminate_write(cmd);} /* finish_rw *//* Perform a QIC command through ll_do_qic_cmd(). * If necessary, rewind the tape first. * Return an OS error code if something goes wrong, 0 if all is well. */static int do_qic_cmd(int cmd, time_t timeout){ int stat; finish_rw(cmd); if (need_rewind) { tpqputs(TPQD_REWIND, "Rewinding tape..."); stat = ll_do_qic_cmd(QCMD_REWIND, TIM_R); if (stat != 0) { printk(TPQIC02_NAME ": rewind failed in do_qic_cmd(). stat=0x%2x", stat); return stat; } need_rewind = NO; if (cmd==QCMD_REWIND) /* don't wind beyond BOT ;-) */ return 0; } return ll_do_qic_cmd(cmd, timeout);} /* do_qic_cmd *//* Not all ioctls are supported for all drives. Some rely on * optional QIC-02 commands. Check tpqic02.h for configuration. * Some of these commands may require ONLINE to be active. */static int do_ioctl_cmd(int cmd){ int stat; /* It is not permitted to read or wind the tape after bytes have * been written. It is not permitted to write the tape while in * read mode. * We try to be kind and allow reading again after writing a FM... */ switch (cmd) { case MTRESET: /* reset verbose */ return (tape_reset(1)==TE_OK)? 0 : -EIO; case MTFSF: tpqputs(TPQD_IOCTLS, "MTFSF forward searching filemark"); if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; return do_qic_cmd(QCMD_RD_FM, TIM_F); case MTBSF: if (TP_HAVE_BSF) { tpqputs(TPQD_IOCTLS, "MTBSF backward searching filemark -- optional command"); if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; stat = do_qic_cmd(QCMD_RD_FM_BCK, TIM_F); } else { stat = -ENXIO; } status_eom_detected = status_eof_detected = NO; return stat; case MTFSR: if (TP_HAVE_FSR) { /* This is an optional QIC-02 command */ tpqputs(TPQD_IOCTLS, "MTFSR forward space record"); if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; stat = do_qic_cmd(QCMD_SPACE_FWD, TIM_F); } else { /**** fake it by doing a read data block command? ******/ tpqputs(TPQD_IOCTLS, "MTFSR not supported"); stat = -ENXIO; } return stat; case MTBSR: if (TP_HAVE_BSR) { /* This is an optional QIC-02 command */ /* we need this for appending files with GNU tar!! */ tpqputs(TPQD_IOCTLS, "MTFSR backward space record"); if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; stat = do_qic_cmd(QCMD_SPACE_BCK, TIM_F); } else { tpqputs(TPQD_IOCTLS, "MTBSR not supported"); stat = -ENXIO; } status_eom_detected = status_eof_detected = NO; return stat; case MTWEOF: tpqputs(TPQD_IOCTLS, "MTWEOF write eof mark"); /* Plain GNU mt(1) 2.2 uses read-only mode for writing FM. :-( */ if (mode_access==READ) return -EACCES; /* allow tape movement after writing FM */ status_bytes_rd = status_bytes_wr; /* Kludge-O-Matic */ status_bytes_wr = NO; return do_qic_cmd(QCMD_WRT_FM, TIM_M); /* not sure what to do with status_bytes when WFM should fail */ case MTREW: tpqputs(TPQD_IOCTLS, "MTREW rewinding tape"); if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; status_eom_detected = status_eof_detected = NO; return do_qic_cmd(QCMD_REWIND, TIM_R); case MTOFFL: tpqputs(TPQD_IOCTLS, "MTOFFL rewinding & going offline"); /* Doing a drive select will clear (unlock) the current drive. * But that requires support for multiple drives and locking. */ if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; status_eom_detected = status_eof_detected = NO; /**** do rewind depending on minor bits??? ***/ stat = do_qic_cmd(QCMD_REWIND, TIM_R); return stat; case MTNOP: tpqputs(TPQD_IOCTLS, "MTNOP setting status only"); /********** should do `read position' for drives that support it **********/ return (tp_sense(-1)==TE_OK)? 0 : -EIO; /**** check return codes ****/ case MTRETEN: tpqputs(TPQD_IOCTLS, "MTRETEN retension tape"); if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; status_eom_detected = status_eof_detected = NO; return do_qic_cmd(QCMD_RETEN, TIM_R); case MTBSFM: /* Think think is like MTBSF, except that * we shouldn't skip the FM. Tricky. * Maybe use RD_FM_BCK, then do a SPACE_FWD? */ tpqputs(TPQD_IOCTLS, "MTBSFM not supported"); if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; return -ENXIO; case MTFSFM: /* I think this is like MTFSF, except that * we shouldn't skip the FM. Tricky. * Maybe use QCMD_RD_DATA until we get a TP_FIL exception? * But then the FM will have been skipped... * Maybe use RD_FM, then RD_FM_BCK, but not all * drives will support that! */ tpqputs(TPQD_IOCTLS, "MTFSFM not supported"); if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; return -ENXIO; case MTEOM: /* This should leave the tape ready for appending * another file to the end, such that it would append * after the last FM on tape. */ tpqputs(TPQD_IOCTLS, "MTEOM search for End Of recorded Media"); if ((mode_access==WRITE) && status_bytes_wr) return -EACCES; if (TP_HAVE_EOD) { /* Use faster seeking when possible. * This requires the absence of data beyond the EOM. * It seems that my drive does not always perform the * SEEK_EOD correctly, unless it is preceded by a * rewind command. */# if 0 status_eom_detected = status_eof_detected = NO;# endif stat = do_qic_cmd(QCMD_REWIND, TIM_R); if (stat) return stat; stat = do_qic_cmd(QCMD_SEEK_EOD, TIM_F); /* After a successful seek, TP_EOR should be returned */ } else { /* else just seek until the drive returns exception "No Data" */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -