/* $Header: /sys/linux-0.96a/kernel/chr_drv/RCS/tpqic02.c,v 0.1.0.3 1992/06/01 01:57:34 root Exp root $
 *
 * Driver for tape drive support for Linux-386 0.96.  March 1992.
 *
 * Copyright (c) 1992 by H. H. Bergman. All rights reserved.
 * Current e-mail address: csg279@cs.wing2.cs.rug.nl
 *
 * 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.
 *
 * This code comes without warranty of any kind.
 * Use it at your own risk. Don't blame me if it destroys your data!
 * Make sure you have a backup before you try this code :-)
 *
 * This driver is derived from the 'wt' driver in the 386BSD distribution,
 * which carries the following copyright notice:
 *
 * Copyright (c) 1991 The Regents of the University of California.
 * All rights reserved.
 *
 * You are not alowed to change this line and/or the text above.
 *
 * $Log: tpqic02.c,v $
 * 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
 *
 * Revision 0.0  1992/05/18  00:58:55  root
 * Managed to recompile with gcc 2.1 by moving set_intr_gate() to
 * a seperate function.
 *
 * Revision 0.0  1992/05/17  23:56:51  root
 * Initial version by Hennus Bergman.
 *
 */

/* 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!
 */

#include <linux/config.h>

/* skip this driver if not required for this configuration */
#ifdef	CONFIG_TAPE_QIC02

#include <linux/system.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <errno.h>

#include <sys/mtio.h>
#include <fcntl.h>

/* check existance of required configuration parameters */
#if !defined(TAPE_QIC02_MAJOR) || !defined(TAPE_QIC02_PORT) || \
    !defined(TAPE_QIC02_IRQ) || !defined(TAPE_QIC02_DMA)
#error "tape_qic configuration error: check config.h"
#endif

/* check for valid config */
#if (TAPE_QIC02_MAJOR != 10)
#error "Unusual major number for tape_qic: check config*.h"
#endif
#if (TAPE_QIC02_DMA != DMA1) && (TAPE_QIC02_DMA != DMA3)
#error "invalid DMA channel for tape_qic: check config*.h"
#endif

#include "tpqic02.h"
#define TPQIC_NAME	"tape_qic"

/* Linux outb() commands have (value,port) as parameters.
 * One might expect (port,value) instead, so beware!
 */

volatile static int mbits = 0;	/* current mode bits for tape controller */

static struct task_struct *tape_qic02_transfer = NULL; /* sync rw with interrupts */

volatile static struct mtget ioctl_status;	/* current generic status */

volatile static struct tpstatus tperror;	/* last drive status */


static char rcsid[] = "$Id: tpqic02.c,v 0.1.0.3 1992/06/01 01:57:34 root Exp root $";
static char rcs_revision[] = "$Revision: 0.1.0.3 $";
static char rcs_date[] = "$Date: 1992/06/01 01:57:34 $";

/* flag bits for status and outstanding requests [could all be put in one bit-field-struct */
volatile static flag status_dead = YES;	/* device is legally dead until proven alive */
	 static flag status_open = NO;	/* in use or not */
volatile static flag status_bytes_wr = NO;	/* write 2 FMs at close or not */
volatile static flag status_bytes_rd = NO;	/* (rd|wr) used for rewinding */
volatile static unsigned long status_cmd_pending = 0; /* cmd in progress */
volatile static flag status_expect_int = NO;	/* ready for interrupts */
volatile static flag status_timer_on = NO; 	/* using time-out */
volatile static int  status_error = 0;	/* int handler may detect error */
volatile static flag status_eof_detected = NO;
volatile static flag status_eom_detected = NO;
	 static flag doing_read = NO;
	 static flag doing_write = NO;

volatile static unsigned long dma_bytes_todo;
volatile static unsigned long dma_bytes_done;
volatile static unsigned dma_mode = 0;	/* !=0 also means DMA in use */
	 static flag do_rewind = YES;


/* In write mode, we have to write a File Mark after the last block written, 
 * when the tape device is closed. Tape repositioning in write mode is allowed
 * as long as no actual writing has been done.
 * NOTE: We allow reading, when in write mode. Maybe this is wrong?
 */
static int  mode_access;	/* access mode: R xor W */

/* Avoid crossing a 64k boundary in the dma buffer below.
 * THIS IS NOT MERELY A PERFORMANCE ISSUE. If a 64k boundary crossing occurs in
 * a block that is to be transfered, part of the block will be lost, because the
 * DMA address will wrap around, without warning! The minimum alignment needed
 * when the DMA buffer contains a 64k boundary crossing is at most 511 bytes.
 * If the dma buffer contains a 64k boundary crossing, we must at least align 
 * the 64k boundary at a tape block boundary.
 *
 *
 * If you move the variables around, you may avoid having to use padding,
 * but make sure the padding always physically precedes the dma buffer
 * (tape_qic02_buf)!
 */
#define TPQ_PADDING_SIZE	0	/* size of padding buffer in bytes */
#define TPQ_SET_PAGE_ALWAYS	1	/* always update page regs? */


/* This is the actual kernel buffer where the interrupt routines read
 * from/write to. We need a large buffer to prevent tape repositioning,
 * which is bad for the drive & tape. 10 to 40k bytes should be enough.
 * The reason we need an in-kernel buffer is that user-space memory cannot
 * be guaranteed to be in physical memory at the time we need it. It is
 * possible to use a buffer in userspace by locking it in-core but that's
 * tricky to say the least. Also, the (slave) DMA controller can only access
 * the lower 1MBytes of system memory. Hopefully the kernel resides there.
 * If the filesystem cannot keep up with the streamer, use a larger buffer,
 * say 64k. Or have the user program use an extremely large blocking factor.
 * [NOTE: 64k just happens to be the upper kernel-limit because the DMA
 * controller cannot cross 64k boundaries. I think the overhead of setting
 * the page registers up for every 512-byte block transfered is too high.
 * Besides, gcc can't align to 512 bytes without major problems.]
 * Using a buffer of more than one block may leave the tape at a different
 * position than the user program might think. Another reason to disallow
 * writing when in read mode. You shouldn't write anything to an existing
 * file on tape anyway, except at the end for appending.
 */

#if TPQ_PADDING_SIZE > 0 
/* with gcc1.40 this should be placed *after* tape_qic02_buf[],
 * with gcc2.1 *before*. Check with 'gcc -S'
 */
static char tape_qic02_padding_buffer[TPQ_PADDING_SIZE];
#endif

volatile static char tape_qic02_buf[TPQBUF_SIZE];
/* A really good compiler would be able to align this at 512 bytes... */

static unsigned long buffaddr;	/* full physical address of tape_qic02_buf[] */
static int dma_cross;


static void tpqputs(char *s)
{
	printk(TPQIC_NAME ": %s\n", s);
} /* tpqputs */


static void sensemsg(short n)
{
	static char *sensemsg[15] = { 
		"Unknown exception status code",		/* 0 */
		"Cartridge not in place",			/* 1 */
		"Drive not online",				/* 2 */
		"Write protected cartridge",			/* 3 */
		"End of media",					/* 4 */
		"Read or Write error. Rewind tape.",		/* 5 */
		"Read error. Bad block transfered.",		/* 6 */
		"Read error. Cannot recover filler block.",	/* 7 */
		"Read error. No data detected.",		/* 8 */
		"Read error. No data detected. EOM.",		/* 9 */
		"Read error. No data detected. BOM.",		/* 10 */
		"File mark detected",				/* 11 */
		"Illegal command",				/* 12 */
		"Reset occured",				/* 13 */
		"Marginal block detected"			/* 14 */
	};

	if ((n<1) || (n>14)) n = 0;
	printk(TPQIC_NAME ": sense: %s\n", sensemsg[n]);
} /* sensemsg */


/* There are exectly 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 now...
 * 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 = 0;

	if (s & TP_ST1) {
		if (s & TP_ILL)		/* 12: Illegal command. FATAL */
			n = 12;
		if (s & TP_POR)		/* 13: Reset occured. FATAL */
			n = 13;
	} 
	else if (s & TP_ST0) {
		if (s & TP_EOM)		/* 4: End Of Media. CONTINUABLE */
			n = 4;
		if (s & TP_USL)		/* 2: Drive not online. FATAL */
			n = 2;
		else if (s & TP_CNI) {	/* 1: Cartridge not in place. FATAL */
			n = 1;
			do_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 filler block. CONTINUABLE */
					n = 7;
			}
			else {
				if (s & TP_EOM)	/* 5: Read or Write error. Rewind tape. FATAL */
					n = 5;
				else		/* 6: Read error. Bad block transfered. CONTINUABLE */
					/* block is bad, but transfer may continue */
					n = 6;
			}
		}
		else if (s & TP_FIL) {
			if (s & TP_MBD)		/* 14: Marginal block detected. CONTINUABLE */
				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 */


static inline int is_exception(void)
{
	return (inb(QIC_STAT_PORT) & WTS_EXCEPTION) == 0;
} /* is_exception */


/* tape_reset() 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(short verbose, short no_sleep)
{
	volatile static time_t init_start_time = 0;
	short i;

	mbits |= WTC_RESET;	/* ONLINE, REQUEST don't matter here */
	outb_p(mbits, QIC_CTL_PORT);	/* assert reset */

	/* Next, we need to wait >=25 usec.
	 * This has to work on a lot of different machines,
	 * so a timed loop is not a good solution, but it's short.
	 * A kernel 'usleep()' would be Nice.
	 */
	if ((init_start_time==0) || no_sleep)	{
		/* This is what we use on boot-up and
		 * when we're not allowed to sleep.
		 */
		for (i=0; i<800; i++)
			init_start_time = jiffies;    /* something volatile */
	} else {
		/* This only works if there *are* other processes. */
		init_start_time = jiffies;
		while (jiffies - init_start_time < 1)
			schedule();	/* once should suffice, I think */
	}
	status_eof_detected = NO;
	status_eom_detected = NO;
	status_cmd_pending = 0;

	mbits &= ~WTC_RESET;
	outb_p(mbits, QIC_CTL_PORT);	/* de-assert reset */
	status_dead = ((inb_p(QIC_STAT_PORT) & WTS_RESETMASK) != WTS_RESETVAL);
	/* if successful, inb(STAT) returned RESETVAL */
	if (verbose)
		printk(TPQIC_NAME ": reset %s\n", 
			(status_dead)? "failed!" : "successful");
	return (status_dead)? TE_DEAD : TE_OK;
} /* tape_reset */



/* Notify tape drive of a new command. It only waits for the
 * command to be accepted, not for the actual command to complete.
 * pre: QIC_CMD_PORT has 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.
 */
static int notify_cmd(short ignore_ex, short no_sleep)
{
	int i;

	if ((!ignore_ex) && is_exception()) {
		tpqputs("*** exception detected in notify_cmd");
		/** force a reset here **/
		if (tape_reset(1, no_sleep)==TE_DEAD)
			return TE_DEAD;
		if (is_exception()) {
			tpqputs("exception persists after reset.");
			tpqputs(" ^ exception ignored.");
		}
	}

	outb_p(mbits | WTC_REQUEST, QIC_CTL_PORT);  /* set request bit */
	i = TAPE_TIMEOUT;
	/* The specs say this takes about 500 usec, but there is no upper limit! */
	while ((inb_p(QIC_STAT_PORT) & WTS_READY) && (--i>0))
		/*skip*/;			  /* wait for ready */
	if (i==0) {
		tpqputs("timed out waiting for ready in notify_cmd");
		status_dead = YES;
		return TE_TIM;
	}

	outb_p(mbits & ~WTC_REQUEST, QIC_CTL_PORT); /* reset request bit */
	i = TAPE_TIMEOUT;
	/* according to the specs, this one should never time-out */
	while (((inb_p(QIC_STAT_PORT) & WTS_READY) == 0) && (--i>0))
		/*skip*/;			  /* wait for not ready */
	if (i==0) {
		tpqputs("timed out waiting for !ready in notify_cmd");
		status_dead = YES;
		return TE_TIM;
	}
	/* command accepted */
	return TE_OK;
} /* notify_cmd */


/* wait_for_ready() waits for a command to complete, with timeout */
static int wait_for_ready(time_t timeout)
{
	int stat;

	status_timer_on = YES;
	TIMERON(timeout);

	/* Wait for ready or exception */
	/* should busy wait for 1000 usec before going to sleep() here */
	while (((stat = inb_p(QIC_STAT_PORT) & WTS_STAT) == WTS_STAT) && (status_timer_on == YES))
		schedule();	/* but don't waste all the CPU time */

	TIMEROFF;
	if (status_timer_on == NO) {
		tpqputs("wait_for_ready timed out");
		return TE_TIM;
	}
	status_timer_on = NO;

	if ((stat & WTS_EXCEPTION) == 0) {
		tpqputs("exception detected after waiting_for_ready");
		return TE_EX;
	} else
		return TE_OK;
} /* wait_for_ready */


/* Send a QIC-02 command to the tape drive
 * This one is also used by tp_sense(), so we must have
 * a flag to disable exception checking. 
 *
 * On entry, the controller is supposed to be READY.
 */
static int send_qic02_cmd(int cmd, time_t timeout, int ignore_ex)
{
	int stat;

	stat = inb_p(QIC_STAT_PORT);
	if ((stat & WTS_EXCEPTION) == 0)	/* if exception */
		return TE_EX;
	if (stat & WTS_READY)			/* if 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;

	outb_p(cmd, QIC_CMD_PORT);	 /* output the command */
	mbits = WTC_ONLINE;		 /* always online */
	outb_p(mbits, QIC_CTL_PORT);	 /* tape drive online */
	stat = notify_cmd(ignore_ex, 0); /* tell drive new command was loaded, */
					 /* inherit exception check, may sleep. */
	if (stat != TE_OK) {
		tpqputs("send_qic02_cmd failed");
	}
	return stat;
} /* send_qic02_cmd */



/* Get drive status. Assume drive is ready or has exception set.
 * (or will be in <1000 usec.)
 */
static int rdstatus(char *stp)
{
	int	s;
	int	n;
	volatile time_t	dummy;

	/* Try to busywait a few (700) usec, after that de-schedule.
	 *
	 * The problem is, if we don't de-schedule, performance will
	 * drop to zero when the drive is not responding and if we
	 * de-schedule immediately, we waste a lot of time because a
	 * task switch is much longer than we usually have to wait here.
	 */
	n = 1000;	/* 500 is not enough on a 486/33 */
	while ((n>0) && ((inb_p(QIC_STAT_PORT) & WTS_STAT) == WTS_STAT))
		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 reponse time.]
		 */
		tpqputs("waiting looong in wt_rdstatus() -- drive dead?");
		while ((inb_p(QIC_STAT_PORT) & WTS_STAT) == WTS_STAT)
			schedule();
		tpqputs("finished waiting in wt_rdstatus()");
	}

	outb_p(QCMD_RD_STAT, QIC_CMD_PORT);
	(void) notify_cmd(1, 0);		/* 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.
	 */

	printk(TPQIC_NAME ": reading status bytes: ");
	for (n=0; n<TPSTATSIZE; n++)
	{
		do s = inb_p(QIC_STAT_PORT);
		while ((s & WTS_STAT) == WTS_STAT);	/* wait for ready or exception */

		if ((s & WTS_EXCEPTION) == 0) {		/* if exception */
			tpqputs("rdstatus: exception error");
			ioctl_status.mt_erreg = 0;	/* dunno... */
			return TE_NS;		/* error */
		}

		stp[n] = inb_p(QIC_DATA_PORT);		/* read status byte */
		printk("[%1d]=0x%x  ", n, (unsigned) stp[n] & 0xff);

		outb_p(mbits | WTC_REQUEST, QIC_CTL_PORT);	/* set request */

		while ((inb_p(QIC_STAT_PORT) & WTS_READY) == 0);	/* wait for not ready */
		for (s=100; s>0; s--)		/* wait an additional time */
			dummy = jiffies;

		outb_p(mbits & ~WTC_REQUEST, QIC_CTL_PORT);/* unset request */

	}
	ioctl_status.mt_erreg = ioctl_status.mt_dsreg = tperror.exs; /***/
	printk("\n");

	return TE_OK;
} /* rdstatus */



/* 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, gs = 0;

	printk(TPQIC_NAME ": tp_sense(ignore=0x%x) enter\n", ignore);

	if (rdstatus((char *) &tperror) != TE_OK) {
		tpqputs("wt_sense: could not read tape drive status");
		return TE_ERR;
	}
	err = tperror.exs;	/* get exception status bits */
	if (err & (TP_ST0|TP_ST1))
		printk(TPQIC_NAME ": tp_sense: status: %x, error count: %d, underruns: %d\n",
			tperror.exs, tperror.dec, tperror.urc);
	else
		tpqputs("tp_sense: no errors at all");

	/* 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 (mbits & WTC_ONLINE)
			gs |= GMT_ONLINE(-1);
		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);	/* bottom of tape */
	}
	ioctl_status.mt_gstat = gs;

	err &= ~ignore;		/* mask unwanted errors -- not the correct way, use exception nrs */
	report_error(err);	/* otherwise, report_error() may fail */
	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 (may take very long)
 */
static int wait_for_rewind(time_t timeout)
{
	int stat;

	stat = inb(QIC_STAT_PORT) & WTS_STAT;
	printk(TPQIC_NAME ": Waiting for (re-) wind to finish: stat=0x%x\n", stat);

	stat = wait_for_ready(timeout);

	if (stat != TE_OK) {
			tpqputs("(re-) winding failed\n");
	}
	return stat;
} /* wait_for_rewind */



/* Perform a full QIC02 command, and wait for completion,
 * check status when done. Complain about exceptions.
 *
 * This function should return an OS error code is something is wrong,
 * 0 otherwise.
 *
 */
static int ll_do_qic_cmd(int cmd, time_t timeout)
{
	int stat;

	if (status_dead)
		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 TE_ERR;
		/* else nothing to worry about, I hope */
		stat = TE_OK;
	}
	if (stat != TE_OK)
		return stat;

	stat = send_qic02_cmd(cmd, timeout, 0);	/* (checks for exceptions) */

	if (stat==TE_EX) {
		if (tp_sense(TP_WRP|TP_BOM|TP_EOM|TP_FIL)!=TE_OK) {
			tpqputs("Exception persist in ll_do_qic_cmd()");
			status_dead = YES;
			return -ENXIO;
			/* if rdstatus fails too, we're in trouble */
		}
	}
	else if (stat!=TE_OK) {
		printk(TPQIC_NAME ": ll_do_qic_cmd: send_qic02_cmd failed, stat = 0x%x\n", stat);
		return -EIO;	/*** -EIO is probably not always appropriate */
	}
	if (cmd==QCMD_RD_FM) {
		status_eof_detected = NO;
	}

	if (timeout == TIM_R)
		stat = wait_for_rewind(timeout);
	else
		stat = wait_for_ready(timeout);

	if (stat==TE_EX) {
		if (tp_sense(TP_WRP|TP_BOM|TP_EOM|TP_FIL)!=TE_OK) {
			tpqputs("Exception persist in ll_do_qic_cmd()");
			status_dead = YES;
			return -ENXIO;
			/* if rdstatus fails too, we're in trouble */
		}
	}
	else if (stat!=TE_OK) {
		printk(TPQIC_NAME ": ll_do_qic_cmd: wait failed, stat == 0x%x\n", stat);
		return -EIO;
	}
	return 0;
} /* ll_do_qic_cmd */


/*
 * "Deactivating ONLINE during a READ also causes the"
 * "tape to be rewound to BOT." Ditto for WRITEs, except
 * a FM is written first.
 *
 * For reads:
 * When the tape device is closed, we have two options:
 *	1) de-activate ONLINE and cause a rewind to BOT
 *	2) do a READ_FM.
 * Option 1 is annoying when you want to get the next file on tape.
 * Option 2 is annoying when you haven't read the entire file yet.
 * So, check for EOF, if so, do a READ_FM, else rewind it. Normal
 * (full) reads should leave the tape after the FM.
 *
 * For writes it's worse:
 * 	1) de-activate ONLINE: causes a WRITE_FM + rewind to BOT
 *	2) just do a WRITE_FM.
 * Problem is, I'd like to use 2 FMs as EOM marker. READ_FM in 
 * reverse is an optional QIC-02 command. Ditto SEEK_EOD. :-(
 * For the time being, I'll use 1 FM and option 2.
 *
 * I don't like this auto-rewind-on-every-possible-error thing much.
 */
static void finish_rw(int cmd)
{
	int stat;

	if (doing_read == YES) {

		if ((cmd != QCMD_RD_FM) && (status_eof_detected == YES)) {
			/* finish read-cycle properly */
			stat = ll_do_qic_cmd(QCMD_RD_FM, TIM_M);
			if (stat != TE_OK)
				tpqputs("Couldn't finish read cycle properly");
			status_eof_detected = NO;
			(void) tp_sense(0);
		} else if (status_eof_detected != YES) {
			/* do it the hard way */
			tpqputs("Warning: tape will rewind first.");
			mbits &= ~(WTC_ONLINE | WTC_DMA);
			outb_p(mbits, QIC_CTL_PORT);
		}
		doing_read = NO;

	} else if (doing_write == YES) {

		if (cmd != QCMD_WRT_FM) {
			/* finish off write cycle */
			stat = ll_do_qic_cmd(QCMD_WRT_FM, TIM_M);
			if (stat != TE_OK)
				tpqputs("Couldn't finish write cycle properly");
			(void) tp_sense(0);
		}
		doing_write = NO;

	}
} /* finish_rw */


static int do_qic_cmd(int cmd, time_t timeout)
{
	finish_rw(cmd);
	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;
	time_t t;

	/* 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, may sleep */
			return (tape_reset(1, 0)==TE_OK)? 0 : -EIO;

		case MTFSF:
			tpqputs("MTFSF forward searching filemark");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EIO;
			return do_qic_cmd(QCMD_RD_FM, TIM_F);

		case MTBSF:
			tpqputs("MTBSF backward searching filemark -- optional command");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EIO;
#ifdef TP_HAVE_BSF
			stat = do_qic_cmd(QCMD_RD_FM_BCK, TIM_F);
#else
			tpqputs("MTBSF not supported");
			stat = -EINVAL;
#endif
			status_eom_detected = status_eof_detected = NO;
			return stat;

		case MTFSR:
			tpqputs("MTFSR forward space record -- optional command");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EIO;
#ifdef TP_HAVE_FSR	/* This is an optional QIC-02 command */
			stat = do_qic_cmd(QCMD_SPACE_FWD, TIM_F);
#else
			tpqputs("MTFSR not supported");
			stat = -EINVAL;
#endif
			return stat;

		case MTBSR:
			/* we need this for appending files with GNU tar!! */
			tpqputs("MTFSR backward space record");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EIO;
#ifdef TP_HAVE_BSR	/* This is an optional QIC-02 command */
			stat = do_qic_cmd(QCMD_SPACE_BCK, TIM_F);
#else
			tpqputs("MTBSR not supported");
			stat = -EINVAL;
#endif
			status_eom_detected = status_eof_detected = NO;
			return stat;

		case MTWEOF:
			tpqputs("MTWEOF write eof mark");
			if (mode_access=READ)
				return -EIO;
			/* 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);

		case MTREW:
			tpqputs("MTREW rewinding tape");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EIO;
			status_eom_detected = status_eof_detected = NO;
			return do_qic_cmd(QCMD_REWIND, TIM_R);

		case MTOFFL:
			tpqputs("MTOFFL rewinding & going offline"); /*---*/
			/******* What exactly are we supposed to do, to take it 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 -EIO;
			status_eom_detected = status_eof_detected = NO;
			/**** do rewind depending on minor bits??? ***/
			stat = do_qic_cmd(QCMD_REWIND, TIM_R);
			mbits = 0;
			outb_p(mbits, QIC_CTL_PORT);
#if 0
			if (stat)
				return stat;
			stat = do_qic_cmd(0xf0, 50*HZ);	/* this might eject the tape -- please report */
#endif			
			return stat;

		case MTNOP:
			tpqputs("MTNOP setting status only");
			return (tp_sense(-1)==TE_OK)? 0 : -EIO;	/**** check return codes ****/

		case MTRETEN:
			tpqputs("MTRETEN retension tape");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EIO;
			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("MTBSFM not supported");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EIO;
			return -EINVAL;

		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?
			 * Another option is to use RD_FM, then RD_FM_BCK, but not all
			 * drives will support that!
			 */
			tpqputs("MTFSFM not supported");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EIO;
			return -EINVAL;

		case MTEOM:
			/* This should leave the tape ready for appending
			 * another file to the end, such that it would overwrite
			 * the last FM on tape. (Of the two FMs marking the EOM).
			 ------- I am not quite sure this whether all this is correct ------
			 */
			tpqputs("MTEOM search for End Of recorded Media");
			if ((mode_access==WRITE) && status_bytes_wr)
				return -EIO;
#ifdef TP_HAVE_EOD
			stat = do_qic_cmd(QCMD_SEEK_EOD, TIM_F);
#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???? ******/
			}
#endif
			return stat;

		case MTERASE:
			tpqputs("MTERASE -- ERASE TAPE !");
			/* give user a few seconds to pull out tape */
			t = jiffies;
			while (jiffies - t < 3*HZ)
				schedule();
#if 0
			if (mode_access==READ) 
				return -EIO;
#else
			/* GNU mt(1) is crazy. Erasing a tape in O_RDONLY. Hrmpf. */
#endif
			/* don't bother writing Filemark */
			status_eom_detected = status_eof_detected = NO;
			return do_qic_cmd(QCMD_ERASE, TIM_R);

		default:
			return -EINVAL;
	}
} /* 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!)
 * Upon entry, the DMA_address has been set. and the DMA flip-flop is clear.
 * When other devices use DMA they must ensure double byte accesses to the DMA 
 * controller are not interruptible. Floppy.c is ok.
 *
 * 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 page register  if we must.
 *	- adjust the timeout
 *	- tell the tape controller to start transfering
 * We assume the dma address and mode are, and remain, valid.
 */ 
static void inline dma_transfer(void)
{
	/* must have interrupts disabled here */

	CLEAR_DMA_FF(TAPE_QIC02_DMA);
	SET_DMA_MODE(TAPE_QIC02_DMA, dma_mode);
	SET_DMA_ADDR(TAPE_QIC02_DMA, buffaddr+dma_bytes_done);	/* full address */

#if TPQ_SET_PAGE_ALWAYS > 0
	/* Update the DMA page register, if required.
	 * This will only work correctly, if the buffer
	 * is aligned at 512 bytes.
	 */
	SET_DMA_PAGE(TAPE_QIC02_DMA, (buffaddr+dma_bytes_done)>>16);
#endif
	SET_DMA_COUNT(TAPE_QIC02_DMA, TAPE_BLKSIZE);

	/* start tape DMA controller */
	outb_p(WTC_DMA | WTC_ONLINE, QIC_CTL_PORT);
	/* start system DMA controller */
	ENABLE_DMA(TAPE_QIC02_DMA);
	/* block transfer should start now, jumping to the 
	 * interrupt routine when done or exception.
	 */
} /* dma_transfer */


/* start_dma() sets a DMA transfer up between the tape controller and
 * the kernel tape_qic02_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, volatile char * buf, unsigned long bytes_todo)
/* assume 'bytes_todo'>0 */
{
	int stat;
	
#ifdef TDEBUG
	tpqputs("start_dma() enter");
#endif
	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)) {
		switch (mode) {
			case READ:
				doing_read = YES;
				break;
			case WRITE:
				doing_write = YES;
				break;
			default:
				panic(TPQIC_NAME ": invalid mode in start_dma()");
		}

		/* First, we have to clear the status -- maybe remove TP_FIL???
		 */
		stat = tp_sense(((mode == WRITE)? 0 : TP_WRP) | TP_BOM | TP_FIL);
		if (stat != TE_OK)
			return stat;

		/* 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(TPQIC_NAME ": start_dma: init %s failed\n",
				(mode == WRITE)? "write" : "read");
			(void) tp_sense(0);
			return stat;
		}

		if (wait_for_ready(TIM_M) != TE_OK)
			return -EIO;
	}
	/* should allocate DMA channel here */

	status_expect_int = YES;
#if 0
	DISABLE_DMA(TAPE_QIC02_DMA);
	cli();
	CLEAR_DMA_FF(TAPE_QIC02_DMA);
	SET_DMA_MODE(TAPE_QIC02_DMA, dma_mode);
	SET_DMA_ADDR(TAPE_QIC02_DMA, buffaddr);	/* also sets page register */
	sti();
#endif
	/* This assumes tape is already positioned, but these
	 * semi-'intelligent' drives are unpredictable... Sigh.
	 */
	TIMERON(TIM_M*2);

	/* initiate first data block read from/write to the tape controller */
	cli();
	dma_transfer();
	sti();

#ifdef TDEBUG
	tpqputs("start_dma() end");
#endif
	return TE_OK;
} /* start_dma */


static void end_dma(unsigned long * bytes_done)
{
	int stat = TE_OK;

	TIMEROFF;
#ifdef TDEBUG
	tpqputs("end_dma() enter");
#endif
	DISABLE_DMA(TAPE_QIC02_DMA);
	stat = wait_for_ready(TIM_M);

	if (status_error || (stat!=TE_OK)) {
		tpqputs("DMA transfer error");
		(void) tp_sense(TP_WRP);
		/* no return here -- got to clean up first! */
	}
	/* take the tape controller offline */

	/* finish off DMA stuff */
	DISABLE_DMA(TAPE_QIC02_DMA);
	CLEAR_DMA_FF(TAPE_QIC02_DMA);
	dma_mode = 0;
	/* Should release dma channel here.
	 * Note: The drive is left on-line, ready for the next
	 * data transfer. Close should take it off-line.
	 * If we were to take it offline here, the controller would first
 	 * rewind it before starting to read.
	 */

	*bytes_done = dma_bytes_done;
	status_expect_int = NO;
#ifdef TDEBUG
	tpqputs("end_dma() exit");
#endif
} /* end_dma */

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


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


/* do_tape_qic02_interrupt() is called when the tape controller completes 
 * a DMA transfer. This is called by tape_qic02_interrupt() in tpqicint.s.
 * We are not allowed to sleep here! 
 * Check if the transfer was succesful, check if we need to transfer
 * more. If the buffer contains enough data/is emtpy enough, signal the
 * rw_tape_qic() thread to copy it to/from user space.
 * When we are finished, set flags to indicate end, disable timer.
 * NOTE: This *must* be fast! 
 */
void do_tape_qic02_interrupt(void)
{
	static short stat;
	static short r;

	if (status_expect_int) {
		printk("@");
		stat = inb(QIC_STAT_PORT);
		if ((stat & WTS_EXCEPTION) == 0) {	/* exception occured */
			/* something went wrong */
			tpqputs("isr: exception");
			/************ should deal with TP_FIL here, I think ****/
			status_error = -EIO;
			dma_mode = 0;	/* wake up rw() */
			status_expect_int = NO;
			wake_up(&tape_qic02_transfer);
			return;
		}
		/* return if tape controller not ready, or
		 * if dma channel hasn't finished last byte yet.
		 * PROBLEM: reading the DMA_STAT register will clear all
		 * Terminal Count flags. This may cause problems for
		 * other dma transfers!! [Maybe should use global status bits
		 * here and update each time someone reads DMA_STAT. :-(]
		 * --> Use global DMA channel allocation, disallow simultanious
		 *     use of the same DMA controller. This is not as bad as it
		 *     may sound because the DMA is supposed to saturate the
		 *     bus anyway. Also, the transfers are supposed to be 'short'.
		 *     This may not be true for tapes, but should work generally.
		 */
		r = 0;
		if (stat & WTS_READY) {		/* not ready */
			tpqputs("isr: ? Tape controller not ready");
			r = 1;
		}
		/* This assumes exclusive access to slave DMA controller */
		if (!(inb(DMA_STAT) & (1<<TAPE_QIC02_DMA))) {
			tpqputs("isr: ? TC not reached");
			r = 1;
		}
		if (r) 
			return;

		outb_p(WTC_ONLINE, QIC_CTL_PORT);

		/* 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;
#ifdef TDEBUG
			tpqputs("isr: dma_bytes_done");
#endif
			wake_up(&tape_qic02_transfer);
		} else {
			/* start next transfer */
			timer_table[TAPE_QIC02_TIMER].expires = jiffies + 6*HZ;
			dma_transfer();
		}
	} else {
		printk(TPQIC_NAME ": Unexpected interrupt\n");
	}
} /* do_tape_qic02_interrupt */


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


/* read/write routines:
 * This code copies between a kernel buffer and a user buffer. The 
 * actual data transfer is done using DMA and interrupts. Time-outs
 * are also used.
 *
 * When a filemark is read, we return '0 bytes read' and continue with the
 * next file after that.
 * When EOM is read, we return '0 bytes read' twice.
 * 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. We don't reblock.
 *
 */

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

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

	if (count % TAPE_BLKSIZE)	/* only allow mod 512 bytes at a time */
		return -EIO;

	/* Just assume everything is ok. Controller will scream if not. */
	/**	(void) tp_sense(TP_WRP);	 set FIL, EOM flags **/


	if (status_bytes_wr)	/* once written, no more reads */
		return -EACCES;

	/* see how much room there is in the buffer */
	bytes_todo = TPQBUF_SIZE;
	if (bytes_todo>count)
		bytes_todo = count;

	if (return_eof==YES) {
		return_eof = -YES;	/* move on next time */
		return 0;
	}
	if (return_eof==-YES) {
		/* application program has already received EOF
		 * (above), now continue with next file on tape,
		 * if possible.
		 */
		if (status_eom_detected)	/* check EOM */
			return 0;	/* nothing left to read */
		else {	/* just eof, there may be more files */
			/* skip over FM */
			stat = do_qic_cmd(QCMD_RD_FM, TIM_M); /**** not sure if this is needed  ***/
			if (status_eom_detected) {
				return 0; /* return EOF on subsequent reads */
			} else {
				/* If we can read more after a FM,
				 * we haven't reached EOM yet.
				 * There must be another file on tape.
				 */
				return_eof = NO;
				status_eof_detected = NO; /* reset this too */
				if (stat)
					return stat;
				/* else start reading data */
			}
		}
	}
	verify_area(buf, bytes_todo);	/* check buffer is valid */
	if (bytes_todo>0) {
		if (start_dma(READ, tape_qic02_buf, bytes_todo) != TE_OK) 
			/* should do sense() on error here */
			return -ENXIO;
	/* Wait for transfer to complete, interrupt should wake us */
		while (dma_mode != 0) {
			sleep_on(&tape_qic02_transfer);
		}
		end_dma(&bytes_done);
		if (bytes_done>bytes_todo) {
			tpqputs("read: Ooops, read more bytes than requested");
			return -EIO;
		}
		/* copy buffer to user-space in one go */
		if (bytes_done>0)
			memcpy_tofs( (void *) buf, (void *) tape_qic02_buf, bytes_done);
		if (bytes_todo != bytes_done)
			/* EOF|EOM detected. return EOF next time. */
			return_eof = YES;
	} else {
		tpqputs("read request for 0 bytes ignored");
	}

	if (bytes_done>0) {
		status_bytes_rd |= YES;
		filp->f_pos += bytes_done;
	}
	return bytes_done;
} /* tape_qic02_read */


static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf, int count)
{
	dev_t dev = inode->i_rdev;
	unsigned short flags = filp->f_flags;
	unsigned long bytes_todo, bytes_done = 0;
	static flag return_eof = NO;

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

	if (count % TAPE_BLKSIZE)	/* only allow mod 512 bytes at a time */
		return -EIO;

	/* Just assume everything is ok. Controller will scream if not. */
	/**	(void) tp_sense(0);	 set WRP, FIL, EOM flags **/

	if (mode_access=READ)
		return -EACCES; /*** right code? ***/
	if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP))
		return -EACCES;

	/* see how much room there's in the buffer */
	bytes_todo = TPQBUF_SIZE;
	if (bytes_todo>count)
		bytes_todo = count;
	
	/* account for write attempt after read EOF too: */
	if (return_eof != NO) {
		/* EOF on write means EOM. Don't want to break the tape. */
		return_eof = NO;	/* clear it */
		do_rewind = YES;	/* at EOT, so need rewind */
		return 0;
	}

	/* copy to DMA buffer and initiate transfer. Don't wait up */
	if (bytes_todo>0) {
		memcpy_fromfs( (void *) tape_qic02_buf, (void *) buf, bytes_todo);

		if (start_dma(WRITE, tape_qic02_buf, bytes_todo) != TE_OK)
			/* should do sense() on error here */
			return -ENXIO;
		/* Wait for write to complete, interrupt should wake us. */
		while ((status_error == 0) && (dma_mode != 0)) {
			sleep_on(&tape_qic02_transfer);
		}

		end_dma(&bytes_done);
		if (bytes_done>bytes_todo) {
			tpqputs("write: Ooops, wrote more bytes than requested");
			return -EIO;
		}
		if (status_error) {
			/* should report user error here if required */
			tpqputs("dma: error in writing");
			return -EIO;
		}
		if (bytes_todo != bytes_done)
			/* EOF|EOM detected. return EOT next time. */
			return_eof = YES;
	} else {
		/* if bytes_todo==0, should we write a filemark??? */
		tpqputs("Write request for 0 bytes ignored.");
	}

	if (bytes_done>0) {
		status_bytes_wr |= YES;
		filp->f_pos += bytes_done;
	}
	return bytes_done;
} /* tape_qic02_write */



/* tape_qic02_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.
 * 
 * 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 tape_qic02_open(struct inode * inode, struct file * filp)
{
	static unsigned old_minor = 0xff00;
	dev_t dev = inode->i_rdev;
	unsigned short flags = filp->f_flags;
	int s;

	printk("tape_qic02_open: dev=%x, flags=%x     ", dev, flags);

	if (status_open==YES) {
		tpqputs("tape driver busy");
		return -EBUSY;	/* only one at a time... */
	}

	status_bytes_rd = NO;
	status_bytes_wr = NO;

	/* O_NDELAY, O_NONBLOCK --> immediate report mode
	 * O_APPEND --> maybe do a SEEK_EOD before writing
 	 */
	switch (flags & O_ACCMODE) {
		case O_RDONLY:
			mode_access = READ;
			break;
		case O_WRONLY:	/* Strictly speaking this is not correct... */
		case O_RDWR:	/* reads are allowed as long as nothing is written */
			mode_access = WRITE;
			break;
		default:
			return -EINVAL;
	}

	mbits = WTC_ONLINE;
	outb_p(mbits, QIC_CTL_PORT);

	/* not allowed to do TP_DENS_* unless tape is rewound */
	if (TP_DENS(old_minor) != TP_DENS(MINOR(dev))) {
		/* force rewind if minor bits have changed,
		 * i.e. user wants to read tape in different density.
		 * [assuming single drive operation]
		 */
		tpqputs("Minor bits have changed. Forcing rewind.");
		do_rewind = YES;
	}

	if (do_rewind == YES) {
		s = do_qic_cmd(QCMD_REWIND, TIM_R);
		if (s != 0)
			return s;
		do_rewind = NO;
	}


/* Note: After a reset command, the controller will rewind the tape
 *	 before performing any tape movement operation!
 */ 
	if (status_dead) {
		tpqputs("open: tape dead, attempting reset");
		if (tape_reset(1, 0)!=TE_OK) {
			return -ENXIO;
		} else {
			status_dead = NO;
			if (tp_sense(~(TP_ST1|TP_ILL)) != TE_OK) {
				tpqputs("open: wt_sense() failed\n");
				status_dead = YES;	/* try reset next time */
				return -EIO;
			}
		}
	}

	status_open = YES;	/* 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(old_minor) == TP_DENS(MINOR(dev))) || (TP_DENS(MINOR(dev)) == 0) )
		return 0;

	old_minor = MINOR(dev);
	do_rewind = NO;
	printk("density=%d\n", TP_DENS(dev));
	switch (TP_DENS(MINOR(dev))) {
		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_open = NO;	/* fail if fault occured */
		status_dead = YES;	/* force reset */
	}
	old_minor = MINOR(dev);
	return s;
} /* tape_qic02_open */


static int tape_qic02_readdir(struct inode * inode, struct file * filp, struct dirent * dp, int count)
{
	return -ENOTDIR;	/* not supported */
} /* tape_qic02_readdir */


/* Linus seems to prefer 'release' over 'close'. */
static void tape_qic02_release(struct inode * inode, struct file * filp)
{
	dev_t dev = inode->i_rdev;

	printk("tape_qic02_close: dev=%x\n", dev);

	finish_rw(0);	/* terminate any pending read or write cycle */

#if 0 /* this is probably permanent... */
	/* Write second filemark, if data has been written
	 * to mark end of media for user programs.
	 * NOTE: You can't write someting on tape and expect to recover
	 * the data *after* that!
	 */
	if (status_bytes_wr!=NO) {
		tpqputs("close: Should be writing FM now...");
		if (mode_access!=WRITE)
			printk("release: NOTE: mode_access == %x\n",mode_access);
		(void) do_qic_cmd(QCMD_WRT_FM, TIM_M);
	}
#endif

	if (status_dead==YES) {
		tpqputs("device dead on close!?");
		status_open = NO;
		return;
	}

	if (status_open==NO) {
		tpqputs("in release(): device not open");
		status_open = NO;
		return;
	}
 	/* Rewind only if minor number requires it AND 
	 * read/writes have been done.
	 */
	if (TP_REWCLOSE(dev) && (status_bytes_rd || status_bytes_wr)) {
		tpqputs("release: doing rewind...");
		(void) do_qic_cmd(QCMD_REWIND, TIM_R);
	}

	status_open = NO;
	return;
} /* tape_qic02_release */



/* ioctl allows user programs to rewind the tape and stuff like that */
static int tape_qic02_ioctl(struct inode * inode, struct file * filp, 
		     unsigned int iocmd, unsigned int ioarg)
{
	short i;
	int dev_maj = MAJOR(inode->i_rdev);
	int c;
	struct mtop operation;
	char *stp, *argp;

	printk(TPQIC_NAME ": ioctl(%4x, %4x, %4x)\n", dev_maj, iocmd, ioarg);

	/* check iocmd first */

	if (dev_maj != TAPE_QIC02_MAJOR) {
		printk(TPQIC_NAME ": Ooops! Wrong device?\n");
		/* A panic() would be apropriate here */
		return -ENODEV;
	}

	c = iocmd & IOCCMD_MASK;
	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("sizeof(struct mtop) does not match!");
			return -EFAULT;
		}

		/* copy mtop struct from user space to kernel space */
		stp = (char *) &operation;
		argp = (char *) ioarg;
		for (i=0; i<sizeof(struct mtop); i++)
			*stp++ = get_fs_byte(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.
		 */

		printk("OP op=%4x, count=%4x\n", operation.mt_op, operation.mt_count);
		while (operation.mt_count > 0) {
			ioctl_status.mt_resid = operation.mt_count--;	/**??**/
			if ((c = do_ioctl_cmd(operation.mt_op)) != 0)
				return c;
		}
		ioctl_status.mt_resid = 0;	/**??**/
		return 0;

	} else if (c == (MTIOCGET & IOCCMD_MASK)) {
		printk("GET ");

		/* compare expected struct size and actual struct size */
		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtget)) {
			tpqputs("sizeof(struct mtget) does not match!");
			return -EFAULT;
		}

		/* check for valid address */
		verify_area((void *) ioarg, sizeof(struct mtget));

		/* 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(struct mtget); i++) 
			put_fs_byte(*stp++, argp++);
		return 0;
	} else
		return -ENOTTY;	/* Other cmds not supported. */
} /* tape_qic02_ioctl */


/* these 'report' functions are for printing the returned values without changing the
 * read and write routines themselves.
 */
static int report_tape_qic02_write(struct inode * inode, struct file * filp, char * buf, int count)
{
	int s = tape_qic02_write(inode, filp, buf, count);
	printk(TPQIC_NAME ": exit write = %x\n", s);
	return s;
}


static int report_tape_qic02_read(struct inode * inode, struct file * filp, char * buf, int count)
{
	int s = tape_qic02_read(inode, filp, buf, count);
	printk(TPQIC_NAME ": exit read = %x\n", s);
	return s;
}


/* These are (most) of the interface functions: */
static struct file_operations tape_qic02_fops = {
	tape_qic02_lseek,		/* not allowed */
	report_tape_qic02_read,		/* read */
	report_tape_qic02_write,	/* write */
	tape_qic02_readdir,		/* not allowed */
	NULL,				/* select ??? */
	tape_qic02_ioctl,		/* ioctl */
	tape_qic02_open,		/* open */
	tape_qic02_release		/* release */
};


/* This is a hack to avoid getting 'signal 6' in cc1 v2.1 when compiling :-(
 * There is no other reason why I would want this in a separate function.
 */
static void bug_set_intr_gate(void)
{
	/* TAPE_QIC02_IRQ is defined in config*.h. */
	set_intr_gate(0x20 + TAPE_QIC02_IRQ, &tape_qic02_interrupt);
} /* bug_set_intr_gate */

/* init() is called from chr_dev_init() in kernel/chr_drv/mem.c */
void tape_qic02_init(void)
{
	unsigned long pad;

	/* install driver functions */
	chrdev_fops[TAPE_QIC02_MAJOR] = &tape_qic02_fops;

	printk(TPQIC_NAME ": %s, %s\n", rcs_revision, rcs_date);
	printk(TPQIC_NAME ": Configuration: IRQ %d, DMA %d, IO %xh\n", TAPE_QIC02_IRQ,
	       TAPE_QIC02_DMA, TAPE_QIC02_PORT);
	if (TPSTATSIZE != 6)
		panic(TPQIC_NAME ": internal error: tpstatus struct incorrect!\n");
	if ((TPQBUF_SIZE<512) ||  (TPQBUF_SIZE>=0x10000))
		panic(TPQIC_NAME ": internal error: DMA buffer size\n");

	printk(TPQIC_NAME ": DMA buffers: %u blocks\n", NR_BLK_BUF);

	/* Setup the page-address for the dma transfer.
	 * This assumes a one-to-one identity mapping for
	 * kernel addresses. In the near future this may no longer be true...
	 */
	buffaddr = (unsigned long) &tape_qic02_buf;
	printk(TPQIC_NAME ": DMA buffer address 0x%lx\n", buffaddr);
	if (buffaddr+TPQBUF_SIZE>=0x100000)
		panic(TPQIC_NAME ": DMA buffer *must* be in lower 1MB\n");

	/* See if the dma buffer contains a boundary crossing.
	 * If so, panic. Otherwise, don't. ;-)
	 */
	if ((buffaddr & 0xffff0000) != ((buffaddr + TPQBUF_SIZE - 1) & 0xffff0000)) {
		dma_cross = YES;
		pad = 0x10000 - ((buffaddr - TPQ_PADDING_SIZE) & 0xffff);
		printk(TPQIC_NAME ": suggest recompilation with #define TPQ_PADDING_SIZE 0x%x\n", pad);
		if (buffaddr % TAPE_BLKSIZE) {
			tpqputs("Alignment problem!");
			panic(TPQIC_NAME "DMA buffer contains a 64k boundary crossing!");
		}
	} else {
		dma_cross = NO;
	}

	/* set_intr_gate() clears the interrupt automatically, so if you
	 * want to share an irq, you'll have to use set_trap_gate() and
	 * probably use interrupt chaining. Each handler should check
	 * for the source of the interrupt. Tricky.
	 * For now, we just assume the interrupt is free for us to use.
	 */

	/* If this function call is replaced by the function body,
	 * gcc won't compile this program!
 	 */
	bug_set_intr_gate();
#if 0
	printk(TPQIC_NAME ": gate 0x20+%d set\n", TAPE_QIC02_IRQ);
#endif

	/* We must also *enable* the interrupt: */
	ENABLE_IRQ(TAPE_QIC02_IRQ);

	/* prepare timer */
	timer_table[TAPE_QIC02_TIMER].expires = 0;
	timer_table[TAPE_QIC02_TIMER].fn = tape_qic02_times_out;

	/* not allowed to sleep yet */
	if (tape_reset(1, 1)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK)
		status_dead = YES;

	if (is_exception()) {
		tpqputs("exception detected \n");
		(void) tp_sense(TP_WRP|TP_POR|TP_CNI);
	}

	/* initialize generic status for ioctl requests */
	ioctl_status.mt_type	= 0;	/* ---tape drive id */
	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;	/* not used */
	ioctl_status.mt_blkno	= 0;	/* not used */
	tpqputs("end init.");
} /* tape_qic02_init */


#endif /* CONFIG_TAPE_QIC02 */

