📄 tpqic02.c
字号:
static void report_exception(unsigned n)
{
if (n >= NR_OF_EXC) { tpqputs("Oops -- report_exception"); n = 0; }
printk(TPQIC_NAME ": sense: %s\n", exception_list[n].msg);
} /* report_exception */
/* Try to map the drive-exception bits `s' to a predefined "exception number",
* by comparing the significant exception bits for each entry in the
* exception table (`exception_list[]').
* It is assumed that s!=0.
*/
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... */
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -