⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tpqic02.c

📁 LINUX 1.0 内核c源代码
💻 C
📖 第 1 页 / 共 5 页
字号:

		*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 + -