OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [uclinux/] [uClinux-2.0.x/] [drivers/] [char/] [tpqic02.c] - Rev 1765

Compare with Previous | Blame | View Log

/* $Id: tpqic02.c,v 1.1.1.1 2001-09-10 07:44:17 simons Exp $
 *
 * Driver for tape drive support for Linux-i386 1.1.58
 *
 * Copyright (c) 1992, 1993, 1994 by H. H. Bergman. All rights reserved.
 * Current e-mail address: hennus@sky.ow.org [This is a UUCP link.]
 * [If you are unable to reach me directly, try the TAPE mailing list
 * channel on linux-activists@niksula.hut.fi using "X-Mn-Key: TAPE" as
 * the first line in your message.]
 *
 * Distribution of this program in executable form is only allowed if
 * all of the corresponding source files are made available through the same
 * medium at no extra cost.
 *
 * I will not accept any responsibility for damage caused directly or
 * indirectly by this program, or code derived from this program.
 *
 * Use this code at your own risk. Don't blame me if it destroys your data!
 * Make sure you have a backup before you try this code.
 *
 * If you make changes to my code and redistribute it in source or binary
 * form you must make it clear to even casual users of your code that you
 * have modified my code, clearly point out what the changes exactly are
 * (preferably in the form of a context diff file), how to undo your changes,
 * where the original can be obtained, and that complaints/requests about the
 * modified code should be directed to you instead of me.
 *
 * This driver was partially inspired by the 'wt' driver in the 386BSD
 * source distribution, which carries the following copyright notice:
 *
 *  Copyright (c) 1991 The Regents of the University of California.
 *  All rights reserved.
 *
 * You are not allowed to change this line nor the text above.
 *
 * $Log: not supported by cvs2svn $
 * Revision 1.1.1.1  2001/07/02 17:58:25  simons
 * Initial revision
 *
 * Revision 0.4.1.5  1994/10/29  02:46:13  root
 * Minor cleanups.
 *
 * Revision 0.4.1.4  1994/07/21  02:15:45  root
 * ifdef'd DDI. Exception masks.
 *
 * Revision 0.4.1.3  1994/05/03  01:49:09  root
 * Initial attempt at Mountain support for the Mountain 7150.
 * Based on patches provided by Erik Jacobson.
 *
 * Revision 0.4.1.2  1994/03/18  21:16:50  root
 * Many driver messages can now be turned off (runtime selectable).
 *
 * Revision 0.4.1.1  1994/02/16  19:47:22  root
 * First stab at runtime debug-variable.
 *
 * Revision 0.4  1994/02/15  01:53:16  root
 * DYNCONF mark II.
 * Minor cleanups.
 *
 * Revision 0.3  1994/02/07  01:23:16  root
 * More improved DYNCONF.
 * Archive changes & some cleanups by Eddy Olk.
 * Removed status_open, more cleanups, misc other.
 *
 * Revision 0.2.1.25  1994/01/24  02:01:33  root
 * Changed tape_qic02 to QIC02_TAPE.
 * Changes to prepare for DYNCONF.
 *
 * Revision 0.2.1.24  1994/01/23  07:27:18  root
 * Attempt to remove compilation warnings, G++ bug,
 * Linus changed TAPE_QIC02 to QIC02_TAPE.
 *
 * Revision 0.2.1.23  1994/01/20  23:49:28  root
 * Changed some exception decoding stuff.
 * TP_HAVE_SEEK, TP_HAVE_DENS. byte_swap_w() on arg, not global.
 * Attempt to fix cartridge-changed-problem for 2150L.
 * Release irq and dma reservations if initial reset fails.
 *
 * Revision 0.2.1.22  1994/01/19  20:56:55  root
 * Speed measuring stuff moved from aperf.h to delay.h.
 * BogoMips (tm) introduced by Linus.
 *
 * Revision 0.2.1.21  1993/06/18  19:04:33  root
 * minor fixes for 0.99.10.
 *
 * Revision 0.2.1.20  1993/06/11  21:38:51  root
 * Added exception code for status 0x8000 (Cypher weirdness).
 *
 * Revision 0.2.1.19  1993/04/19  23:13:59  root
 * Cleanups. Changed to 0.99.8.
 *
 * Revision 0.2.1.18  1993/03/22  17:39:47  root
 * Moved to 0.99.7. Added Archive MTSEEK and MTTELL support.
 *
 * Revision 0.2.1.17  1993/03/08  18:51:59  root
 * Tried to `fix' write-once bug in previous release.
 *
 * Revision 0.2.1.16  1993/03/01  00:06:16  root
 * Use register_chrdev() for 0.99.6.
 *
 * Revision 0.2.1.15  1993/02/25  00:14:25  root
 * minor cleanups.
 *
 * Revision 0.2.1.14  1993/01/25  00:06:14  root
 * Kernel udelay. Eof fixups.
 * Removed report_ read/write dummies; have strace(1) now.
 *
 * Revision 0.2.1.13  1993/01/10  02:24:43  root
 * Rewrote wait_for_ready() to use newer schedule() features.
 * This improves performance for rewinds etc.
 *
 * Revision 0.2.1.12  1993/01/05  18:44:09  root
 * Changes for 0.99.1. Fixed `restartable reads'.
 *
 * Revision 0.2.1.11  1992/11/28  01:19:10  root
 * Changes to exception handling (significant).
 * Changed returned error codes. Hopefully they're correct now.
 * Changed declarations to please gcc-2.3.1.
 * Patch to deal with bogus interrupts for Archive cards.
 *
 * Revision 0.2.1.10  1992/10/28  00:50:44  root
 * underrun/error counter needed byte swapping.
 *
 * Revision 0.2.1.9  1992/10/15  17:06:01  root
 * Removed online() stuff. Changed EOF handling.
 *
 * Revision 0.2.1.8  1992/10/02  22:25:48  root
 * Removed `no_sleep' parameters (got usleep() now),
 * cleaned up some comments.
 *
 * Revision 0.2.1.7  1992/09/27  01:41:55  root
 * Changed write() to do entire user buffer in one go, rather than just
 * a kernel-buffer sized portion each time.
 *
 * Revision 0.2.1.6  1992/09/21  02:15:30  root
 * Introduced udelay() function for microsecond-delays.
 * Trying to use get_dma_residue rather than TC flags.
 * Patch to fill entire user buffer on reads before
 * returning.
 *
 * Revision 0.2.1.5  1992/09/19  02:31:28  root
 * Some changes based on patches by Eddy Olk to
 * support Archive SC402/SC499R controller cards.
 *
 * Revision 0.2.1.4  1992/09/07  01:37:37  root
 * Minor changes
 *
 * Revision 0.2.1.3  1992/08/13  00:11:02  root
 * Added some support for Archive SC402 and SC499 cards.
 * (Untested.)
 *
 * Revision 0.2.1.2  1992/08/10  02:02:36  root
 * Changed from linux/system.h macros to asm/dma.h inline functions.
 *
 * Revision 0.2.1.1  1992/08/08  01:12:39  root
 * cleaned up a bit. added stuff for selftesting.
 * preparing for asm/dma.h instead of linux/system.h
 *
 * Revision 0.2  1992/08/03  20:11:30  root
 * Changed to use new IRQ allocation. Padding now done at runtime, pads to
 * 512 bytes. Because of this the page regs must be re-programmed every
 * block! Added hooks for selftest commands.
 * Moved to linux-0.97.
 *
 * Revision 0.1.0.5  1992/06/22  22:20:30  root
 * moved to Linux 0.96b
 *
 * Revision 0.1.0.4  1992/06/18  02:00:04  root
 * Use minor bit-7 to enable/disable printing of extra debugging info
 * when do tape access.
 * Added semop stuff for DMA/IRQ allocation checking. Don't think this
 * is the right way to do it though.
 *
 * Revision 0.1.0.3  1992/06/01  01:57:34  root
 * changed DRQ to DMA. added TDEBUG ifdefs to reduce output.
 *
 * Revision 0.1.0.2  1992/05/31  14:02:38  root
 * changed SET_DMA_PAGE handling slightly.
 *
 * Revision 0.1.0.1  1992/05/27  12:12:03  root
 * Can now use multiple files on tape (sort of).
 * First release.
 *
 * Revision 0.1  1992/05/26  01:16:31  root
 * Initial version. Copyright H. H. Bergman 1992
 *
 */
 
/* After the legalese, now the important bits:
 * 
 * This is a driver for the Wangtek 5150 tape drive with 
 * a QIC-02 controller for ISA-PC type computers.
 * Hopefully it will work with other QIC-02 tape drives as well.
 *
 * Make sure your setup matches the configuration parameters.
 * Also, be careful to avoid IO conflicts with other devices!
 */
 
 
/*
#define TDEBUG
*/
 
#define REALLY_SLOW_IO		/* it sure is ... */
 
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/mtio.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/tpqic02.h>
#include <linux/config.h>
#include <linux/mm.h>
 
#include <asm/dma.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
 
/* We really shouldn't be using this define.. */
#define IOCCMD_MASK 0x0000ffff
 
/* check existence of required configuration parameters */
#if !defined(QIC02_CMD_PORT) || \
    !defined(QIC02_TAPE_IRQ) || \
    !defined(QIC02_TAPE_DMA)
#error qic02_tape configuration error
#endif
 
 
#define TPQIC02_NAME	"tpqic02"
 
/* Linux outb() commands have (value,port) as parameters.
 * One might expect (port,value) instead, so beware!
 */
 
#ifdef CONFIG_QIC02_DYNCONF
/* This holds the dynamic configuration info for the interface
 * card+drive info if runtime configuration has been selected.
 */
struct mtconfiginfo qic02_tape_dynconf = { 0, };	/* user settable */
struct qic02_ccb qic02_tape_ccb = { 0, };		/* private stuff */
 
#else
 
unsigned long qic02_tape_debug;
 
# if ((QIC02_TAPE_IFC!=WANGTEK) && (QIC02_TAPE_IFC!=ARCHIVE) && (QIC02_TAPE_IFC!=MOUNTAIN))
#  error No valid interface card specified
# endif
#endif
 
static volatile int ctlbits = 0;     /* control reg bits for tape interface */
 
static struct wait_queue *qic02_tape_transfer = NULL; /* sync rw with interrupts */
 
static volatile struct mtget ioctl_status;	/* current generic status */
 
static volatile struct tpstatus tperror;	/* last drive status */
 
static char rcs_revision[] = "$Revision: 1.1.1.1 $";
static char rcs_date[] = "$Date: 2001-09-10 07:44:17 $";
 
/* Flag bits for status and outstanding requests.
 * (Could all be put in one bit-field-struct.)
 * Some variables need `volatile' because they may be modified
 * by an interrupt.
 */
static volatile flag status_dead = YES;	/* device is legally dead until proven alive */
static 		flag status_zombie = YES; /* it's `zombie' until irq/dma allocated */
 
static volatile flag status_bytes_wr = NO;	/* write FM at close or not */
static volatile flag status_bytes_rd = NO;	/* (rd|wr) used for rewinding */
 
static volatile unsigned long status_cmd_pending = 0; /* cmd in progress */
static volatile flag status_expect_int = NO;	/* ready for interrupts */
static volatile flag status_timer_on = NO; 	/* using time-out */
static volatile int  status_error = 0;		/* int handler may detect error */
static volatile flag status_eof_detected = NO;	/* end of file */
static volatile flag status_eom_detected = NO;	/* end of recorded media */
static volatile flag status_eot_detected = NO;	/* end of tape */
static volatile flag doing_read = NO;
static volatile flag doing_write = NO;
 
static volatile unsigned long dma_bytes_todo;
static volatile unsigned long dma_bytes_done;
static volatile unsigned dma_mode = 0;		/* !=0 also means DMA in use */
static 		flag need_rewind = YES;
 
static kdev_t current_tape_dev;
static int extra_blocks_left = BLOCKS_BEYOND_EW;
 
 
/* return_*_eof:
 *	NO:	not at EOF,
 *	YES:	tell app EOF was reached (return 0).
 *
 * return_*_eof==YES && reported_*_eof==NO  ==>
 *	return current buffer, next time(s) return EOF.
 *
 * return_*_eof==YES && reported_*_eof==YES  ==>
 *	at EOF and application knows it, so we can
 *	move on to the next file.
 *
 */
static flag return_read_eof = NO;	/* set to signal app EOF was reached */
static flag return_write_eof = NO;
static flag reported_read_eof = NO;	/* set when we've done that */
static flag reported_write_eof = NO;
 
 
/* This is for doing `mt seek <blocknr>' */
static char seek_addr_buf[AR_SEEK_BUF_SIZE];
 
 
/* In write mode, we have to write a File Mark after the last block written, 
 * when the tape device is closed. Tape repositioning and reading in write
 * mode is allowed as long as no actual writing has been done. After writing
 * the File Mark, repositioning and reading are allowed again.
 */
static int  mode_access;	/* access mode: READ or WRITE */
 
 
/* This is the actual kernel buffer where the interrupt routines read
 * from/write to. It is needed because the DMA channels 1 and 3 cannot
 * always access the user buffers. [The kernel buffer must reside in the
 * lower 16MBytes of system memory because of the DMA controller.]
 * The user must ensure that a large enough buffer is passed to the
 * kernel, in order to reduce tape repositioning.
 *
 * The buffer is 512 bytes larger than expected, because I want to align it
 * at 512 bytes, to prevent problems with 64k boundaries.
 */
 
static volatile char qic02_tape_buf[TPQBUF_SIZE+TAPE_BLKSIZE];
/* A really good compiler would be able to align this at 512 bytes... :-( */
 
static unsigned long buffaddr;	/* aligned physical address of buffer */
 
 
/* This translates minor numbers to the corresponding recording format: */
static const char *format_names[] = {
	"not set",	/* for dumb drives unable to handle format selection */
	"11",		/* extinct */
	"24",
	"120",
	"150",
	"300",		/* untested. */
	"600"		/* untested. */
};
 
 
/* `exception_list' is needed for exception status reporting.
 * Exceptions 1..14 are defined by QIC-02 rev F.
 * The drive status is matched sequentially to each entry,
 * ignoring irrelevant bits, until a match is found. If no
 * match is found, exception number 0 is used. (That should of
 * course never happen...) The original table was based on the
 * "Exception Status Summary" in QIC-02 rev F, but some changes
 * were required to make it work with real-world drives.
 *
 * Exception 2 (CNI) is changed to also cover status 0x00e0 (mask USL),
 * Exception 4 (EOM) is changed to also cover status 0x8288 (mask EOR),
 * Exception 11 (FIL) is changed to also cover status 0x0089 (mask EOM).
 * Exception 15 (EOR) is added for seek-to-end-of-data (catch EOR),
 * Exception 16 (BOM) is added for beginning-of-media (catch BOM).
 *
 * Had to swap EXC_NDRV and EXC_NCART to ensure that extended EXC_NCART
 * (because of the incorrect Wangtek status code) doesn't catch the
 * EXC_NDRV first.
 */
static struct exception_list_type {
	unsigned short mask, code;
	const char *msg;
	/* EXC_nr attribute should match with tpqic02.h */
} exception_list[] = {
	{0, 0,
		"Unknown exception status code",		/* extra: 0 */},
	{~(0), TP_ST0|TP_CNI|TP_USL|TP_WRP,
		"Drive not online"				/* 1 */},
		/* Drive presence goes before cartridge presence. */
	{~(TP_WRP|TP_USL), TP_ST0|TP_CNI,
		/* My Wangtek 5150EQ sometimes reports a status code
		 * of 0x00e0, which is not a valid exception code, but
		 * I think it should be recognized as "NO CARTRIDGE".
		 */
		"Cartridge not in place"			/* 2 */},
	{(unsigned short) ~(TP_ST1|TP_BOM), (TP_ST0|TP_WRP),
		"Write protected cartridge"			/* 3 */},
	{(unsigned short) ~(TP_ST1|TP_EOR), (TP_ST0|TP_EOM),
		"End of media"					/* 4 */},
	{~TP_WRP, TP_ST0|TP_UDA| TP_ST1|TP_BOM,
		"Read or Write abort. Rewind tape."		/* 5 */},
	{~TP_WRP, TP_ST0|TP_UDA,
		"Read error. Bad block transferred."		/* 6 */},
	{~TP_WRP, TP_ST0|TP_UDA|TP_BNL,
		"Read error. Filler block transferred."		/* 7 */},
	{~TP_WRP, TP_ST0|TP_UDA|TP_BNL |TP_ST1|TP_NDT,
		"Read error. No data detected."			/* 8 */},
	{~TP_WRP, TP_ST0|TP_EOM|TP_UDA|TP_BNL |TP_ST1|TP_NDT,
		"Read error. No data detected. EOM."		/* 9 */},
	{~(TP_WRP|TP_MBD|TP_PAR|TP_EOR), TP_ST0|TP_UDA|TP_BNL |TP_ST1|TP_NDT|TP_BOM,
		"Read error. No data detected. BOM."		/* 10 */},
	{~(TP_WRP|TP_EOM), TP_ST0|TP_FIL,
		/* Status 0x0089 (EOM & FM) is viewed as an FM,
		 * because it can only happen during a read.
		 * EOM is checked separately for an FM condition.
		 */
		"File mark detected"				/* 11 */},
	{~(TP_ST0|TP_CNI|TP_USL|TP_WRP|TP_BOM), TP_ST1|TP_ILL,
		"Illegal command"				/* 12 */},
	{~(TP_ST0|TP_CNI|TP_USL|TP_WRP|TP_BOM), TP_ST1|TP_POR,
		"Reset occurred"					/* 13 */},
	{~TP_WRP, TP_ST0|TP_FIL|TP_MBD,		/* NOTE: ST1 not set! */
		"Marginal block detected"			/* 14 */},
	{~(TP_ST0|TP_WRP|TP_EOM|TP_UDA|TP_BNL|TP_FIL |TP_NDT), TP_ST1|TP_EOR,
		/********** Is the extra TP_NDT really needed Eddy? **********/
		"End of recorded media"				/* extra: 15 */},
		/* 15 is returned when SEEKEOD completes successfully */
	{~(TP_WRP|TP_ST0), TP_ST1|TP_BOM,
		"Beginning of media"				/* extra: 16 */}
};
#define NR_OF_EXC	(sizeof(exception_list)/sizeof(struct exception_list_type))
 
 
 
static void tpqputs(unsigned long flags, const char *s)
{
	if ((flags & TPQD_ALWAYS) || (flags & QIC02_TAPE_DEBUG))
		printk(TPQIC02_NAME ": %s\n", s);
} /* tpqputs */
 
 
 
 
/* Perform byte order swapping for a 16-bit word.
 *
 * [FIXME] This should probably be in include/asm/
 * ([FIXME] i486 can do this faster)
 */
static inline void byte_swap_w(volatile unsigned short * w)
{
	int t = *w;
 
	*w = (t>>8) | ((t & 0xff)<<8);
}
 
 
 
/* Init control register bits on interface card.
 * For Archive, interrupts must be enabled explicitly.
 * Wangtek interface card requires ONLINE to be set, Archive SC402/SC499R
 * cards keep it active all the time.
 */
static void ifc_init(void)
{
	if (QIC02_TAPE_IFC == WANGTEK) /* || (QIC02_TAPE_IFC == EVEREX) */ {
		ctlbits = WT_CTL_ONLINE;	/* online */
		outb_p(ctlbits, QIC02_CTL_PORT);
 
	} else if (QIC02_TAPE_IFC == ARCHIVE) {
		ctlbits = 0;			/* no interrupts yet */
		outb_p(ctlbits, QIC02_CTL_PORT);
		outb_p(0, AR_RESET_DMA_PORT);	/* dummy write to reset DMA */
 
	} else /* MOUNTAIN */ {
		ctlbits = MTN_CTL_ONLINE;       /* online, and logic enabled */
		outb_p(ctlbits, QIC02_CTL_PORT);
	}
} /* ifc_init */
 
 
static void report_exception(unsigned n)
{
	if (n >= NR_OF_EXC) { tpqputs(TPQD_ALWAYS, "Oops -- report_exception"); n = 0; }
	if (TPQDBG(SENSE_TEXT) || n==0)
		printk(TPQIC02_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(unsigned s)
{
	int i;
 
	for (i=1; i<NR_OF_EXC; i++) {
		if ((s & exception_list[i].mask)==exception_list[i].code)
			return i;
	}
	printk(TPQIC02_NAME ": decode_exception_nr: exception(%x) not recognized\n", s);
	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(TPQD_ALWAYS, "[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(TPQD_ALWAYS, "[CRC failed!]");
					n = 6;
				}
			}
		}
		else if (s & TP_FIL) {
			if (s & TP_MBD) {	/* 14: Marginal block detected. CONTINUABLE */
				tpqputs(TPQD_ALWAYS, "[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.
 * should return a value to indicate stop/continue (in case of bad blocks)
 */
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(TPQD_ALWAYS, "[Bad block -- filler data transferred.]");
	else if (exnr==EXC_XBAD)
		tpqputs(TPQD_ALWAYS, "[CRC failed!]");
	else if (exnr==EXC_MARGINAL) {
		/* A marginal block behaves much like a FM.
		 * User may continue reading, if desired.
		 */
		tpqputs(TPQD_ALWAYS, "[Marginal block]");
		doing_read = NO;
	} else if (exnr==EXC_FM)
		doing_read = NO;
} /* handle_exception */
 
 
static inline int is_exception(void)
{
	return (inb(QIC02_STAT_PORT) & QIC02_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 */
 
	/* assert reset */
	if (QIC02_TAPE_IFC == MOUNTAIN)
		outb_p(ctlbits & ~MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT);
	else /* WANGTEK, ARCHIVE */
 		outb_p(ctlbits | QIC02_CTL_RESET, QIC02_CTL_PORT);
 
	/* 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;
 
	/* de-assert reset */
	if (QIC02_TAPE_IFC == MOUNTAIN)
		outb_p(ctlbits | MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT);
	else
		outb_p(ctlbits & ~QIC02_CTL_RESET, QIC02_CTL_PORT);
 
	/* KLUDGE FOR G++ BUG */
	{ int stat = inb_p(QIC02_STAT_PORT);
	  status_dead = ((stat & QIC02_STAT_RESETMASK) != QIC02_STAT_RESETVAL); }
	/* if successful, inb(STAT) returned RESETVAL */
	if (status_dead == YES)
		printk(TPQIC02_NAME ": reset failed!\n");
	else if (verbose)
		printk(TPQIC02_NAME ": reset successful\n");
 
	return (status_dead == YES)? 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, QIC02_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, QIC02_CMD_PORT);    /* output the command */
 
	/* wait 1 usec before asserting /REQUEST */
	udelay(1);
 
	if ((!ignore_ex) && is_exception()) {
		tpqputs(TPQD_ALWAYS, "*** exception detected in notify_cmd");
		/** force a reset here **/
		if (tape_reset(1)==TE_DEAD)
			return TE_DEAD;
		if (is_exception()) {
			tpqputs(TPQD_ALWAYS, "exception persists after reset.");
			tpqputs(TPQD_ALWAYS, " ^ exception ignored.");
		}
	}
 
	outb_p(ctlbits | QIC02_CTL_REQUEST, QIC02_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(QIC02_STAT_PORT) & QIC02_STAT_READY) && (--i>0))
		/*skip*/;			  /* wait for ready */
	if (i==0) {
		tpqputs(TPQD_ALWAYS, "timed out waiting for ready in notify_cmd");
		status_dead = YES;
		return TE_TIM;
	}
 
	outb_p(ctlbits & ~QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* reset request bit */
	i = TAPE_NOTIFY_TIMEOUT;
	/* according to the specs this one should never time-out */
	while (((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) == 0) && (--i>0))
		/*skip*/;			  /* wait for not ready */
	if (i==0) {
		tpqputs(TPQD_ALWAYS, "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(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (--spin_t>0))
		/*SKIP*/;
	if ((stat & QIC02_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(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (jiffies<spin_t))
		schedule();		/* don't waste all the CPU time */
	if ((stat & QIC02_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(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (jiffies<spin_t)) {
		/* be `nice` to other processes on long operations... */
		current->timeout = jiffies + 3*HZ/10;	/* 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 & QIC02_STAT_MASK) == QIC02_STAT_MASK) {
		tpqputs(TPQD_ALWAYS, "wait_for_ready() timed out");
		return TE_TIM;
	}
 
	if ((stat & QIC02_STAT_EXCEPTION) == 0) {
		tpqputs(TPQD_ALWAYS, "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(QIC02_STAT_PORT);
	if ((stat & QIC02_STAT_EXCEPTION) == 0) {	/* if exception */
		tpqputs(TPQD_ALWAYS, "send_qic02_cmd: Exception!");
		return TE_EX;
	}
	if (stat & QIC02_STAT_READY) {			/* if not ready */
		tpqputs(TPQD_ALWAYS, "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 (TP_HAVE_SEEK && (cmd == AR_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(TPQD_ALWAYS, "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(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_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(TPQD_ALWAYS, "waiting looong in rdstatus() -- drive dead?");
		while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK)
			schedule();
		tpqputs(TPQD_ALWAYS, "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(TPQIC02_NAME ": reading status bytes: ");
 
	for (q=stp; q<stp+size; q++)
	{
		do s = inb_p(QIC02_STAT_PORT);
		while ((s & QIC02_STAT_MASK) == QIC02_STAT_MASK);	/* wait for ready or exception */
 
		if ((s & QIC02_STAT_EXCEPTION) == 0) {		/* if exception */
			tpqputs(TPQD_ALWAYS, "rdstatus: exception error");
			ioctl_status.mt_erreg = 0;		/* dunno... */
			return TE_NS;				/* error, shouldn't happen... */
		}
 
		*q = inb_p(QIC02_DATA_PORT);			/* read status byte */
 
		if (TP_DIAGS(current_tape_dev))
			printk("[%1d]=0x%x  ", q-stp, (unsigned) (*q) & 0xff);
 
		outb_p(ctlbits | QIC02_CTL_REQUEST, QIC02_CTL_PORT);	/* set request */
 
		while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) == 0);	/* wait for not ready */
 
		udelay(22);	/* delay >20 usec */
 
		outb_p(ctlbits & ~QIC02_CTL_REQUEST, QIC02_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(QIC02_STAT_PORT) & QIC02_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(volatile struct tpstatus *stp)
{
	int stat = rdstatus((char *) stp, TPSTATSIZE, QCMD_RD_STAT);
#if defined(i386) || defined(i486)
	byte_swap_w(&(stp->dec));
	byte_swap_w(&(stp->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(TPQD_ALWAYS, "Attempting to read Extended Status 3...");
	stat = rdstatus(vus, sizeof(vus), QCMD_RD_STAT_X3);
	if (stat != TE_OK)
		return stat;
 
	tpqputs(TPQD_ALWAYS, "Returned status bytes:");
	for (i=0; i<sizeof(vus); i++) {
		if ( i % 8 == 0 )
			printk("\n" TPQIC02_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);
 
	if (TPQDBG(SENSE_TEXT))
		printk(TPQIC02_NAME ": tp_sense(ignore=0x%x) enter\n", ignore);
 
	/* sense() is not allowed during a read or write cycle */
	if (doing_write == YES)
		tpqputs(TPQD_ALWAYS, "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(&tperror) != TE_OK) {
		tpqputs(TPQD_ALWAYS, "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(TPQIC02_NAME ": tp_sense: status: %x, error count: %d, underruns: %d\n",
			tperror.exs, tperror.dec, tperror.urc);
	else if ((tperror.dec!=0) || (tperror.urc!=0) || TPQDBG(SENSE_CNTS))
		printk(TPQIC02_NAME ": tp_sense: no hard errors, 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 & (TP_ST0|TP_ST1)) {
		/* My Wangtek occasionally reports `status' 1212 which should be ignored. */
		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(QIC02_STAT_PORT) & QIC02_STAT_MASK;
	if (TPQDBG(REWIND))
		printk(TPQIC02_NAME ": Waiting for (re-)wind to finish: stat=0x%x\n", stat);
 
	stat = wait_for_ready(timeout);
 
	if (stat != TE_OK) {
			tpqputs(TPQD_ALWAYS, "(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 == YES) {
		tpqputs(TPQD_ALWAYS, "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(TPQIC02_NAME ": ll_do_qic_cmd(%x, %ld) failed\n", cmd, (long) timeout);
		return -EIO;
	}
 
 
#if OBSOLETE
	/* wait for ready since it may not be active immediately after reading status */
	while ((inb_p(QIC02_STAT_PORT) & QIC02_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(TPQIC02_NAME ": Exception persist in ll_do_qic_cmd[1](%x, %ld)", cmd, (long) timeout);
			status_dead = YES;
			return -ENXIO;
			/* if rdstatus fails too, we're in trouble */
		}
	}
	else if (stat!=TE_OK) {
		printk(TPQIC02_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(TPQIC02_NAME ": Exception persist in ll_do_qic_cmd[2](%x, %ld)\n", cmd, (long) 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(TPQIC02_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(TPQD_ALWAYS, "terminating pending read-cycle");
 
			/* I'm not too sure about this part  -- hhb */
			if (QIC02_TAPE_IFC == MOUNTAIN) {
				/* Mountain reference says can terminate by de-asserting online */
				ctlbits &= ~MTN_QIC02_CTL_ONLINE;
			}
 
			if (tp_sense(TP_FIL|TP_EOM|TP_WRP) != TE_OK) {
				tpqputs(TPQD_ALWAYS, "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(TPQD_ALWAYS, "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(TPQD_ALWAYS, "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(TPQD_ALWAYS, "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(TPQD_REWIND, "Rewinding tape...");
		stat = ll_do_qic_cmd(QCMD_REWIND, TIM_R);
		if (stat != 0) {
			printk(TPQIC02_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(TPQD_IOCTLS, "MTFSF forward searching filemark");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EACCES;
			return do_qic_cmd(QCMD_RD_FM, TIM_F);
 
		case MTBSF:
			if (TP_HAVE_BSF) {
				tpqputs(TPQD_IOCTLS, "MTBSF backward searching filemark -- optional command");
				if ((mode_access==WRITE) && status_bytes_wr)
					return -EACCES;
				stat = do_qic_cmd(QCMD_RD_FM_BCK, TIM_F);
			} else {
				stat = -ENXIO;
			}
			status_eom_detected = status_eof_detected = NO;
			return stat;
 
		case MTFSR:
			if (TP_HAVE_FSR) { /* This is an optional QIC-02 command */
				tpqputs(TPQD_IOCTLS, "MTFSR forward space record");
				if ((mode_access==WRITE) && status_bytes_wr)
					return -EACCES;
				stat = do_qic_cmd(QCMD_SPACE_FWD, TIM_F);
			} else {
				/**** fake it by doing a read data block command? ******/
				tpqputs(TPQD_IOCTLS, "MTFSR not supported");
				stat = -ENXIO;
			}
			return stat;
 
		case MTBSR:
			if (TP_HAVE_BSR) { /* This is an optional QIC-02 command */
				/* we need this for appending files with GNU tar!! */
				tpqputs(TPQD_IOCTLS, "MTFSR backward space record");
				if ((mode_access==WRITE) && status_bytes_wr)
					return -EACCES;
				stat = do_qic_cmd(QCMD_SPACE_BCK, TIM_F);
			} else {
				tpqputs(TPQD_IOCTLS, "MTBSR not supported");
				stat = -ENXIO;
			}
			status_eom_detected = status_eof_detected = NO;
			return stat;
 
		case MTWEOF:
			tpqputs(TPQD_IOCTLS, "MTWEOF write eof mark");
			/* Plain GNU mt(1) 2.2 uses read-only mode for writing FM. :-( */
			if (mode_access==READ)
				return -EACCES;
 
			/* allow tape movement after writing FM */
			status_bytes_rd = status_bytes_wr;	/* Kludge-O-Matic */
			status_bytes_wr = NO;
			return do_qic_cmd(QCMD_WRT_FM, TIM_M);
			/* not sure what to do with status_bytes when WFM should fail */
 
		case MTREW:
			tpqputs(TPQD_IOCTLS, "MTREW rewinding tape");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EACCES;
			status_eom_detected = status_eof_detected = NO;
			return do_qic_cmd(QCMD_REWIND, TIM_R);
 
		case MTOFFL:
			tpqputs(TPQD_IOCTLS, "MTOFFL rewinding & going offline");
			/* Doing a drive select will clear (unlock) the current drive.
			 * But that requires support for multiple drives and locking.
			 */
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EACCES;
			status_eom_detected = status_eof_detected = NO;
			/**** do rewind depending on minor bits??? ***/
			stat = do_qic_cmd(QCMD_REWIND, TIM_R);
			return stat;
 
		case MTNOP:
			tpqputs(TPQD_IOCTLS, "MTNOP setting status only");
			/********** should do `read position' for drives that support it **********/
			return (tp_sense(-1)==TE_OK)? 0 : -EIO;	/**** check return codes ****/
 
		case MTRETEN:
			tpqputs(TPQD_IOCTLS, "MTRETEN retension tape");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EACCES;
			status_eom_detected = status_eof_detected = NO;
			return do_qic_cmd(QCMD_RETEN, TIM_R);
 
		case MTBSFM:
			/* Think think is like MTBSF, except that
			 * we shouldn't skip the FM. Tricky.
			 * Maybe use RD_FM_BCK, then do a SPACE_FWD?
			 */
			tpqputs(TPQD_IOCTLS, "MTBSFM not supported");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EACCES;
			return -ENXIO;
 
		case MTFSFM:
			/* I think this is like MTFSF, except that
			 * we shouldn't skip the FM. Tricky.
			 * Maybe use QCMD_RD_DATA until we get a TP_FIL exception?
			 * But then the FM will have been skipped...
			 * Maybe use RD_FM, then RD_FM_BCK, but not all
			 * drives will support that!
			 */
			tpqputs(TPQD_IOCTLS, "MTFSFM not supported");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EACCES;
			return -ENXIO;
 
		case MTEOM:
			/* This should leave the tape ready for appending
			 * another file to the end, such that it would append
			 * after the last FM on tape.
			 */
			tpqputs(TPQD_IOCTLS, "MTEOM search for End Of recorded Media");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EACCES;
			if (TP_HAVE_EOD) {
				/* Use faster seeking when possible.
				 * This requires the absence of data beyond the EOM.
				 * It seems that my drive does not always perform the
				 * SEEK_EOD correctly, unless it is preceded by a
				 * rewind command.
				 */
# if 0
				status_eom_detected = status_eof_detected = NO;
# endif
				stat = do_qic_cmd(QCMD_REWIND, TIM_R);
				if (stat)
					return stat;
				stat = do_qic_cmd(QCMD_SEEK_EOD, TIM_F);
				/* After a successful seek, TP_EOR should be returned */
			} else {
				/* else just seek until the drive returns exception "No Data" */
				stat = 0;
				while ((stat==0) && (!status_eom_detected)) {
					stat = do_qic_cmd(QCMD_RD_FM, TIM_F); /***** should use MTFSFM here???? ******/
				}
				if (tperror.exs & TP_NDT)
					return 0;
			}
			return stat;
 
		case MTERASE:
			tpqputs(TPQD_IOCTLS, "MTERASE -- ERASE TAPE !");
			if  ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) {
				tpqputs(TPQD_ALWAYS, "Cartridge is write-protected.");
				return -EACCES;
			} else {
				time_t t = jiffies;
 
				/* Plain GNU mt(1) 2.2 erases a tape in O_RDONLY. :-( */
				if (mode_access==READ) 
					return -EACCES;
 
				/* give user a few seconds to pull out tape */
				while (jiffies - t < 4*HZ)
					schedule();
			}
 
			/* don't bother writing filemark first */
			status_eom_detected = status_eof_detected = NO;
			return do_qic_cmd(QCMD_ERASE, TIM_R);
 
		case MTRAS1:
			if (TP_HAVE_RAS1) {
				tpqputs(TPQD_IOCTLS, "MTRAS1: non-destructive self test");
				stat = do_qic_cmd(QCMD_SELF_TST1, TIM_R);
				if (stat != 0) {
					tpqputs(TPQD_ALWAYS, "RAS1 failed");
					return stat;
				}
				return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
			}
			tpqputs(TPQD_IOCTLS, "RAS1 not supported");
			return -ENXIO;
 
		case MTRAS2:
			if (TP_HAVE_RAS2) {
				tpqputs(TPQD_IOCTLS, "MTRAS2: destructive self test");
				stat = do_qic_cmd(QCMD_SELF_TST2, TIM_R);
				if (stat != 0) {
					tpqputs(TPQD_ALWAYS, "RAS2 failed");
					return stat;
				}
				return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
			}
			tpqputs(TPQD_IOCTLS, "RAS2 not supported");
			return -ENXIO;
 
		case MTSEEK:
			if (TP_HAVE_SEEK && (QIC02_TAPE_IFC==ARCHIVE)) {
				tpqputs(TPQD_IOCTLS, "MTSEEK seeking block");
				if ((mode_access==WRITE) && status_bytes_wr)
					return -EACCES;
				/* NOTE: address (24 bits) is in seek_addr_buf[] */
				return do_qic_cmd(AR_QCMDV_SEEK_BLK, TIM_F);
			}
			else
				return -ENOTTY;
 
		default:
			return -ENOTTY;
	}
} /* do_ioctl_cmd */
 
 
/* dma_transfer(): This routine is called for every 512 bytes to be read
 * from/written to the tape controller. Speed is important here!
 * (There must be enough time left for the hd controller!)
 * When other devices use DMA they must ensure they use un-interruptible
 * double byte accesses to the DMA controller. Floppy.c is ok.
 * Must have interrupts disabled when this function is invoked,
 * otherwise, the double-byte transfers to the DMA controller will not
 * be atomic. That could lead to nasty problems when they are interrupted
 * by other DMA interrupt-routines.
 *
 * This routine merely does the least possible to keep
 * the transfers going:
 *	- set the DMA count register for the next 512 bytes
 *	- adjust the DMA address and page registers
 *	- adjust the timeout
 *	- tell the tape controller to start transferring
 * We assume the dma address and mode are, and remain, valid.
 */ 
static inline void dma_transfer(void)
{
 
	if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */
		outb_p(WT_CTL_ONLINE, QIC02_CTL_PORT);	/* back to normal */
	else if (QIC02_TAPE_IFC == ARCHIVE)
		outb_p(0, AR_RESET_DMA_PORT);
	else /* QIC02_TAPE_IFC == MOUNTAIN */
		outb_p(ctlbits, QIC02_CTL_PORT);
 
 
	clear_dma_ff(QIC02_TAPE_DMA);
	set_dma_mode(QIC02_TAPE_DMA, dma_mode);
	set_dma_addr(QIC02_TAPE_DMA, buffaddr+dma_bytes_done);	/* full address */
	set_dma_count(QIC02_TAPE_DMA, TAPE_BLKSIZE);
 
	/* start tape DMA controller */
	if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */
		outb_p(WT_CTL_DMA | WT_CTL_ONLINE, QIC02_CTL_PORT); /* trigger DMA transfer */
 
	else if (QIC02_TAPE_IFC == ARCHIVE) {
		outb_p(AR_CTL_IEN | AR_CTL_DNIEN, QIC02_CTL_PORT);  /* enable interrupts again */
		outb_p(0, AR_START_DMA_PORT);			  /* start DMA transfer */
		/* In dma_end() AR_RESET_DMA_PORT is written too. */
 
	} else /* QIC02_TAPE_IFC == MOUNTAIN */ {
		inb(MTN_R_DESELECT_DMA_PORT);
		outb_p(ctlbits | (MTN_CTL_EXC_IEN | MTN_CTL_DNIEN), QIC02_CTL_PORT);
		outb_p(0, MTN_W_SELECT_DMA_PORT);	 /* start DMA transfer */
		if (dma_mode == DMA_MODE_WRITE)
			outb_p(0, MTN_W_DMA_WRITE_PORT); /* start DMA transfer */
	}
 
	/* start computer DMA controller */
	enable_dma(QIC02_TAPE_DMA);
	/* block transfer should start now, jumping to the 
	 * interrupt routine when done or an exception was detected.
	 */
} /* dma_transfer */
 
 
/* start_dma() sets a DMA transfer up between the tape controller and
 * the kernel qic02_tape_buf buffer.
 * Normally bytes_todo==dma_bytes_done at the end of a DMA transfer. If not,
 * a filemark was read, or an attempt to write beyond the End Of Tape 
 * was made. [Or some other bad thing happened.]
 * Must do a sense() before returning error.
 */
static int start_dma(short mode, unsigned long bytes_todo)
/* assume 'bytes_todo'>0 */
{
	int stat;
 
	tpqputs(TPQD_DEBUG, "start_dma() enter");
	TPQDEB({printk(TPQIC02_NAME ": doing_read==%d, doing_write==%d\n", doing_read, doing_write);})
 
	dma_bytes_done = 0;
	dma_bytes_todo = bytes_todo;
	status_error = NO;
	/* dma_mode!=0 indicates that the dma controller is in use */
	dma_mode = (mode == WRITE)? DMA_MODE_WRITE : DMA_MODE_READ;	
 
	/* Only give READ/WRITE DATA command to tape drive if we haven't
	 * done that already. Otherwise the drive will rewind to the beginning
	 * of the current file on tape. Any QIC command given other than
	 * R/W FM will break the read/write transfer cycle.
	 * do_qic_cmd() will terminate doing_{read,write}
	 */
	if ((doing_read == NO) && (doing_write == NO)) {
		/* First, we have to clear the status -- maybe remove TP_FIL???
		 */
 
#if 0
		/* Next dummy get status is to make sure CNI is valid,
                   since we're only just starting a read/write it doesn't
                   matter some exceptions are cleared by reading the status;
                   we're only interested in CNI and WRP. -Eddy */
		get_status(&tperror);
#else
		/* TP_CNI should now be handled in open(). -Hennus */
#endif
 
		stat = tp_sense(((mode == WRITE)? 0 : TP_WRP) | TP_BOM | TP_FIL);
		if (stat != TE_OK)
			return stat;
 
#if OBSOLETE
		/************* not needed iff rd_status() would wait for ready!!!!!! **********/
		if (wait_for_ready(TIM_S) != TE_OK) {	/*** not sure this is needed ***/
			tpqputs(TPQD_ALWAYS, "wait_for_ready failed in start_dma");
			return -EIO;
		}
#endif
 
		if (QIC02_TAPE_IFC == MOUNTAIN) {
			/* Set control bits to select ONLINE during command */
			ctlbits |= MTN_QIC02_CTL_ONLINE;
		}
 
		/* Tell the controller the data direction */
 
		/* r/w, timeout medium, check exceptions, sets status_cmd_pending. */
		stat = send_qic02_cmd((mode == WRITE)? QCMD_WRT_DATA : QCMD_RD_DATA, TIM_M, 0);
		if (stat!=TE_OK) {
			printk(TPQIC02_NAME ": start_dma: init %s failed\n",
				(mode == WRITE)? "write" : "read");
			(void) tp_sense(0);
			return stat;
		}
 
		/* Do this last, because sense() will clear the doing_{read,write}
		 * flags, causing trouble next time around.
		 */
		if (wait_for_ready(TIM_M) != TE_OK)
			return -EIO;
		switch (mode) {
			case READ:
				doing_read = YES;
				break;
			case WRITE:
				doing_write = YES;
				break;
			default:
				printk(TPQIC02_NAME ": requested unknown mode %d\n", mode);
				panic(TPQIC02_NAME ": invalid mode in start_dma()");
		}
 
	} else if (is_exception()) {
		/* This is for Archive drives, to handle reads with 0 bytes
		 * left for the last read request.
		 *
		 * ******** this also affects EOF/EOT handling! ************
		 */
		tpqputs(TPQD_ALWAYS, "detected exception in start_dma() while transfer in progress");
		status_error = YES;
		return TE_END;
	}
 
 
	status_expect_int = YES;
 
	/* This assumes tape is already positioned, but these
	 * semi-'intelligent' drives are unpredictable...
	 */
	TIMERON(TIM_M*2);
 
	/* initiate first data block read from/write to the tape controller */
 
	cli();
	dma_transfer();
	sti();
 
	TPQPUTS("start_dma() end");
	return TE_OK;
} /* start_dma */
 
 
/* This cleans up after the dma transfer has completed
 * (or failed). If an exception occurred, a sense()
 * must be done. If the exception was caused by a FM,
 * sense() will set `status_eof_detected' and
 * `status_eom_detected', as required.
 */
static void end_dma(unsigned long * bytes_done)
{
	int stat = TE_OK;
 
	TIMEROFF;
 
	TPQPUTS("end_dma() enter");
 
	disable_dma(QIC02_TAPE_DMA);
	clear_dma_ff(QIC02_TAPE_DMA);
 
	if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */
		outb_p(WT_CTL_ONLINE, QIC02_CTL_PORT);	/* back to normal */
	else if (QIC02_TAPE_IFC == ARCHIVE)
		outb_p(0, AR_RESET_DMA_PORT);
	else /* QIC02_TAPE_IFC == MOUNTAIN */ {
		/* Clear control bits, de-select ONLINE during tp_sense */
		ctlbits &= ~MTN_QIC02_CTL_ONLINE;
	}
 
	stat = wait_for_ready(TIM_M);
	if (status_error || (stat!=TE_OK)) {
		tpqputs(TPQD_DMAX, "DMA transfer exception");
		stat = tp_sense((dma_mode==READ)? TP_WRP : 0);
		/* no return here -- got to clean up first! */
	} else /* if (QIC02_TAPE_IFC == MOUNTAIN) */ {
		outb_p(ctlbits, QIC02_CTL_PORT);
	}
 
	if (QIC02_TAPE_IFC == MOUNTAIN)
		inb(MTN_R_DESELECT_DMA_PORT);
 
 	/* take the tape controller offline */
 
	/* 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 ***********/
 
 
/* qic02_tape_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 qic02_tape_times_out(void)
{
	printk("time-out in %s driver\n", TPQIC02_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(&qic02_tape_transfer);
		}
	}
} /* qic02_tape_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???????]
 */
 
 
/* qic02_tape_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 qic02_tape_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	int stat, r, i;
 
	TIMEROFF;
 
	if (status_expect_int) {
#ifdef WANT_EXTRA_FULL_DEBUGGING
		if (TP_DIAGS(current_tape_dev))
			printk("@");
#endif
		stat = inb(QIC02_STAT_PORT);	/* Knock, knock */
		if (QIC02_TAPE_IFC == ARCHIVE) {	/* "Who's there?" */
			if (((stat & (AR_STAT_DMADONE)) == 0) &&
        	              ((stat & (QIC02_STAT_EXCEPTION)) != 0)) {
				TIMERCONT;
				return;			/* "Linux with IRQ sharing" */
			}
		}
 
		if ((stat & QIC02_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(TPQD_ALWAYS, "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(&qic02_tape_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 (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;
			}
 
		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... */
		}
 
		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(&qic02_tape_transfer);
		} else {
			/* start next transfer, account for track-switching time */
			timer_table[QIC02_TAPE_TIMER].expires = jiffies + 6*HZ;
			dma_transfer();
		}
	} else {
		printk(TPQIC02_NAME ": Unexpected interrupt, stat == %x\n",
		       inb(QIC02_STAT_PORT));
	}
} /* qic02_tape_interrupt */
 
 
static int qic02_tape_lseek(struct inode * inode, struct file * file, off_t offset, int origin)
{
	return -EINVAL;	/* not supported */
} /* qic02_tape_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 qic02_tape_read(struct inode * inode, struct file * filp, char * buf, int count)
{
	int error;
	kdev_t dev = inode->i_rdev;
	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_tape_dev))
		/* can't print a ``long long'' (for filp->f_pos), so chop it */
		printk(TPQIC02_NAME ": request READ, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n",
			MINOR(dev), buf, count, (unsigned long) 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;
 
 
	/* Make sure buffer is safe to write into. */
	error = verify_area(VERIFY_WRITE, buf, count);
	if (error)
		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) {
			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 */
				while (dma_mode != 0) {
					sleep_on(&qic02_tape_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(TPQD_ALWAYS, "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(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;
			filp->f_pos += 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 int qic02_tape_write(struct inode * inode, struct file * filp, const char * buf, int count)
{
	int error;
	kdev_t dev = inode->i_rdev;
	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_tape_dev))
		/* can't print a ``long long'' (for filp->f_pos), so chop it */
		printk(TPQIC02_NAME ": request WRITE, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n",
			MINOR(dev), buf, count, (unsigned long) 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 */
	}
 
	/* Make sure buffer is safe to read from. */
	error = verify_area(VERIFY_READ, buf, count);
	if (error)
		return error;
 
	if (doing_read == YES)
		terminate_read(0);
 
	while (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) {
			memcpy_fromfs( (void *) buffaddr, (const void *) buf, bytes_todo);
 
/****************** 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. */
			while ((status_error == 0) && (dma_mode != 0)) {
				sleep_on(&qic02_tape_transfer);
			}
 
			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;
			filp->f_pos += 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 %x\n", status_bytes_wr, buf, total_bytes_done, 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 minor bits. When they are different from the
 * remembered values, rewind the tape and set the required density.
 * Don't rewind if the minor bits specify density 0.
 */
static int qic02_tape_open(struct inode * inode, struct file * filp)
{
	kdev_t dev = inode->i_rdev;
	unsigned short flags = filp->f_flags;
	unsigned short dens = 0;
	int s;
 
 
	if (TP_DIAGS(dev)) {
		printk("qic02_tape_open: dev=%s, flags=%x     ",
		       kdevname(dev), flags);
	}
 
	if (MINOR(dev)==255)	/* special case for resetting */
		if (suser())
			return (tape_reset(1)==TE_OK) ? -EAGAIN : -ENXIO;
		else
			return -EPERM;
 
	if (status_dead==YES)
		/* Allow `mt reset' ioctl() even when already open()ed. */
		return 0;
 
	/* Only one at a time from here on... */
	if (filp->f_count>1) {	/* filp->f_count==1 for the first open() */
		return -EBUSY;
	}
 
	if (status_zombie==YES)
		/* no irq/dma/port stuff allocated yet, no reset done
		 * yet, so return until MTSETCONFIG has been done.
		 */
		return 0;
 
	status_bytes_rd = NO;
	status_bytes_wr = NO;
 
	return_read_eof = NO;	/********????????????????*****/
	return_write_eof = (status_eot_detected)? YES : NO;
 
	/* Clear this in case user app close()d before reading EOF token */
	status_eof_detected = NO;
 
	reported_read_eof = NO;
	reported_write_eof = NO;
 
 
	switch (flags & O_ACCMODE) {
		case O_RDONLY:
			mode_access = READ;
			break;
		case O_WRONLY:	/* Fallthru... Strictly speaking this is not correct... */
		case O_RDWR:	/* Reads are allowed as long as nothing is written */
			mode_access = WRITE;
			break;
	}
 
	/* This is to avoid tape-changed problems (TP_CNI exception).
	 *
	 * Since removing the cartridge will not raise an exception,
	 * we always do a tp_sense() to make sure we have the proper
	 * CNI status, the 2150L may need an additional sense.... - Eddy
	 */
	s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR);
 
	if (s == TE_OK)
		/* Try to clear cartridge-changed status for Archive-2150L */
		if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI))
			s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR);
 
	if (s != TE_OK) {
		tpqputs(TPQD_ALWAYS, "open: sense() failed");
		return -EIO;
	}
 
	/* exception bits should be up-to-date now, so check for
	 * tape presence and exit if absent.
	 * Even `mt stat' will fail without a tape.
	 */
	if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) {
		tpqputs(TPQD_ALWAYS, "No tape present.");
		return -EIO;
	}
 
	/* At this point we can assume that a tape is present and
	 * that it will remain present until release() is called.
	 */
 
	/* not allowed to do QCMD_DENS_* unless tape is rewound */
	if ((TP_DENS(dev)!=0) && (TP_DENS(current_tape_dev) != TP_DENS(dev))) {
		/* force rewind if minor bits have changed,
		 * i.e. user wants to use tape in different format.
		 * [assuming single drive operation]
		 */
		if (TP_HAVE_DENS) {
			tpqputs(TPQD_REWIND, "Density minor bits have changed. Forcing rewind.");
			need_rewind = YES;
		}
	} else {
		/* density bits still the same, but TP_DIAGS bit 
		 * may have changed.
		 */
		current_tape_dev = dev;
	}
 
	if (need_rewind == YES) { /***************** CHECK THIS!!!!!!!! **********/
		s = do_qic_cmd(QCMD_REWIND, TIM_R);
		if (s != 0) {
			tpqputs(TPQD_ALWAYS, "open: rewind failed");
			return -EIO;
		}
	}
 
 
/* Note: After a reset command, the controller will rewind the tape
 *	 just before performing any tape movement operation! ************ SO SET need_rewind flag!!!!!
 */ 
	if (status_dead==YES) {
		tpqputs(TPQD_ALWAYS, "open: tape dead, attempting reset");
		if (tape_reset(1)!=TE_OK) {
			return -ENXIO;
		} else {
			status_dead = NO;
			if (tp_sense(~(TP_ST1|TP_ILL)) != TE_OK) {
				tpqputs(TPQD_ALWAYS, "open: tp_sense() failed\n");
				status_dead = YES;	/* try reset next time */
				return -EIO;
			}
		}
	}
 
	/* things should be ok, once we get here */
 
 
	/* set density: only allowed when TP_BOM status bit is set,
	 * so we must have done a rewind by now. If not, just skip over.
	 * Only give set density command when minor bits have changed.
	 */
	if (TP_DENS(current_tape_dev) == TP_DENS(dev) )
		return 0;
 
	current_tape_dev = dev;
	need_rewind = NO;
	if (TP_HAVE_DENS)
		dens = TP_DENS(dev);
 
	if (dens < sizeof(format_names)/sizeof(char *))
		printk(TPQIC02_NAME ": format: %s%s\n", (dens!=0)? "QIC-" : "", format_names[dens]);
	else
		tpqputs(TPQD_REWIND, "Wait for retensioning...");
 
	switch (TP_DENS(dev)) {
		case 0: /* Minor 0 is for drives without set-density support */
			s = 0;
			break;
		case 1:
			s = do_qic_cmd(QCMD_DENS_11, TIM_S);
			break;
		case 2:
			s = do_qic_cmd(QCMD_DENS_24, TIM_S);
			break;
		case 3:
			s = do_qic_cmd(QCMD_DENS_120, TIM_S);
			break;
		case 4:
			s = do_qic_cmd(QCMD_DENS_150, TIM_S);
			break;
		case 5:
			s = do_qic_cmd(QCMD_DENS_300, TIM_S);
			break;
		case 6:
			s = do_qic_cmd(QCMD_DENS_600, TIM_S);
			break;
		default:  /* otherwise do a retension before anything else */
			s = do_qic_cmd(QCMD_RETEN, TIM_R);
	}
	if (s != 0) {
		status_dead = YES;	/* force reset */
		current_tape_dev = 0; /* earlier 0xff80 */
		return -EIO;
	}
 
	return 0;
} /* qic02_tape_open */
 
 
static void qic02_tape_release(struct inode * inode, struct file * filp)
{
	kdev_t dev = inode->i_rdev;
 
	if (TP_DIAGS(dev))
		printk("qic02_tape_release: dev=%s\n",
		       kdevname(dev));
 
	if (status_zombie==YES)		/* don't rewind in zombie mode */
		return;
 
	/* Terminate any pending write cycle. Terminating the read-cycle
	 * is delayed until it is required to do so for a new command.
	 */
	terminate_write(-1);
 
	if (status_dead==YES)
		tpqputs(TPQD_ALWAYS, "release: device dead!?");
 
 	/* Rewind only if minor number requires it AND 
	 * read/writes have been done. ************* IS THIS CORRECT??????????
	 */
	if ((TP_REWCLOSE(dev)) && (status_bytes_rd | status_bytes_wr)) {
		tpqputs(TPQD_REWIND, "release: Doing rewind...");
		(void) do_qic_cmd(QCMD_REWIND, TIM_R);
	}
 
	return;
} /* qic02_tape_release */
 
 
#ifdef CONFIG_QIC02_DYNCONF
/* Set masks etc. based on the interface card type. */
int update_ifc_masks(int ifc)
{
	QIC02_TAPE_IFC = ifc;
 
	if ((QIC02_TAPE_IFC == WANGTEK) || (QIC02_TAPE_IFC == EVEREX)) {
		QIC02_STAT_PORT = QIC02_TAPE_PORT;
		QIC02_CTL_PORT = QIC02_TAPE_PORT;
		QIC02_CMD_PORT = QIC02_TAPE_PORT+1;
		QIC02_DATA_PORT = QIC02_TAPE_PORT+1;
		QIC02_STAT_READY = WT_QIC02_STAT_READY;
		QIC02_STAT_EXCEPTION = WT_QIC02_STAT_EXCEPTION;
		QIC02_STAT_MASK = WT_QIC02_STAT_MASK;
 
		QIC02_STAT_RESETMASK = WT_QIC02_STAT_RESETMASK;
		QIC02_STAT_RESETVAL = WT_QIC02_STAT_RESETVAL;
 
		QIC02_CTL_RESET = WT_QIC02_CTL_RESET;
		QIC02_CTL_REQUEST = WT_QIC02_CTL_REQUEST;
 
		if (QIC02_TAPE_DMA == 3)
			WT_CTL_DMA = WT_CTL_DMA3;
		else if (QIC02_TAPE_DMA == 1)
			WT_CTL_DMA = WT_CTL_DMA1;
		else {
			tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
			return -EIO;
		} 
 
		if (QIC02_TAPE_IFC == EVEREX) {
			/* Everex is a special case for Wangtek (actually
			 * it's the other way 'round, but I saw Wangtek first)
			 */
			if (QIC02_TAPE_DMA==3)
				WT_CTL_DMA = WT_CTL_DMA1;
			/* Fixup the kernel copy of the IFC type to that
			 * we don't have to distinguish between Wangtek and
			 * and Everex at runtime.
			 */
			QIC02_TAPE_IFC = WANGTEK;
		}
	} else if (QIC02_TAPE_IFC == ARCHIVE) {
		QIC02_STAT_PORT = QIC02_TAPE_PORT+1;
		QIC02_CTL_PORT = QIC02_TAPE_PORT+1;
		QIC02_CMD_PORT = QIC02_TAPE_PORT;
		QIC02_DATA_PORT = QIC02_TAPE_PORT;
		QIC02_STAT_READY = AR_QIC02_STAT_READY;
		QIC02_STAT_EXCEPTION  = AR_QIC02_STAT_EXCEPTION;
		QIC02_STAT_MASK = AR_QIC02_STAT_MASK;
 
		QIC02_STAT_RESETMASK = AR_QIC02_STAT_RESETMASK;
		QIC02_STAT_RESETVAL = AR_QIC02_STAT_RESETVAL;
 
		QIC02_CTL_RESET = AR_QIC02_CTL_RESET;
		QIC02_CTL_REQUEST = AR_QIC02_CTL_REQUEST;
 
		if (QIC02_TAPE_DMA > 3) {
			tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
			return -EIO;
		} 
	} else if (QIC02_TAPE_IFC == MOUNTAIN) {
		QIC02_STAT_PORT = QIC02_TAPE_PORT+1;
		QIC02_CTL_PORT = QIC02_TAPE_PORT+1;
		QIC02_CMD_PORT = QIC02_TAPE_PORT;
		QIC02_DATA_PORT = QIC02_TAPE_PORT;
 
		QIC02_STAT_READY = MTN_QIC02_STAT_READY;
		QIC02_STAT_EXCEPTION  = MTN_QIC02_STAT_EXCEPTION;
		QIC02_STAT_MASK = MTN_QIC02_STAT_MASK;
 
		QIC02_STAT_RESETMASK = MTN_QIC02_STAT_RESETMASK;
		QIC02_STAT_RESETVAL = MTN_QIC02_STAT_RESETVAL;
 
		QIC02_CTL_RESET = MTN_QIC02_CTL_RESET;
		QIC02_CTL_REQUEST = MTN_QIC02_CTL_REQUEST;
 
		if (QIC02_TAPE_DMA > 3) {
			tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
			return -EIO;
		} 
	} else {
		tpqputs(TPQD_ALWAYS, "Invalid interface type");
		return -ENXIO;
	}
	return 0;
} /* update_ifc-masks */
#endif
 
 
/* ioctl allows user programs to rewind the tape and stuff like that */
static int qic02_tape_ioctl(struct inode * inode, struct file * filp, 
		     unsigned int iocmd, unsigned long ioarg)
{
	int error;
	short i;
	int dev_maj = MAJOR(inode->i_rdev);
	int c;
	struct mtop operation;
	char *stp, *argp;
	unsigned char blk_addr[6];
	struct mtpos ioctl_tell;
 
 
	if (TP_DIAGS(current_tape_dev))
		printk(TPQIC02_NAME ": ioctl(%4x, %4x, %4lx)\n", dev_maj, iocmd, ioarg);
 
	if (!inode || !ioarg)
		return -EINVAL;
 
	/* check iocmd first */
 
	if (dev_maj != QIC02_TAPE_MAJOR) {
		printk(TPQIC02_NAME ": Oops! Wrong device?\n");
		/* A panic() would be appropriate here */
		return -ENODEV;
	}
 
	c = iocmd & IOCCMD_MASK;
 
#ifdef DDIOCSDBG
	/* Check for DDI Debug Control, contributed by FvK, edited by HHB. */
	if (c == DDIOCSDBG) {
		if (!suser())
			return -EPERM;
		error = verify_area(VERIFY_READ, (int *) ioarg, sizeof(int));
		if (error) return error;
		c = get_user((int *) ioarg);
		if (c==0) {
			QIC02_TAPE_DEBUG = 0;
			return 0;
		}
		if ((c>=1) && (c<=32)) {
			QIC02_TAPE_DEBUG |= (1 << (c-1));
			return 0;
		}
		if (c >= 128) {
			QIC02_TAPE_DEBUG &= ~(1 << (c - 128));
			return 0;
		}
		return -EINVAL;
	}
#endif
 
#ifdef CONFIG_QIC02_DYNCONF
	if (c == (MTIOCGETCONFIG & IOCCMD_MASK)) {
		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) {
			tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!");
			return -EFAULT;
		}
 
		/* check for valid user address */
		error = verify_area(VERIFY_WRITE, (void *) ioarg, sizeof(qic02_tape_dynconf));
		if (error)
			return error;
		/* copy current settings to user space */
		stp = (char *) &qic02_tape_dynconf;
		argp = (char *) ioarg;
		for (i=0; i<sizeof(qic02_tape_dynconf); i++) 
			put_user(*stp++, argp++);
		return 0;
 
	} else if (c == (MTIOCSETCONFIG & IOCCMD_MASK)) {
		static int qic02_get_resources(void), qic02_release_resources(void);
 
		/* One should always do a MTIOCGETCONFIG first, then update
		 * user-settings, then write back with MTIOCSETCONFIG.
		 * Re-open() the device before actual use to make sure
		 * everything is initialized.
		 */
		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) {
			tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!");
			return -EFAULT;
		}
		if (!suser())
			return -EPERM;
		if ((doing_read!=NO) || (doing_write!=NO))
			return -EBUSY;
		error = verify_area(VERIFY_READ, (char *) ioarg, sizeof(qic02_tape_dynconf));
		if (error)
			return error;
 
		/* copy struct from user space to kernel space */
		stp = (char *) &qic02_tape_dynconf;
		argp = (char *) ioarg;
		for (i=0; i<sizeof(qic02_tape_dynconf); i++)
			*stp++ = get_user(argp++);
		if (status_zombie==NO)
			qic02_release_resources();	/* and go zombie */
		if (update_ifc_masks(qic02_tape_dynconf.ifc_type))
			return -EIO;
		if (qic02_get_resources())
			return -ENXIO;
		return 0;
 
	}
	if (status_zombie==YES) {
		tpqputs(TPQD_ALWAYS, "Configs not set");
		return -ENXIO;
	}
#endif
	if (c == (MTIOCTOP & IOCCMD_MASK)) {
 
		/* Compare expected struct size and actual struct size. This
		 * is useful to catch programs compiled with old #includes.
		 */
		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtop)) {
			tpqputs(TPQD_ALWAYS, "sizeof(struct mtop) does not match!");
			return -EFAULT;
		}
		error = verify_area(VERIFY_READ, (char *) ioarg, sizeof(operation));
		if (error)
			return error;
 
		/* copy mtop struct from user space to kernel space */
		stp = (char *) &operation;
		argp = (char *) ioarg;
		for (i=0; i<sizeof(operation); i++)
			*stp++ = get_user(argp++);
 
		/* ---note: mt_count is signed, negative seeks must be
		 * ---	    translated to seeks in opposite direction!
		 * (only needed for Sun-programs, I think.)
		 */
		/* ---note: MTFSF with count 0 should position the
		 * ---	    tape at the beginning of the current file.
		 */
 
		if (TP_DIAGS(current_tape_dev))
			printk("OP op=%4x, count=%4x\n", operation.mt_op, operation.mt_count);
 
		if (operation.mt_count < 0)
			tpqputs(TPQD_ALWAYS, "Warning: negative mt_count ignored");
 
		ioctl_status.mt_resid = operation.mt_count;
		if (operation.mt_op == MTSEEK) {
			if (!TP_HAVE_SEEK)
				return -ENOTTY;
			seek_addr_buf[0] = (operation.mt_count>>16)&0xff;
			seek_addr_buf[1] = (operation.mt_count>>8)&0xff;
			seek_addr_buf[2] = (operation.mt_count)&0xff;
			if (operation.mt_count>>24)
				return -EINVAL;
			if ((error = do_ioctl_cmd(operation.mt_op)) != 0)
				return error;
			ioctl_status.mt_resid = 0;
		} else {
			while (operation.mt_count > 0) {
				operation.mt_count--;
				if ((error = do_ioctl_cmd(operation.mt_op)) != 0)
					return error;
				ioctl_status.mt_resid = operation.mt_count;
			}
		}
		return 0;
 
	} else if (c == (MTIOCGET & IOCCMD_MASK)) {
		if (TP_DIAGS(current_tape_dev))
			printk("GET ");
 
		/* compare expected struct size and actual struct size */
		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtget)) {
			tpqputs(TPQD_ALWAYS, "sizeof(struct mtget) does not match!");
			return -EFAULT;
		}
 
		/* check for valid user address */
		error =	verify_area(VERIFY_WRITE, (void *) ioarg, sizeof(ioctl_status));
		if (error)
			return error;
 
		/* It appears (gmt(1)) that it is normal behaviour to
		 * first set the status with MTNOP, and then to read
		 * it out with MTIOCGET
		 */
 
		/* copy results to user space */
		stp = (char *) &ioctl_status;
		argp = (char *) ioarg;
		for (i=0; i<sizeof(ioctl_status); i++) 
			put_user(*stp++, argp++);
		return 0;
 
 
	} else if (TP_HAVE_TELL && (c == (MTIOCPOS & IOCCMD_MASK))) {
		if (TP_DIAGS(current_tape_dev))
			printk("POS ");
 
		/* compare expected struct size and actual struct size */
		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtpos)) {
			tpqputs(TPQD_ALWAYS, "sizeof(struct mtpos) does not match!");
			return -EFAULT;
		}
 
		/* check for valid user address */
		error = verify_area(VERIFY_WRITE, (void *) ioarg, sizeof(ioctl_tell));
		if (error)
			return error;
 
		tpqputs(TPQD_IOCTLS, "MTTELL reading block address");
		if ((doing_read==YES) || (doing_write==YES))
			finish_rw(AR_QCMDV_TELL_BLK);
 
		c = rdstatus((char *) blk_addr, sizeof(blk_addr), AR_QCMDV_TELL_BLK);
		if (c!=TE_OK)
			return -EIO;
 
		ioctl_tell.mt_blkno = (blk_addr[3] << 16) | (blk_addr[4] << 8) | blk_addr[5];
 
		/* copy results to user space */
		stp = (char *) &ioctl_tell;
		argp = (char *) ioarg;
		for (i=0; i<sizeof(ioctl_tell); i++) 
			put_user(*stp++, argp++);
		return 0;
 
	} else
		return -ENOTTY;	/* Other cmds not supported. */
} /* qic02_tape_ioctl */
 
 
 
/* These are (most) of the interface functions: */
static struct file_operations qic02_tape_fops = {
	qic02_tape_lseek,		/* not allowed */
	qic02_tape_read,		/* read */
	qic02_tape_write,		/* write */
	NULL,				/* readdir not allowed */
	NULL,				/* select ??? */
	qic02_tape_ioctl,		/* ioctl */
	NULL,				/* mmap not allowed */
	qic02_tape_open,		/* open */
	qic02_tape_release,		/* release */
	NULL,				/* fsync */
	NULL,				/* fasync */
	NULL,				/* check_media_change */
	NULL				/* revalidate */
};
 
/* align `a' at `size' bytes. `size' must be a power of 2 */
static inline unsigned long const align_buffer(unsigned long a, unsigned size)
{
	if (a & (size-1))			/* if not aligned */
		return (a | (size-1)) + 1;
	else					/* else is aligned */
		return a;
}
 
 
 
static void qic02_release_resources(void)
{
	free_irq(QIC02_TAPE_IRQ, NULL);
	free_dma(QIC02_TAPE_DMA);
	status_zombie = YES;
} /* qic02_release_resources */
 
 
 
 
static int qic02_get_resources(void)
{
	/* First perform some checks. If one of them fails,
	 * the tape driver will not be registered to the system.
	 */
	if (QIC02_TAPE_IRQ>16) {
		tpqputs(TPQD_ALWAYS, "Bogus interrupt number.");
		return -1;
	}
 
	/* for DYNCONF, allocating DMA & IRQ should not be done until 
         * the config parameters have been set using MTSETCONFIG.
	 */
 
	/* get IRQ */
	if (request_irq(QIC02_TAPE_IRQ, qic02_tape_interrupt, SA_INTERRUPT, "QIC-02", NULL)) {
		printk(TPQIC02_NAME ": can't allocate IRQ%d for QIC-02 tape\n",
			QIC02_TAPE_IRQ);
		status_zombie = YES;
		return -1;
	}
 
	/* After IRQ, allocate DMA channel */
	if (request_dma(QIC02_TAPE_DMA,"QIC-02")) {
		printk(TPQIC02_NAME ": can't allocate DMA%d for QIC-02 tape\n",
			QIC02_TAPE_DMA);
		free_irq(QIC02_TAPE_IRQ, NULL);
		status_zombie = YES;
		return -1;
	}
 
	printk(TPQIC02_NAME ": Settings: IRQ %d, DMA %d, IO 0x%x, IFC %s\n",
		QIC02_TAPE_IRQ, QIC02_TAPE_DMA,
		((QIC02_TAPE_IFC==ARCHIVE) || (QIC02_TAPE_IFC==MOUNTAIN))?
			QIC02_CMD_PORT : QIC02_STAT_PORT,
		(QIC02_TAPE_IFC==MOUNTAIN)? "Mountain" :
			((QIC02_TAPE_IFC==ARCHIVE)? "Archive" : "Wangtek"));
 
	if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) {
		/* No drive detected, so vanish */
		tpqputs(TPQD_ALWAYS, "No drive detected -- releasing irq and dma.");
		status_dead = YES;
		qic02_release_resources();
		return -1;
	}
 
	/* All should be ok now */
	status_zombie = NO;
	return 0;
} /* qic02_get_resources */
 
 
int qic02_tape_init(void)
	/* Shouldn't this be a caddr_t ? */
{
 
	if (TPSTATSIZE != 6) {
		printk(TPQIC02_NAME ": internal error: tpstatus struct incorrect!\n");
		return -ENODEV;
	}
	if ((TPQBUF_SIZE<512) || (TPQBUF_SIZE>=0x10000)) {
		printk(TPQIC02_NAME ": internal error: DMA buffer size out of range\n");
		return -ENODEV;
	}
 
	QIC02_TAPE_DEBUG = TPQD_DEFAULT_FLAGS;
 
	current_tape_dev = MKDEV(QIC02_TAPE_MAJOR, 0);
 
#ifndef CONFIG_QIC02_DYNCONF
	printk(TPQIC02_NAME ": IRQ %d, DMA %d, IO 0x%x, IFC %s, %s, %s\n",
		 QIC02_TAPE_IRQ, QIC02_TAPE_DMA,
# if QIC02_TAPE_IFC == WANGTEK
		 QIC02_STAT_PORT, "Wangtek",
# elif QIC02_TAPE_IFC == ARCHIVE
		 QIC02_CMD_PORT, "Archive",
# elif QIC02_TAPE_IFC == MOUNTAIN
		 QIC02_CMD_PORT, "Mountain",
# else
#  error
# endif
		 rcs_revision, rcs_date);
	if (qic02_get_resources())
		return -ENODEV;
#else
	printk(TPQIC02_NAME ": Runtime config, %s, %s\n", 
		 rcs_revision, rcs_date);
 
	QIC02_TAPE_IRQ = BOGUS_IRQ;	/* invalid value */
#endif
 
	printk(TPQIC02_NAME ": DMA buffers: %u blocks", NR_BLK_BUF);
 
	/* Setup the page-address for the dma transfer.
	 * This assumes a one-to-one identity mapping between
	 * kernel addresses and physical memory.
	 */
	buffaddr = align_buffer((unsigned long) &qic02_tape_buf, TAPE_BLKSIZE);
	printk(", at address 0x%lx (0x%lx)\n", buffaddr, (unsigned long) &qic02_tape_buf);
 
#ifndef CONFIG_MAX_16M
	if (buffaddr+TPQBUF_SIZE>=0x1000000) {
		printk(TPQIC02_NAME ": DMA buffer *must* be in lower 16MB\n");
		return -ENODEV;
	}
#endif
 
	/* If we got this far, install driver functions */
	if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops)) {
		printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR);
#ifndef CONFIG_QIC02_DYNCONF
		free_irq(QIC02_TAPE_IRQ, NULL);
		free_dma(QIC02_TAPE_DMA);
#endif
		return -ENODEV;
	}
 
	/* prepare timer */
	TIMEROFF;
	timer_table[QIC02_TAPE_TIMER].expires = 0;
	timer_table[QIC02_TAPE_TIMER].fn = qic02_tape_times_out;
 
#ifndef CONFIG_QIC02_DYNCONF
	if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) {
		/* No drive detected, so vanish */
		tpqputs(TPQD_ALWAYS, "No drive detected -- driver going on vacation...");
		status_dead = YES;
		free_irq(QIC02_TAPE_IRQ, NULL);
		free_dma(QIC02_TAPE_DMA);
		unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME);
		return -ENODEV;
	} else {
		if (is_exception()) {
			tpqputs(TPQD_ALWAYS, "exception detected\n");
			(void) tp_sense(TP_WRP|TP_POR|TP_CNI);
		}
	}
#endif
 
	/* initialize generic status for ioctl requests */
 
	ioctl_status.mt_type	= QIC02_TAPE_DRIVE;	/* MT_IS* id nr */
 
	ioctl_status.mt_resid	= 0;	/* ---residual count */
	ioctl_status.mt_gstat	= 0;	/* ---generic status */
	ioctl_status.mt_erreg	= 0;	/* not used */
	ioctl_status.mt_fileno	= 0;	/* number of current file on tape */
	ioctl_status.mt_blkno	= 0;	/* number of current (logical) block */
 
	return 0;
} /* qic02_tape_init */
 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.