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

📄 tpqic02.c

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


	dma_mode = 0;
	/* Note: The drive is left on-line, ready for the next
	 * data transfer.
	 * If the next command to the drive does not continue
	 * the pending cycle, it must do 2 sense()s first.
	 */

	*bytes_done = dma_bytes_done;
	status_expect_int = NO;
	ioctl_status.mt_blkno += (dma_bytes_done / TAPE_BLKSIZE);

	TPQPUTS("end_dma() exit");
	/*** could return stat here ***/
} /* end_dma */

/*********** Below are the (public) OS-interface procedures ***********/


/* tape_qic02_times_out() is called when a DMA transfer doesn't complete
 * quickly enough. Usually this means there is something seriously wrong
 * with the hardware/software, but it could just be that the controller
 * has decided to do a long rewind, just when I didn't expect it.
 * Just try again.
 */
static void tape_qic02_times_out(void)
{
	printk("time-out in %s driver\n", TPQIC_NAME);
	if ((status_cmd_pending>0) || dma_mode) {
		/* takes tooo long, shut it down */
		status_dead = YES;
		status_cmd_pending = 0;
		status_timer_on = NO;
		status_expect_int = NO;
		status_error = YES;
		if (dma_mode) {
			dma_mode = 0;	/* signal end to read/write routine */
			wake_up(&tape_qic02_transfer);
		}
	}
} /* tape_qic02_times_out */

/*
 * Interrupt handling:
 *
 * 1) Interrupt is generated iff at the end of 
 *    a 512-DMA-block transfer.
 * 2) EXCEPTION is not raised unless something 
 *    is wrong or EOT/FM is detected.
 * 3) FM EXCEPTION is set *after* the last byte has
 *    been transferred by DMA. By the time the interrupt
 *    is handled, the EXCEPTION may already be set.
 *
 * So,
 * 1) On EXCEPTION, assume data has been transferred, so
 *    continue as usual, but set a flag to indicate the
 *    exception was detected.
 *    Do a sense status when the flag is found set.
 * 2) Do not attempt to continue a transfer after an exception.
 *    [??? What about marginal blocks???????]
 */


/* tape_qic02_interrupt() is called when the tape controller completes 
 * a DMA transfer.
 * We are not allowed to sleep here! 
 *
 * Check if the transfer was successful, check if we need to transfer
 * more. If the buffer contains enough data/is empty enough, signal the
 * read/write() thread to copy to/from user space.
 * When we are finished, set flags to indicate end, disable timer.
 * NOTE: This *must* be fast! 
 */
static void tape_qic02_interrupt(int unused)
{
	int stat, r, i;

	TIMEROFF;

	if (status_expect_int) {
		if (TP_DIAGS(current_tape_dev))
			printk("@");
	
		stat = inb(QIC_STAT_PORT);	/* Knock, knock */
#if TAPE_QIC02_IFC == ARCHIVE			/* "Who's there?" */
		if (((stat & (AR_STAT_DMADONE)) == 0) &&
                      ((stat & (QIC_STAT_EXCEPTION)) != 0)) {
			TIMERCONT;
			return;			/* "Linux with IRQ sharing" */
		}
#endif
		if ((stat & QIC_STAT_EXCEPTION) == 0) {	/* exception occurred */
			/* Possible causes for an exception during a transfer:
			 * 	- during a write-cycle: end of tape (EW) hole detected.
			 *	- during a read-cycle: filemark or EOD detected.
			 *	- something went wrong
			 * So don't continue with the next block.
			 */
			tpqputs("isr: exception on tape controller");
			printk("      status %02x\n", stat);
			status_error = TE_EX;

			dma_bytes_done += TAPE_BLKSIZE;

			dma_mode = 0;	/* wake up rw() */
			status_expect_int = NO;
			wake_up(&tape_qic02_transfer);
			return;
		}
		/* return if tape controller not ready, or
		 * if dma channel hasn't finished last byte yet.
		 */
		r = 0;
/* Skip next ready check for Archive controller because
 * it may be busy reading ahead. Weird. --hhb
 */
#if TAPE_QIC02_IFC != ARCHIVE		/* I think this is a drive-dependency, not IFC -- hhb */
		if (stat & QIC_STAT_READY) {		/* not ready */
			tpqputs("isr: ? Tape controller not ready");
			r = 1;
		}
#endif

		if ( (i = get_dma_residue(TAPE_QIC02_DMA)) != 0 ) {
			printk(TPQIC_NAME ": dma_residue == %x !!!\n", i);
			r = 1;	/* big trouble, but can't do much about it... */
		}

		if (r) 
			return;

		/* finish DMA cycle */

		/* no errors detected, continue */
		dma_bytes_done += TAPE_BLKSIZE;
		if (dma_bytes_done >= dma_bytes_todo) {
			/* finished! Wakeup rw() */
			dma_mode = 0;
			status_expect_int = NO;
			TPQPUTS("isr: dma_bytes_done");
			wake_up(&tape_qic02_transfer);
		} else {
			/* start next transfer, account for track-switching time */
			timer_table[TAPE_QIC02_TIMER].expires = jiffies + 6*HZ;
			dma_transfer();
		}
	} else {
		printk(TPQIC_NAME ": Unexpected interrupt, stat == %x\n",
		       inb(QIC_STAT_PORT));
	}
} /* tape_qic02_interrupt */


static int tape_qic02_lseek(struct inode * inode, struct file * file, off_t offset, int origin)
{
	return -EINVAL;	/* not supported */
} /* tape_qic02_lseek */


/* read/write routines:
 * This code copies between a kernel buffer and a user buffer. The 
 * actual data transfer is done using DMA and interrupts. Time-outs
 * are also used.
 *
 * When a filemark is read, we return '0 bytes read' and continue with the
 * next file after that.
 * When EOM is read, we return '0 bytes read' twice.
 * When the EOT marker is detected on writes, '0 bytes read' should be
 * returned twice. If user program does a MTNOP after that, 2 additional
 * blocks may be written.	------- FIXME: Implement this correctly  *************************************************
 *
 * Only read/writes in multiples of 512 bytes are accepted.
 * When no bytes are available, we sleep() until they are. The controller will
 * generate an interrupt, and we (should) get a wake_up() call.
 *
 * Simple buffering is used. User program should ensure that a large enough
 * buffer is used. Usually the drive does some buffering as well (something
 * like 4k or so).
 *
 * Scott S. Bertilson suggested to continue filling the user buffer, rather
 * than waste time on a context switch, when the kernel buffer fills up.
 */

/*
 * Problem: tar(1) doesn't always read the entire file. Sometimes the entire file
 * has been read, but the EOF token is never returned to tar(1), simply because
 * tar(1) knows it has already read all of the data it needs. So we must use
 * open/release to reset the `reported_read_eof' flag. If we don't, the next read
 * request would return the EOF flag for the previous file.
 */

static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf, int count)
{
	int error;
	dev_t dev = inode->i_rdev;
	unsigned short flags = filp->f_flags;
	unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
	int stat;

	if (TP_DIAGS(current_tape_dev))
		printk(TPQIC_NAME ": request READ, minor=%x, buf=%p, count=%x, pos=%x, flags=%x\n",
			MINOR(dev), buf, count, filp->f_pos, flags);

	if (count % TAPE_BLKSIZE) {	/* Only allow mod 512 bytes at a time. */
		tpqputs("Wrong block size");
		return -EINVAL;
	}

	/* Just assume everything is ok. Controller will scream if not. */

	if (status_bytes_wr)	/* Once written, no more reads, 'till after WFM. */
		return -EACCES;


	/* Make sure buffer is safe to write into. */
	error = verify_area(VERIFY_WRITE, buf, count);
	if (error) {
		printk(TPQIC_NAME ": read: verify_area(WRITE, %p, %x) failed\n", buf, count);
		return error;
	}

	/* This is rather ugly because it has to implement a finite state
	 * machine in order to handle the EOF situations properly.
	 */
	while (count>=0) {
		bytes_done = 0;
		/* see how much fits in the kernel buffer */
		bytes_todo = TPQBUF_SIZE;
		if (bytes_todo>count)
			bytes_todo = count;

		/* Must ensure that user program sees exactly one EOF token (==0) */
		if (return_read_eof==YES) {
			printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%d\n", return_read_eof, reported_read_eof, total_bytes_done);

			if (reported_read_eof==NO) {
				/* have not yet returned EOF to user program */
				if (total_bytes_done>0) {
					return total_bytes_done; /* next time return EOF */
				} else {
					reported_read_eof = YES; /* move on next time */
					return 0;		 /* return EOF */
				}				
			} else {
				/* Application program has already received EOF
				 * (above), now continue with next file on tape,
				 * if possible.
				 * When the FM is reached, EXCEPTION is set,
				 * causing a sense(). Subsequent read/writes will
				 * continue after the FM.
				 */
/*********** ?????????? this should check for (EOD|NDT), not EOM, 'cause we can read past EW: ************/
				if (status_eom_detected)
					/* If EOM, nothing left to read, so keep returning EOFs.
					 *** should probably set some flag to avoid clearing
					 *** status_eom_detected through ioctls or something
					 */
					return 0;
				else {
					/* just eof, there may be more files ahead... */
					return_read_eof = NO;
					reported_read_eof = NO;
					status_eof_detected = NO; /* reset this too */
					/*fall through*/
				}
			}
		}

/*****************************/
		if (bytes_todo==0)
			return total_bytes_done;

		if (bytes_todo>0) {
			/* start reading data */
			if (is_exception())	/****************************************/
				tpqputs("is_exception() before start_dma()!");
/******************************************************************
 ***** if start_dma() fails because the head is positioned 0 bytes
 ***** before the FM, (causing EXCEPTION to be set) return_read_eof should
 ***** be set to YES, and we should return total_bytes_done, rather than -ENXIO.
 ***** The app should recognize this as an EOF condition.
 ***************************************************************************/
			stat = start_dma(READ, bytes_todo);
			if (stat == TE_OK) {
				/* Wait for transfer to complete, interrupt should wake us */
				while (dma_mode != 0) {
					sleep_on(&tape_qic02_transfer);
				}
				if (status_error)
					return_read_eof = YES;
			} else if (stat != TE_END) {
				/* should do sense() on error here */
#if 0
				return -ENXIO;
#else
				printk("Trouble: stat==%02x\n", stat);
				return_read_eof = YES;
				/*************** check EOF/EOT handling!!!!!! **/
#endif
			}
			end_dma(&bytes_done);
			if (bytes_done>bytes_todo) {
				tpqputs("read: Oops, read more bytes than requested");
				return -EIO;
			}
			/* copy buffer to user-space in one go */
			if (bytes_done>0)
				memcpy_tofs( (void *) buf, (void *) buffaddr, bytes_done);
#if 1
			/* Checks Ton's patch below */
			if ((return_read_eof == NO) && (status_eof_detected == YES)) {
				printk(TPQIC_NAME ": read(): return_read_eof=%d, status_eof_detected=YES. return_read_eof:=YES\n", return_read_eof);
			}
#endif
			if ((bytes_todo != bytes_done) || (status_eof_detected == YES))
				/* EOF or EOM detected. return EOF next time. */
				return_read_eof = YES;
		} /* else: ignore read request for 0 bytes */

		if (bytes_done>0) {
			status_bytes_rd = YES;
			buf += bytes_done;
			filp->f_pos += bytes_done;
			total_bytes_done += bytes_done;
			count -= bytes_done;
		}
	}
	tpqputs("read request for <0 bytes");
	return -EINVAL;
} /* tape_qic02_read */



/* The drive detects near-EOT by means of the holes in the tape.
 * When the holes are detected, there is some space left. The drive
 * reports this as a TP_EOM exception. After clearing the exception,
 * the drive should accept two extra blocks.
 *
 * It seems there are some archiver programs that would like to use the
 * extra space for writing a continuation marker. The driver should return
 * end-of-file to the user program on writes, when the holes are detected.
 * If the user-program wants to use the extra space, it should use the
 * MTNOP ioctl() to get the generic status register and may then continue
 * writing (max 1kB).	----------- doesn't work yet...............
 *
 * EOF behaviour on writes:
 * If there is enough room, write all of the data.
 * If there is insufficient room, write as much as will fit and
 * return the amount written. If the requested amount differs from the
 * written amount, the application program should recognize that as the
 * end of file. Subsequent writes will return -ENOSPC.
 * Unless the minor bits specify a rewind-on-close, the tape will not
 * be rewound when it is full. The user-program should do that, if desired.
 * If the driver were to do that automatically, a user-program could be 
 * confused about the EOT/BOT condition after re-opening the tape device.
 *
 * Multiple volume support: Tar closes the tape device before prompting for
 * the next tape. The user may then insert a new tape and tar will open the
 * tape device again. The driver will detect an exception status in (No Cartridge)
 * and force a rewind. After that tar may continue writing.
 */
static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf, int count)
{
	int error;
	dev_t dev = inode->i_rdev;
	unsigned short flags = filp->f_flags;
	unsigned long bytes_todo, bytes_done, total_bytes_done = 0;

	if (TP_DIAGS(current_tape_dev))
		printk(TPQIC_NAME ": request WRITE, minor=%x, buf=%p, count=%x, pos=%x, flags=%x\n",
			MINOR(dev), buf, count, filp->f_pos, flags);

	if (count % TAPE_BLKSIZE) {	/* only allow mod 512 bytes at a time */
		tpqputs("Wrong block size");
		return -EINVAL;
	}

	if (mode_access==READ) {
		tpqputs("Not in write mode");
		return -EACCES;
	}

	/* open() does a sense() and we can assume the tape isn't changed
	 * between open() and release(), so the tperror.exs bits will still
	 * be valid.
	 */
	if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) {
		tpqputs("Cartridge is write-protected.");
		return -EACCES;	/* don't even try when write protected */
	}

	/* Make sure buffer is safe to read from. */
	error = verify_area(VERIFY_READ, buf, count);
	if (error) {
		printk(TPQIC_NAME ": write: verify_area(READ, %p, %x) failed\n", buf, count);
		return error;
	}

	if (doing_read == YES)
		terminate_read(0)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -