📄 tpqic02.c
字号:
static inline int is_exception(void){ return (inb(QIC02_STAT_PORT) & QIC02_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 */ /* assert reset */ if (QIC02_TAPE_IFC == MOUNTAIN) { outb_p(ctlbits & ~MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT); } else /* WANGTEK, ARCHIVE */ { outb_p(ctlbits | QIC02_CTL_RESET, QIC02_CTL_PORT); } /* 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; /* de-assert reset */ if (QIC02_TAPE_IFC == MOUNTAIN) { outb_p(ctlbits | MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT); } else { outb_p(ctlbits & ~QIC02_CTL_RESET, QIC02_CTL_PORT); } /* KLUDGE FOR G++ BUG */ { int stat = inb_p(QIC02_STAT_PORT); status_dead = ((stat & QIC02_STAT_RESETMASK) != QIC02_STAT_RESETVAL); } /* if successful, inb(STAT) returned RESETVAL */ if (status_dead == YES) { printk(TPQIC02_NAME ": reset failed!\n"); } else if (verbose) { printk(TPQIC02_NAME ": reset successful\n"); } return (status_dead == YES)? 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, QIC02_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, QIC02_CMD_PORT); /* output the command */ /* wait 1 usec before asserting /REQUEST */ udelay(1); if ((!ignore_ex) && is_exception()) { tpqputs(TPQD_ALWAYS, "*** exception detected in notify_cmd"); /** force a reset here **/ if (tape_reset(1)==TE_DEAD) return TE_DEAD; if (is_exception()) { tpqputs(TPQD_ALWAYS, "exception persists after reset."); tpqputs(TPQD_ALWAYS, " ^ exception ignored."); } } outb_p(ctlbits | QIC02_CTL_REQUEST, QIC02_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(QIC02_STAT_PORT) & QIC02_STAT_READY) && (--i>0)) /*skip*/; /* wait for ready */ if (i==0) { tpqputs(TPQD_ALWAYS, "timed out waiting for ready in notify_cmd"); status_dead = YES; return TE_TIM; } outb_p(ctlbits & ~QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* reset request bit */ i = TAPE_NOTIFY_TIMEOUT; /* according to the specs this one should never time-out */ while (((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) == 0) && (--i>0)) /*skip*/; /* wait for not ready */ if (i==0) { tpqputs(TPQD_ALWAYS, "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(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (--spin_t>0)) /*SKIP*/; if ((stat & QIC02_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(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && time_before(jiffies, spin_t)) schedule(); /* don't waste all the CPU time */ if ((stat & QIC02_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(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && time_before(jiffies, spin_t)) { /* be `nice` to other processes on long operations... */ current->state = TASK_INTERRUPTIBLE; /* nap 0.30 sec between checks, */ /* but could be woken up earlier by signals... */ schedule_timeout(3*HZ/10); } /* don't use jiffies for this test because it may have changed by now */ if ((stat & QIC02_STAT_MASK) == QIC02_STAT_MASK) { tpqputs(TPQD_ALWAYS, "wait_for_ready() timed out"); return TE_TIM; } if ((stat & QIC02_STAT_EXCEPTION) == 0) { tpqputs(TPQD_ALWAYS, "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(QIC02_STAT_PORT); if ((stat & QIC02_STAT_EXCEPTION) == 0) { /* if exception */ tpqputs(TPQD_ALWAYS, "send_qic02_cmd: Exception!"); return TE_EX; } if (stat & QIC02_STAT_READY) { /* if not ready */ tpqputs(TPQD_ALWAYS, "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 (TP_HAVE_SEEK && (cmd == AR_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(TPQD_ALWAYS, "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(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_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(TPQD_ALWAYS, "waiting looong in rdstatus() -- drive dead?"); while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) schedule(); tpqputs(TPQD_ALWAYS, "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(TPQIC02_NAME ": reading status bytes: "); for (q=stp; q<stp+size; q++) { do s = inb_p(QIC02_STAT_PORT); while ((s & QIC02_STAT_MASK) == QIC02_STAT_MASK); /* wait for ready or exception */ if ((s & QIC02_STAT_EXCEPTION) == 0) { /* if exception */ tpqputs(TPQD_ALWAYS, "rdstatus: exception error"); ioctl_status.mt_erreg = 0; /* dunno... */ return TE_NS; /* error, shouldn't happen... */ } *q = inb_p(QIC02_DATA_PORT); /* read status byte */ if (TP_DIAGS(current_tape_dev)) printk("[%1d]=0x%x ", q-stp, (unsigned) (*q) & 0xff); outb_p(ctlbits | QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* set request */ while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) == 0); /* wait for not ready */ udelay(22); /* delay >20 usec */ outb_p(ctlbits & ~QIC02_CTL_REQUEST, QIC02_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(QIC02_STAT_PORT) & QIC02_STAT_READY) /*skip*/; /* wait for ready */ if (TP_DIAGS(current_tape_dev)) printk("\n"); return TE_OK;} /* rdstatus *//* Get standard status (6 bytes). * The `.dec' and `.urc' fields are in MSB-first byte-order, * so they have to be swapped first. */static int get_status(volatile struct tpstatus *stp){ int stat = rdstatus((char *) stp, TPSTATSIZE, QCMD_RD_STAT);#if defined(i386) || defined(i486) byte_swap_w(&(stp->dec)); byte_swap_w(&(stp->urc));#else /* should probably swap status bytes #definition */#endif return stat;} /* get_status */#if 0/* This fails for my Wangtek drive *//* get "Extended Status Register 3" (64 bytes) * * If the meaning of the returned bytes were known, the MT_TYPE * identifier could be used to decode them, since they are * "vendor unique". :-( */static int get_ext_status3(void){ char vus[64]; /* vendor unique status */ int stat, i; tpqputs(TPQD_ALWAYS, "Attempting to read Extended Status 3..."); stat = rdstatus(vus, sizeof(vus), QCMD_RD_STAT_X3); if (stat != TE_OK) return stat; tpqputs(TPQD_ALWAYS, "Returned status bytes:"); for (i=0; i<sizeof(vus); i++) { if ( i % 8 == 0 ) printk("\n" TPQIC02_NAME ": %2d:"); printk(" %2x", vus[i] & 0xff); } printk("\n"); return TE_OK;} /* get_ext_status3 */#endif/* Read drive status and set generic status too. * NOTE: Once we do a tp_sense(), read/write transfers are killed. */static int tp_sense(int ignore){ unsigned err = 0, exnr = 0, gs = 0; static void finish_rw(int cmd); if (TPQDBG(SENSE_TEXT)) printk(TPQIC02_NAME ": tp_sense(ignore=0x%x) enter\n", ignore); /* sense() is not allowed during a read or write cycle */ if (doing_write == YES) tpqputs(TPQD_ALWAYS, "Warning: File Mark inserted because of sense() request"); /* The extra test is to avoid calling finish_rw during booting */ if ((doing_read!=NO) || (doing_write!=NO)) finish_rw(QCMD_RD_STAT); if (get_status(&tperror) != TE_OK) { tpqputs(TPQD_ALWAYS, "tp_sense: could not read tape drive status"); return TE_ERR; } err = tperror.exs; /* get exception status bits */ if (err & (TP_ST0|TP_ST1)) printk(TPQIC02_NAME ": tp_sense: status: %x, error count: %d, underruns: %d\n", tperror.exs, tperror.dec, tperror.urc); else if ((tperror.dec!=0) || (tperror.urc!=0) || TPQDBG(SENSE_CNTS)) printk(TPQIC02_NAME ": tp_sense: no hard errors, soft error count: %d, underruns: %d\n", tperror.dec, tperror.urc); /* Set generic status. HP-UX defines these, but some extra would * be useful. Problem is to remain compatible. [Do we want to be * compatible??] */ if (err & TP_ST0) { if (err & TP_CNI) /* no cartridge */ gs |= GMT_DR_OPEN(-1); if (status_dead == NO) gs |= GMT_ONLINE(-1); /* always online */ if (err & TP_USL) /* not online */ gs &= ~GMT_ONLINE(-1); if (err & TP_WRP) gs |= GMT_WR_PROT(-1); if (err & TP_EOM) { /* end of media */ gs |= GMT_EOT(-1); /* not sure this is correct for writes */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -