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

📄 tpqic02.c

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


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