📄 tpqic02.c
字号:
*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;
} /* 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(char *stp)
{
int stat = rdstatus(stp, TPSTATSIZE, QCMD_RD_STAT);
#if defined(i386) || defined(i486)
byte_swap_w(&tperror.dec);
byte_swap_w(&tperror.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("Attempting to read Extended Status 3...");
stat = rdstatus(vus, sizeof(vus), QCMD_RD_STAT_X3);
if (stat != TE_OK)
return stat;
tpqputs("Returned status bytes:");
for (i=0; i<sizeof(vus); i++) {
if ( i % 8 == 0 )
printk("\n" TPQIC_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);
printk(TPQIC_NAME ": tp_sense(ignore=0x%x) enter\n", ignore);
/* sense() is not allowed during a read or write cycle */
if (doing_write == YES)
tpqputs("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((char *) &tperror) != TE_OK) {
tpqputs("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(TPQIC_NAME ": tp_sense: status: %x, error count: %d, underruns: %d\n",
tperror.exs, tperror.dec, tperror.urc);
else
printk(TPQIC_NAME ": tp_sense: no errors at all, 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 */
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!=0) {
exnr = decode_exception_nr(err);
handle_exception(exnr, err); /* update driver state wrt drive status */
report_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(QIC_STAT_PORT) & QIC_STAT_MASK;
printk(TPQIC_NAME ": Waiting for (re-)wind to finish: stat=0x%x\n", stat);
stat = wait_for_ready(timeout);
if (stat != TE_OK) {
tpqputs("(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) {
tpqputs("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(TPQIC_NAME ": ll_do_qic_cmd(%x, %d) failed\n", cmd, timeout);
return -EIO;
}
#if OBSOLETE
/* wait for ready since it may not be active immediately after reading status */
while ((inb_p(QIC_STAT_PORT) & QIC_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(TPQIC_NAME ": Exception persist in ll_do_qic_cmd[1](%x, %d)", cmd, timeout);
status_dead = YES;
return -ENXIO;
/* if rdstatus fails too, we're in trouble */
}
}
else if (stat!=TE_OK) {
printk(TPQIC_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(TPQIC_NAME ": Exception persist in ll_do_qic_cmd[2](%x, %d)\n", cmd, 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(TPQIC_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("terminating pending read-cycle");
if (tp_sense(TP_FIL|TP_EOM|TP_WRP) != TE_OK) {
tpqputs("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("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("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("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("Rewinding tape...");
stat = ll_do_qic_cmd(QCMD_REWIND, TIM_R);
if (stat != 0) {
printk(TPQIC_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("MTFSF forward searching filemark");
if ((mode_access==WRITE) && status_bytes_wr)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -