📄 tpqic02.c
字号:
*/static int decode_exception_nr(short s) /* s must be short, because of sign-extension */{ int i; for (i=1; i<NR_OF_EXC; i++) { if ((s & exception_list[i].mask)==exception_list[i].code) return i; } tpqputs("decode_exception_nr: exception not recognized"); return 0;} /* decode_exception_nr */#ifdef OBSOLETE/* There are exactly 14 possible exceptions, as defined in QIC-02 rev F. * Some are FATAL, some aren't. Currently all exceptions are treated as fatal. * Especially 6 and 14 should not abort the transfer. RSN... * Should probably let sense() figure out the exception number using the code * below, and just report the error based on the number here, returning a code * for FATAL/CONTINUABLE. */static void report_error(int s){ short n = -1; if (s & TP_ST1) { if (s & TP_ILL) /* 12: Illegal command. FATAL */ n = 12; if (s & TP_POR) /* 13: Reset occurred. FATAL */ n = 13; } else if (s & TP_ST0) { if (s & TP_EOR) { /* extra: 15: End of Recorded Media. CONTINUABLE */ n = 15; /********** should set flag here **********/ } else if (s & TP_EOM) /* 4: End Of Media. CONTINUABLE */ n = 4; else if (s & TP_USL) /* 2: Drive not online. FATAL */ n = 2; else if (s & TP_CNI) { /* 1: Cartridge not in place. FATAL */ n = 1; need_rewind = YES; status_eof_detected = NO; status_eom_detected = NO; } else if (s & TP_UDA) { if (s & TP_BNL) { if (s & TP_NDT) { if (s & TP_BOM) /* 9: Read error. No data detected & EOM. CONTINUABLE */ n = 9; else if (s & TP_EOM) /* 10: Read error. No data detected & BOM. CONTINUABLE */ n = 10; else /* 8: Read error. No data detected. CONTINUABLE */ n = 8; } else { /* 7: Read error. Cannot recover block, filler substituted. CONTINUABLE */ tpqputs("[Bad block -- filler data transferred.]"); n = 7; } } else { if (s & TP_EOM) /* 5: Read or Write error. Rewind tape. FATAL */ n = 5; else { /* 6: Read error. Bad block transferred. CONTINUABLE */ /* block is bad, but transfer may continue. * This is why some people prefer not to * use compression on backups... */ tpqputs("[CRC failed!]"); n = 6; } } } else if (s & TP_FIL) { if (s & TP_MBD) { /* 14: Marginal block detected. CONTINUABLE */ tpqputs("[Marginal block]"); n = 14; } else /* 11: File mark detected. CONTINUABLE */ n = 11; } else if (s & TP_WRP) /* 3: Write protected cartridge. FATAL */ n = 3; } if (n >= 0) sensemsg(n);} /* report_error */#endif/* perform appropriate action for certain exceptions */static void handle_exception(int exnr, int exbits){ if (exnr==EXC_NCART) { /* Cartridge was changed. Redo sense(). * EXC_NCART should be handled in open(). * It is not permitted to remove the tape while * the tape driver has open files. */ need_rewind = YES; status_eof_detected = NO; status_eom_detected = NO; } else if (exnr==EXC_XFILLER) tpqputs("[Bad block -- filler data transferred.]"); else if (exnr==EXC_XBAD) tpqputs("[CRC failed!]"); else if (exnr==EXC_MARGINAL) { /* A marginal block behaves much like a FM. * User may continue reading, if desired. */ tpqputs("[Marginal block]"); doing_read = NO; } else if (exnr==EXC_FM) doing_read = NO;} /* handle_exception */static inline int is_exception(void){ return (inb(QIC_STAT_PORT) & QIC_STAT_EXCEPTION) == 0;} /* is_exception *//* Reset the tape drive and controller. * When reset fails, it marks the drive as dead and all * requests (except reset) are to be ignored (ENXIO). */static int tape_reset(int verbose){ ifc_init(); /* reset interface card */ outb_p(ctlbits | QIC_CTL_RESET, QIC_CTL_PORT); /* assert reset */ /* Next, we need to wait >=25 usec. */ udelay(30); /* after reset, we will be at BOT (modulo an automatic rewind) */ status_eof_detected = NO; status_eom_detected = NO; status_cmd_pending = 0; need_rewind = YES; doing_read = doing_write = NO; ioctl_status.mt_fileno = ioctl_status.mt_blkno = 0; outb_p(ctlbits & ~QIC_CTL_RESET, QIC_CTL_PORT); /* de-assert reset */ /* KLUDGE FOR G++ BUG */ { int stat = inb_p(QIC_STAT_PORT); status_dead = ((stat & QIC_STAT_RESETMASK) != QIC_STAT_RESETVAL); } /* if successful, inb(STAT) returned RESETVAL */ if (status_dead) printk(TPQIC_NAME ": reset failed!\n"); else if (verbose) printk(TPQIC_NAME ": reset successful\n"); return (status_dead)? TE_DEAD : TE_OK;} /* tape_reset *//* Notify tape drive of a new command. It only waits for the * command to be accepted, not for the actual command to complete. * * Before calling this routine, QIC_CMD_PORT must have been loaded * with the command to be executed. * After this routine, the exception bit must be checked. * This routine is also used by rdstatus(), so in that case, any exception * must be ignored (`ignore_ex' flag). */static int notify_cmd(char cmd, short ignore_ex){ int i; outb_p(cmd, QIC_CMD_PORT); /* output the command */ /* wait 1 usec before asserting /REQUEST */ udelay(1); if ((!ignore_ex) && is_exception()) { tpqputs("*** exception detected in notify_cmd"); /** force a reset here **/ if (tape_reset(1)==TE_DEAD) return TE_DEAD; if (is_exception()) { tpqputs("exception persists after reset."); tpqputs(" ^ exception ignored."); } } outb_p(ctlbits | QIC_CTL_REQUEST, QIC_CTL_PORT); /* set request bit */ i = TAPE_NOTIFY_TIMEOUT; /* The specs say this takes about 500 usec, but there is no upper limit! * If the drive were busy retensioning or something like that, * it could be *much* longer! */ while ((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) && (--i>0)) /*skip*/; /* wait for ready */ if (i==0) { tpqputs("timed out waiting for ready in notify_cmd"); status_dead = YES; return TE_TIM; } outb_p(ctlbits & ~QIC_CTL_REQUEST, QIC_CTL_PORT); /* reset request bit */ i = TAPE_NOTIFY_TIMEOUT; /* according to the specs this one should never time-out */ while (((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) == 0) && (--i>0)) /*skip*/; /* wait for not ready */ if (i==0) { tpqputs("timed out waiting for !ready in notify_cmd"); status_dead = YES; return TE_TIM; } /* command accepted */ return TE_OK;} /* notify_cmd *//* Wait for a command to complete, with timeout */static int wait_for_ready(time_t timeout){ int stat; time_t spin_t; /* Wait for ready or exception, without driving the loadavg up too much. * In most cases, the tape drive already has READY asserted, * so optimize for that case. * * First, busy wait a few usec: */ spin_t = 50; while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (--spin_t>0)) /*SKIP*/; if ((stat & QIC_STAT_READY) == 0) return TE_OK; /* covers 99.99% of all calls */ /* Then use schedule() a few times */ spin_t = 3; /* max 0.03 sec busy waiting */ if (spin_t > timeout) spin_t = timeout; timeout -= spin_t; spin_t += jiffies; while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (jiffies<spin_t)) schedule(); /* don't waste all the CPU time */ if ((stat & QIC_STAT_READY) == 0) return TE_OK; /* If we reach this point, we probably need to wait much longer, or * an exception occurred. Either case is not very time-critical. * Check the status port only a few times every second. * A interval of less than 0.10 sec will not be noticed by the user, * more than 0.40 sec may give noticeable delays. */ spin_t += timeout; TPQDEB({printk("wait_for_ready: additional timeout: %d\n", spin_t);}) /* not ready and no exception && timeout not expired yet */ while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (jiffies<spin_t)) { /* be `nice` to other processes on long operations... */ current->timeout = jiffies + 30; /* nap 0.30 sec between checks, */ current->state = TASK_INTERRUPTIBLE; schedule(); /* but could be woken up earlier by signals... */ } /* don't use jiffies for this test because it may have changed by now */ if ((stat & QIC_STAT_MASK) == QIC_STAT_MASK) { tpqputs("wait_for_ready() timed out"); return TE_TIM; } if ((stat & QIC_STAT_EXCEPTION) == 0) { tpqputs("exception detected after waiting_for_ready"); return TE_EX; } else { return TE_OK; }} /* wait_for_ready *//* Send some data to the drive */static int send_qic02_data(char sb[], unsigned size, int ignore_ex){ int i, stat; for (i=0; i<size; i++) { stat = wait_for_ready(TIM_S); if (stat != TE_OK) return stat; stat = notify_cmd(sb[i], ignore_ex); if (stat != TE_OK) return stat; } return TE_OK; } /* send_qic02_data *//* Send a QIC-02 command (`cmd') to the tape drive, with * a time-out (`timeout'). * This one is also used by tp_sense(), so we must have * a flag to disable exception checking (`ignore_ex'). * * On entry, the controller is supposed to be READY. */static int send_qic02_cmd(int cmd, time_t timeout, int ignore_ex){ int stat; stat = inb_p(QIC_STAT_PORT); if ((stat & QIC_STAT_EXCEPTION) == 0) { /* if exception */ tpqputs("send_qic02_cmd: Exception!"); return TE_EX; } if (stat & QIC_STAT_READY) { /* if not ready */ tpqputs("send_qic02_cmd: not Ready!"); return TE_ERR; } /* assert(ready & !exception) */ /* Remember current command for later re-use with dma transfers. * (For reading/writing multiple blocks.) */ status_cmd_pending = cmd; stat = notify_cmd(cmd, ignore_ex); /* tell drive new command was loaded, */ /* inherit exception check. */ if (cmd == QCMDV_SEEK_BLK) { /* This one needs to send 3 more bytes, MSB first */ stat = send_qic02_data(seek_addr_buf, sizeof(seek_addr_buf), ignore_ex); } if (stat != TE_OK) { tpqputs("send_qic02_cmd failed"); } return stat;} /* send_qic02_cmd *//* Get drive status. Assume drive is ready or has exception set. * (or will be in <1000 usec.) * Extra parameters added because of 'Read Extended Status 3' command. */static int rdstatus(char *stp, unsigned size, char qcmd){ int s, n; char *q = stp; /* Try to busy-wait a few (700) usec, after that de-schedule. * * The problem is, if we don't de-schedule, performance will * drop to zero when the drive is not responding and if we * de-schedule immediately, we waste a lot of time because a * task switch is much longer than we usually have to wait here. */ n = 1000; /* 500 is not enough on a 486/33 */ while ((n>0) && ((inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK)) n--; /* wait for ready or exception or timeout */ if (n==0) { /* n (above) should be chosen such that on your machine * you rarely ever see the message below, and it should * be small enough to give reasonable response time.] */ tpqputs("waiting looong in rdstatus() -- drive dead?"); while ((inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) schedule(); tpqputs("finished waiting in rdstatus()"); } (void) notify_cmd(qcmd, 1); /* send read status command */ /* ignore return code -- should always be ok, STAT may contain * exception flag from previous exception which we are trying to clear. */ if (TP_DIAGS(current_tape_dev)) printk(TPQIC_NAME ": reading status bytes: "); for (q=stp; q<stp+size; q++) { do s = inb_p(QIC_STAT_PORT); while ((s & QIC_STAT_MASK) == QIC_STAT_MASK); /* wait for ready or exception */ if ((s & QIC_STAT_EXCEPTION) == 0) { /* if exception */ tpqputs("rdstatus: exception error"); ioctl_status.mt_erreg = 0; /* dunno... */ return TE_NS; /* error, shouldn't happen... */ } *q = inb_p(QIC_DATA_PORT); /* read status byte */ if (TP_DIAGS(current_tape_dev)) printk("[%1d]=0x%x ", q-stp, (unsigned) (*q) & 0xff); outb_p(ctlbits | QIC_CTL_REQUEST, QIC_CTL_PORT); /* set request */ while ((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) == 0); /* wait for not ready */ udelay(22); /* delay >20 usec */ outb_p(ctlbits & ~QIC_CTL_REQUEST, QIC_CTL_PORT); /* un-set request */ } /* Specs say we should wait for READY here. * My drive doesn't seem to need it here yet, but others do? */ while (inb_p(QIC_STAT_PORT) & QIC_STAT_READY) /*skip*/; /* wait for ready */ if (TP_DIAGS(current_tape_dev)) printk("\n"); return TE_OK;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -