tpqic02.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,058 行 · 第 1/5 页

C
2,058
字号
		}		/* 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 (QIC02_TAPE_IFC == WANGTEK)	/* I think this is a drive-dependency, not IFC -- hhb */			if (stat & QIC02_STAT_READY) {	/* not ready */				tpqputs(TPQD_ALWAYS, "isr: ? Tape controller not ready");				r = 1;			}		flags = claim_dma_lock();		if ((i = get_dma_residue(QIC02_TAPE_DMA)) != 0) {			printk(TPQIC02_NAME ": dma_residue == %x !!!\n", i);			r = 1;	/* big trouble, but can't do much about it... */		}		release_dma_lock(flags);		if (r)			return IRQ_HANDLED;		/* 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(&qic02_tape_transfer);		} else {			/* start next transfer, account for track-switching time */			mod_timer(&tp_timer, jiffies + 6 * HZ);			dma_transfer();		}	} else {		printk(TPQIC02_NAME ": Unexpected interrupt, stat == %x\n", inb(QIC02_STAT_PORT));	}	return IRQ_HANDLED;}				/* qic02_tape_interrupt *//* 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 ssize_t qic02_tape_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos){	int type = iminor(filp->f_dentry->d_inode);	unsigned short flags = filp->f_flags;	unsigned long bytes_todo, bytes_done, total_bytes_done = 0;	int stat;	if (status_zombie == YES) {		tpqputs(TPQD_ALWAYS, "configs not set");		return -ENXIO;	}	if (TP_DIAGS(current_type))		printk(TPQIC02_NAME ": request READ, minor=%x, buf=%p, count=%lx, pos=%Lx, flags=%x\n", type, buf,		       (long) count, filp->f_pos, flags);	if (count % TAPE_BLKSIZE) {	/* Only allow mod 512 bytes at a time. */		tpqputs(TPQD_BLKSZ, "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;	}	/* This is rather ugly because it has to implement a finite state	 * machine in order to handle the EOF situations properly.	 */	while ((signed) 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) {			if (TPQDBG(DEBUG)) {				printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%lu\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(TPQD_DMAX, "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 */								wait_event(qic02_tape_transfer, dma_mode != 0);				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(TPQD_ALWAYS, "read: Oops, read more bytes than requested");				return -EIO;			}			/* copy buffer to user-space in one go */			if (bytes_done > 0) {				if (copy_to_user(buf, buffaddr, bytes_done))					return -EFAULT;			}#if 1			/* Checks Ton's patch below */			if ((return_read_eof == NO)  && (status_eof_detected == YES)) {				printk(TPQIC02_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;			*ppos += bytes_done;			total_bytes_done += bytes_done;			count -= bytes_done;		}	}	tpqputs(TPQD_ALWAYS, "read request for <0 bytes");	return -EINVAL;}				/* qic02_tape_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 ssize_t qic02_tape_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos){	int type = iminor(filp->f_dentry->d_inode);	unsigned short flags = filp->f_flags;	unsigned long bytes_todo, bytes_done, total_bytes_done = 0;	if (status_zombie == YES) {		tpqputs(TPQD_ALWAYS, "configs not set");		return -ENXIO;	}	if (TP_DIAGS(current_type)) {		printk(TPQIC02_NAME ": request WRITE, minor=%x, buf=%p, count=%lx, pos=%Lx, flags=%x\n",		       type, buf, (long) count, filp->f_pos, flags);	}	if (count % TAPE_BLKSIZE) {	/* only allow mod 512 bytes at a time */		tpqputs(TPQD_BLKSZ, "Wrong block size");		return -EINVAL;	}	if (mode_access == READ) {		tpqputs(TPQD_ALWAYS, "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(TPQD_ALWAYS, "Cartridge is write-protected.");		return -EACCES;	/* don't even try when write protected */	}	if (doing_read == YES) {		terminate_read(0);	}	while ((signed) count >= 0) {		/* see how much fits in the kernel buffer */		bytes_done = 0;		bytes_todo = TPQBUF_SIZE;		if (bytes_todo > count) {			bytes_todo = count;		}		if (return_write_eof == YES) {			/* return_write_eof should be reset on reverse tape movements. */			if (reported_write_eof == NO) {				if (bytes_todo > 0) {					tpqputs(TPQD_ALWAYS, "partial write");					/* partial write signals EOF to user program */				}				reported_write_eof = YES;				return total_bytes_done;			} else {				return -ENOSPC;	/* return error */			}		}		/* Quit when done. */		if (bytes_todo == 0) {			return total_bytes_done;		}		/* copy from user to DMA buffer and initiate transfer. */		if (bytes_todo > 0) {			if (copy_from_user(buffaddr, buf, bytes_todo))				return -EFAULT;/****************** similar problem with read() at FM could happen here at EOT. ******************//***** if at EOT, 0 bytes can be written. start_dma() will ***** fail and write() will return ENXIO error *****/			if (start_dma(WRITE, bytes_todo) != TE_OK) {				tpqputs(TPQD_ALWAYS, "write: start_dma() failed");				/* should do sense() on error here */				return -ENXIO;				/*********** FIXTHIS **************/			}			/* Wait for write to complete, interrupt should wake us. */			wait_event(qic02_tape_transfer, (status_error == 0 && dma_mode != 0));			end_dma(&bytes_done);			if (bytes_done > bytes_todo) {				tpqputs(TPQD_ALWAYS, "write: Oops, wrote more bytes than requested");				return -EIO;			}			/* If the dma-transfer was aborted because of an exception,			 * status_error will have been set in the interrupt handler.			 * Then end_dma() will do a sense().			 * If the exception was EXC_EOM, the EW-hole was encountered			 * and two more blocks could be written. For the time being we'll			 * just consider this to be the EOT.			 * Otherwise, something Bad happened, such as the maximum number			 * of block-rewrites was exceeded. [e.g. A very bad spot on tape was			 * encountered. Normally short dropouts are compensated for by			 * rewriting the block in error, up to 16 times. I'm not sure			 * QIC-24 drives can do this.]			 */			if (status_error) {				if (status_eom_detected == YES) {					tpqputs(TPQD_ALWAYS, "write: EW detected");					return_write_eof = YES;				} else {					/* probably EXC_RWA */					tpqputs(TPQD_ALWAYS, "write: dma: error in writing");					return -EIO;				}			}			if (bytes_todo != bytes_done) {				/* EOF or EOM detected. return EOT next time. */				return_write_eof = YES;			}		}		/* else: ignore write request for 0 bytes. */		if (bytes_done > 0) {			status_bytes_wr = YES;			buf += bytes_done;			*ppos += bytes_done;			total_bytes_done += bytes_done;			count -= bytes_done;		}	}	tpqputs(TPQD_ALWAYS, "write request for <0 bytes");	if (TPQDBG(DEBUG)) {		printk(TPQIC02_NAME ": status_bytes_wr %x, buf %p"		       ", total_bytes_done %lx, count %lx\n",		       status_bytes_wr, buf, total_bytes_done,		       (long) count);	}	return -EINVAL;}				/* qic02_tape_write *//* qic02_tape_open() * We allow the device to be opened, even if it is marked 'dead' because * we want to be able to reset the tape device without rebooting. * Only one open tape file at a time, except when minor=255. * Minor 255 is only allowed for resetting and always returns <0. *  * The density command is only allowed when TP_BOM is set. Thus, remember * the most recently used 

⌨️ 快捷键说明

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