patch-pre2.0.6 linux/drivers/scsi/qlogicfas.c

Next file: linux/drivers/scsi/qlogicfas.h
Previous file: linux/drivers/scsi/qlogic.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file pre2.0.5/linux/drivers/scsi/qlogicfas.c linux/drivers/scsi/qlogicfas.c
@@ -0,0 +1,679 @@
+/*----------------------------------------------------------------*/
+/*
+   Qlogic linux driver - work in progress. No Warranty express or implied.
+   Use at your own risk.  Support Tort Reform so you won't have to read all
+   these silly disclaimers.
+
+   Copyright 1994, Tom Zerucha.   
+   zerucha@shell.portal.com
+
+   Additional Code, and much appreciated help by
+   Michael A. Griffith
+   grif@cs.ucr.edu
+
+   Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
+   help respectively, and for suffering through my foolishness during the
+   debugging process.
+
+   Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
+   (you can reference it, but it is incomplete and inaccurate in places)
+
+   Version 0.44 5/7/96 - kernel 1.2.0+, pcmcia 2.5.4+
+
+   Functions as standalone, loadable, and PCMCIA driver, the latter from
+   Dave Hind's PCMCIA package.
+
+   Redistributable under terms of the GNU Public License
+
+*/
+/*----------------------------------------------------------------*/
+/* Configuration */
+
+/* Set the following to 2 to use normal interrupt (active high/totempole-
+   tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
+   drain */
+#define QL_INT_ACTIVE_HIGH 2
+
+/* Set the following to 1 to enable the use of interrupts.  Note that 0 tends
+   to be more stable, but slower (or ties up the system more) */
+#define QL_USE_IRQ 1
+
+/* Set the following to max out the speed of the PIO PseudoDMA transfers,
+   again, 0 tends to be slower, but more stable.  */
+#define QL_TURBO_PDMA 1
+
+/* This should be 1 to enable parity detection */
+#define QL_ENABLE_PARITY 1
+
+/* This will reset all devices when the driver is initialized (during bootup).
+   The other linux drivers don't do this, but the DOS drivers do, and after
+   using DOS or some kind of crash or lockup this will bring things back
+   without requiring a cold boot.  It does take some time to recover from a
+   reset, so it is slower, and I have seen timeouts so that devices weren't
+   recognized when this was set. */
+#define QL_RESET_AT_START 0
+
+/* crystal frequency in megahertz (for offset 5 and 9)
+   Please set this for your card.  Most Qlogic cards are 40 Mhz.  The
+   Control Concepts ISA (not VLB) is 24 Mhz */
+#define XTALFREQ	40
+
+/**********/
+/* DANGER! modify these at your own risk */
+/* SLOWCABLE can usually be reset to zero if you have a clean setup and
+   proper termination.  The rest are for synchronous transfers and other
+   advanced features if your device can transfer faster than 5Mb/sec.
+   If you are really curious, email me for a quick howto until I have
+   something official */
+/**********/
+
+/*****/
+/* config register 1 (offset 8) options */
+/* This needs to be set to 1 if your cabling is long or noisy */
+#define SLOWCABLE 1
+
+/*****/
+/* offset 0xc */
+/* This will set fast (10Mhz) synchronous timing when set to 1
+   For this to have an effect, FASTCLK must also be 1 */
+#define FASTSCSI 0
+
+/* This when set to 1 will set a faster sync transfer rate */
+#define FASTCLK 0
+/*(XTALFREQ>25?1:0)*/
+
+/*****/
+/* offset 6 */
+/* This is the sync transfer divisor, XTALFREQ/X will be the maximum
+   achievable data rate (assuming the rest of the system is capable
+   and set properly) */
+#define SYNCXFRPD 5
+/*(XTALFREQ/5)*/
+
+/*****/
+/* offset 7 */
+/* This is the count of how many synchronous transfers can take place
+	i.e. how many reqs can occur before an ack is given.
+	The maximum value for this is 15, the upper bits can modify
+	REQ/ACK assertion and deassertion during synchronous transfers
+	If this is 0, the bus will only transfer asynchronously */
+#define SYNCOFFST 0
+/* for the curious, bits 7&6 control the deassertion delay in 1/2 cycles
+	of the 40Mhz clock. If FASTCLK is 1, specifying 01 (1/2) will
+	cause the deassertion to be early by 1/2 clock.  Bits 5&4 control
+	the assertion delay, also in 1/2 clocks (FASTCLK is ignored here). */
+
+/*----------------------------------------------------------------*/
+#ifdef PCMCIA
+#undef QL_INT_ACTIVE_HIGH
+#define QL_INT_ACTIVE_HIGH 0
+#define MODULE
+#endif 
+
+#include <linux/module.h>
+
+#ifdef PCMCIA
+#undef MODULE
+#endif 
+
+#include <linux/blk.h>	/* to get disk capacity */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "sd.h"
+#include "hosts.h"
+#include "qlogicfas.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_qlogicfas = {
+    PROC_SCSI_QLOGICFAS, 6, "qlogicfas",
+    S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/*----------------------------------------------------------------*/
+/* driver state info, local to driver */
+static int	    qbase = 0;	/* Port */
+static int	    qinitid;	/* initiator ID */
+static int	    qabort;	/* Flag to cause an abort */
+static int	    qlirq = -1;	/* IRQ being used */
+static char	    qinfo[80];	/* description */
+static Scsi_Cmnd   *qlcmd;	/* current command being processed */
+
+static int	    qlcfg5 = ( XTALFREQ << 5 );	/* 15625/512 */
+static int	    qlcfg6 = SYNCXFRPD;
+static int	    qlcfg7 = SYNCOFFST;
+static int	    qlcfg8 = ( SLOWCABLE << 7 ) | ( QL_ENABLE_PARITY << 4 );
+static int	    qlcfg9 = ( ( XTALFREQ + 4 ) / 5 );
+static int	    qlcfgc = ( FASTCLK << 3 ) | ( FASTSCSI << 4 );
+
+/*----------------------------------------------------------------*/
+/* The qlogic card uses two register maps - These macros select which one */
+#define REG0 ( outb( inb( qbase + 0xd ) & 0x7f , qbase + 0xd ), outb( 4 , qbase + 0xd ))
+#define REG1 ( outb( inb( qbase + 0xd ) | 0x80 , qbase + 0xd ), outb( 0xb4 | QL_INT_ACTIVE_HIGH , qbase + 0xd ))
+
+/* following is watchdog timeout in microseconds */
+#define WATCHDOG 5000000
+
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+   where something crashed or gets stuck at and as a simple profiler) */
+
+#if 0
+#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+
+/*----------------------------------------------------------------*/
+/* local functions */
+/*----------------------------------------------------------------*/
+static void	ql_zap(void);
+/* error recovery - reset everything */
+void	ql_zap()
+{
+int	x;
+unsigned long	flags;
+	save_flags( flags );
+	cli();
+	x = inb(qbase + 0xd);
+	REG0;
+	outb(3, qbase + 3);				/* reset SCSI */
+	outb(2, qbase + 3);				/* reset chip */
+	if (x & 0x80)
+		REG1;
+	restore_flags( flags );
+}
+
+/*----------------------------------------------------------------*/
+/* do pseudo-dma */
+static int	ql_pdma(int phase, char *request, int reqlen)
+{
+int	j;
+	j = 0;
+	if (phase & 1) {	/* in */
+#if QL_TURBO_PDMA
+rtrc(4)
+		/* empty fifo in large chunks */
+		if( reqlen >= 128 && (inb( qbase + 8 ) & 2) ) { /* full */
+			insl( qbase + 4, request, 32 );
+			reqlen -= 128;
+			request += 128;
+		}
+		while( reqlen >= 84 && !( j & 0xc0 ) ) /* 2/3 */
+			if( (j=inb( qbase + 8 )) & 4 ) {
+				insl( qbase + 4, request, 21 );
+				reqlen -= 84;
+				request += 84;
+			}
+		if( reqlen >= 44 && (inb( qbase + 8 ) & 8) ) {	/* 1/3 */
+			insl( qbase + 4, request, 11 );
+			reqlen -= 44;
+			request += 44;
+		}
+#endif
+		/* until both empty and int (or until reclen is 0) */
+rtrc(7)
+		j = 0;
+		while( reqlen && !( (j & 0x10) && (j & 0xc0) ) ) {
+			/* while bytes to receive and not empty */
+			j &= 0xc0;
+			while ( reqlen && !( (j=inb(qbase + 8)) & 0x10 ) ) {
+				*request++ = inb(qbase + 4);
+				reqlen--;
+			}
+			if( j & 0x10 )
+				j = inb(qbase+8);
+
+		}
+	}
+	else {	/* out */
+#if QL_TURBO_PDMA
+rtrc(4)
+		if( reqlen >= 128 && inb( qbase + 8 ) & 0x10 ) { /* empty */
+			outsl(qbase + 4, request, 32 );
+			reqlen -= 128;
+			request += 128;
+		}
+		while( reqlen >= 84 && !( j & 0xc0 ) ) /* 1/3 */
+			if( !((j=inb( qbase + 8 )) & 8) ) {
+				outsl( qbase + 4, request, 21 );
+				reqlen -= 84;
+				request += 84;
+			}
+		if( reqlen >= 40 && !(inb( qbase + 8 ) & 4 ) ) { /* 2/3 */
+			outsl( qbase + 4, request, 10 );
+			reqlen -= 40;
+			request += 40;
+		}
+#endif
+		/* until full and int (or until reclen is 0) */
+rtrc(7)
+		j = 0;
+		while( reqlen && !( (j & 2) && (j & 0xc0) ) ) {
+			/* while bytes to send and not full */
+			while ( reqlen && !( (j=inb(qbase + 8)) & 2 ) ) {
+				outb(*request++, qbase + 4);
+				reqlen--;
+			}
+			if( j & 2 )
+				j = inb(qbase+8);
+		}
+	}
+/* maybe return reqlen */
+	return inb( qbase + 8 ) & 0xc0;
+}
+
+/*----------------------------------------------------------------*/
+/* wait for interrupt flag (polled - not real hardware interrupt) */
+static int	ql_wai(void)
+{
+int	i,k;
+	k = 0;
+	i = jiffies + WATCHDOG;
+	while ( i > jiffies && !qabort && !((k = inb(qbase + 4)) & 0xe0))
+		barrier();
+	if (i <= jiffies)
+		return (DID_TIME_OUT);
+	if (qabort)
+		return (qabort == 1 ? DID_ABORT : DID_RESET);
+	if (k & 0x60)
+		ql_zap();
+	if (k & 0x20)
+		return (DID_PARITY);
+	if (k & 0x40)
+		return (DID_ERROR);
+	return 0;
+}
+
+/*----------------------------------------------------------------*/
+/* initiate scsi command - queueing handler */
+static void	ql_icmd(Scsi_Cmnd * cmd)
+{
+unsigned int	    i;
+unsigned long	flags;
+
+	qabort = 0;
+
+	save_flags( flags );
+	cli();
+	REG0;
+/* clearing of interrupts and the fifo is needed */
+	inb(qbase + 5); 			/* clear interrupts */
+	if (inb(qbase + 5))			/* if still interrupting */
+		outb(2, qbase + 3);		/* reset chip */
+	else if (inb(qbase + 7) & 0x1f)
+		outb(1, qbase + 3);		/* clear fifo */
+	while (inb(qbase + 5)); 		/* clear ints */
+	REG1;
+	outb(1, qbase + 8);			/* set for PIO pseudo DMA */
+	outb(0, qbase + 0xb);			/* disable ints */
+	inb(qbase + 8); 			/* clear int bits */
+	REG0;
+	outb(0x40, qbase + 0xb);		/* enable features */
+
+/* configurables */
+	outb( qlcfgc , qbase + 0xc);
+/* config: no reset interrupt, (initiator) bus id */
+	outb( 0x40 | qlcfg8 | qinitid, qbase + 8);
+	outb( qlcfg7 , qbase + 7 );
+	outb( qlcfg6 , qbase + 6 );
+/**/
+	outb(qlcfg5, qbase + 5);		/* select timer */
+	outb(qlcfg9 & 7, qbase + 9);			/* prescaler */
+/*	outb(0x99, qbase + 5);	*/
+	outb(cmd->target, qbase + 4);
+
+	for (i = 0; i < cmd->cmd_len; i++)
+		outb(cmd->cmnd[i], qbase + 2);
+	qlcmd = cmd;
+	outb(0x41, qbase + 3);	/* select and send command */
+	restore_flags( flags );
+}
+/*----------------------------------------------------------------*/
+/* process scsi command - usually after interrupt */
+static unsigned int	ql_pcmd(Scsi_Cmnd * cmd)
+{
+unsigned int	i, j, k;
+unsigned int	result; 		/* ultimate return result */
+unsigned int	status; 		/* scsi returned status */
+unsigned int	message;		/* scsi returned message */
+unsigned int	phase;			/* recorded scsi phase */
+unsigned int	reqlen; 		/* total length of transfer */
+struct scatterlist	*sglist;	/* scatter-gather list pointer */
+unsigned int	sgcount;		/* sg counter */
+
+rtrc(1)
+	j = inb(qbase + 6);
+	i = inb(qbase + 5);
+	if (i == 0x20) {
+		return (DID_NO_CONNECT << 16);
+	}
+	i |= inb(qbase + 5);	/* the 0x10 bit can be set after the 0x08 */
+	if (i != 0x18) {
+		printk("Ql:Bad Interrupt status:%02x\n", i);
+		ql_zap();
+		return (DID_BAD_INTR << 16);
+	}
+	j &= 7; /* j = inb( qbase + 7 ) >> 5; */
+/* correct status is supposed to be step 4 */
+/* it sometimes returns step 3 but with 0 bytes left to send */
+/* We can try stuffing the FIFO with the max each time, but we will get a
+   sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
+	if(j != 3 && j != 4) {
+		printk("Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n", j, i, inb( qbase+7 ) & 0x1f );
+		ql_zap();
+		return (DID_ERROR << 16);
+	}
+	result = DID_OK;
+	if (inb(qbase + 7) & 0x1f)	/* if some bytes in fifo */
+		outb(1, qbase + 3);		/* clear fifo */
+/* note that request_bufflen is the total xfer size when sg is used */
+	reqlen = cmd->request_bufflen;
+/* note that it won't work if transfers > 16M are requested */
+	if (reqlen && !((phase = inb(qbase + 4)) & 6)) {	/* data phase */
+rtrc(2)
+		outb(reqlen, qbase);			/* low-mid xfer cnt */
+		outb(reqlen >> 8, qbase+1);			/* low-mid xfer cnt */
+		outb(reqlen >> 16, qbase + 0xe);	/* high xfer cnt */
+		outb(0x90, qbase + 3);			/* command do xfer */
+/* PIO pseudo DMA to buffer or sglist */
+		REG1;
+		if (!cmd->use_sg)
+			ql_pdma(phase, cmd->request_buffer, cmd->request_bufflen);
+		else {
+			sgcount = cmd->use_sg;
+			sglist = cmd->request_buffer;
+			while (sgcount--) {
+				if (qabort) {
+					REG0;
+					return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+				}
+				if (ql_pdma(phase, sglist->address, sglist->length))
+					break;
+				sglist++;
+			}
+		}
+		REG0;
+rtrc(2)
+/* wait for irq (split into second state of irq handler if this can take time) */
+		if ((k = ql_wai()))
+			return (k << 16);
+		k = inb(qbase + 5);	/* should be 0x10, bus service */
+	}
+/*** Enter Status (and Message In) Phase ***/
+	k = jiffies + WATCHDOG;
+	while ( k > jiffies && !qabort && !(inb(qbase + 4) & 6));	/* wait for status phase */
+	if ( k <= jiffies ) {
+		ql_zap();
+		return (DID_TIME_OUT << 16);
+	}
+	while (inb(qbase + 5)); 				/* clear pending ints */
+	if (qabort)
+		return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+	outb(0x11, qbase + 3);					/* get status and message */
+	if ((k = ql_wai()))
+		return (k << 16);
+	i = inb(qbase + 5);					/* get chip irq stat */
+	j = inb(qbase + 7) & 0x1f;				/* and bytes rec'd */
+	status = inb(qbase + 2);
+	message = inb(qbase + 2);
+/* should get function complete int if Status and message, else bus serv if only status */
+	if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
+		printk("Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
+		result = DID_ERROR;
+	}
+	outb(0x12, qbase + 3);	/* done, disconnect */
+rtrc(1)
+	if ((k = ql_wai()))
+		return (k << 16);
+/* should get bus service interrupt and disconnect interrupt */
+	i = inb(qbase + 5);	/* should be bus service */
+	while (!qabort && ((i & 0x20) != 0x20)) {
+		barrier();
+		i |= inb(qbase + 5);
+	}
+rtrc(0)
+	if (qabort)
+		return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+	return (result << 16) | (message << 8) | (status & STATUS_MASK);
+}
+
+#if QL_USE_IRQ
+/*----------------------------------------------------------------*/
+/* interrupt handler */
+static void	       ql_ihandl(int irq, void *dev_id, struct pt_regs * regs)
+{
+Scsi_Cmnd	   *icmd;
+	REG0;
+	if (!(inb(qbase + 4) & 0x80))	/* false alarm? */
+		return;
+	if (qlcmd == NULL) {		/* no command to process? */
+		int	i;
+		i = 16;
+		while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */
+		return;
+	}
+	icmd = qlcmd;
+	icmd->result = ql_pcmd(icmd);
+	qlcmd = NULL;
+/* if result is CHECK CONDITION done calls qcommand to request sense */
+	(icmd->scsi_done) (icmd);
+}
+#endif
+
+/*----------------------------------------------------------------*/
+/* global functions */
+/*----------------------------------------------------------------*/
+/* non queued command */
+#if QL_USE_IRQ
+static void	qlidone(Scsi_Cmnd * cmd) {};		/* null function */
+#endif
+
+/* command process */
+int	qlogicfas_command(Scsi_Cmnd * cmd)
+{
+int	k;
+#if QL_USE_IRQ
+	if (qlirq >= 0) {
+		qlogicfas_queuecommand(cmd, qlidone);
+		while (qlcmd != NULL);
+		return cmd->result;
+	}
+#endif
+/* non-irq version */
+	if (cmd->target == qinitid)
+		return (DID_BAD_TARGET << 16);
+	ql_icmd(cmd);
+	if ((k = ql_wai()))
+		return (k << 16);
+	return ql_pcmd(cmd);
+
+}
+
+#if QL_USE_IRQ
+/*----------------------------------------------------------------*/
+/* queued command */
+int	qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+	if(cmd->target == qinitid) {
+		cmd->result = DID_BAD_TARGET << 16;
+		done(cmd);
+		return 0;
+	}
+
+	cmd->scsi_done = done;
+/* wait for the last command's interrupt to finish */
+	while (qlcmd != NULL)
+		barrier();
+	ql_icmd(cmd);
+	return 0;
+}
+#else
+int	qlogicfas_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+	return 1;
+}
+#endif
+
+#ifdef PCMCIA
+/*----------------------------------------------------------------*/
+/* allow PCMCIA code to preset the port */
+/* port should be 0 and irq to -1 respectively for autoprobing */
+void	qlogicfas_preset(int port, int irq)
+{
+	qbase=port;
+	qlirq=irq;
+}
+#endif
+
+/*----------------------------------------------------------------*/
+/* look for qlogic card and init if found */
+int	qlogicfas_detect(Scsi_Host_Template * host)
+{
+int	i, j;			/* these are only used by IRQ detect */
+int	qltyp;			/* type of chip */
+struct	Scsi_Host	*hreg;	/* registered host structure */
+unsigned long	flags;
+
+host->proc_dir =  &proc_scsi_qlogicfas;
+
+/* Qlogic Cards only exist at 0x230 or 0x330 (the chip itself decodes the
+   address - I check 230 first since MIDI cards are typically at 330
+
+   Theoretically, two Qlogic cards can coexist in the same system.  This
+   should work by simply using this as a loadable module for the second
+   card, but I haven't tested this.
+*/
+
+	if( !qbase ) {
+		for (qbase = 0x230; qbase < 0x430; qbase += 0x100) {
+			if( check_region( qbase , 0x10 ) )
+				continue;
+			REG1;
+			if ( ( (inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7 )
+			  && ( (inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7 ) )
+				break;
+		}
+		if (qbase == 0x430)
+			return 0;
+	}
+	else
+		printk( "Ql: Using preset base address of %03x\n", qbase );
+
+	qltyp = inb(qbase + 0xe) & 0xf8;
+	qinitid = host->this_id;
+	if (qinitid < 0)
+		qinitid = 7;			/* if no ID, use 7 */
+	outb(1, qbase + 8);			/* set for PIO pseudo DMA */
+	REG0;
+	outb(0x40 | qlcfg8 | qinitid, qbase + 8);	/* (ini) bus id, disable scsi rst */
+	outb(qlcfg5, qbase + 5);		/* select timer */
+	outb(qlcfg9, qbase + 9);			/* prescaler */
+#if QL_RESET_AT_START
+	outb( 3 , qbase + 3 );
+	REG1;
+	while( inb( qbase + 0xf ) & 4 );
+	REG0;
+#endif
+#if QL_USE_IRQ
+/* IRQ probe - toggle pin and check request pending */
+
+	if( qlirq == -1 ) {
+		save_flags( flags );
+		cli();
+		i = 0xffff;
+		j = 3;
+		outb(0x90, qbase + 3);	/* illegal command - cause interrupt */
+		REG1;
+		outb(10, 0x20); /* access pending interrupt map */
+		outb(10, 0xa0);
+		while (j--) {
+			outb(0xb0 | QL_INT_ACTIVE_HIGH , qbase + 0xd);	/* int pin off */
+			i &= ~(inb(0x20) | (inb(0xa0) << 8));	/* find IRQ off */
+			outb(0xb4 | QL_INT_ACTIVE_HIGH , qbase + 0xd);	/* int pin on */
+			i &= inb(0x20) | (inb(0xa0) << 8);	/* find IRQ on */
+		}
+		REG0;
+		while (inb(qbase + 5)); 			/* purge int */
+		j = -1;
+		while (i)					/* find on bit */
+			i >>= 1, j++;	/* should check for exactly 1 on */
+		qlirq = j;
+		restore_flags( flags );
+	}
+	else
+		printk( "Ql: Using preset IRQ %d\n", qlirq );
+
+	if (qlirq >= 0 && !request_irq(qlirq, ql_ihandl, 0, "qlogicfas", NULL))
+		host->can_queue = 1;
+#endif
+	request_region( qbase , 0x10 ,"qlogic");
+	hreg = scsi_register( host , 0 );	/* no host data */
+	hreg->io_port = qbase;
+	hreg->n_io_port = 16;
+	hreg->dma_channel = -1;
+	if( qlirq != -1 )
+		hreg->irq = qlirq;
+
+	sprintf(qinfo, "Qlogicfas Driver version 0.44, chip %02X at %03X, IRQ %d, TPdma:%d",
+	    qltyp, qbase, qlirq, QL_TURBO_PDMA );
+	host->name = qinfo;
+
+	return 1;
+}
+
+/*----------------------------------------------------------------*/
+/* return bios parameters */
+int	qlogicfas_biosparam(Disk * disk, kdev_t dev, int ip[])
+{
+/* This should mimic the DOS Qlogic driver's behavior exactly */
+	ip[0] = 0x40;
+	ip[1] = 0x20;
+	ip[2] = disk->capacity / (ip[0] * ip[1]);
+	if (ip[2] > 1024) {
+		ip[0] = 0xff;
+		ip[1] = 0x3f;
+		ip[2] = disk->capacity / (ip[0] * ip[1]);
+		if (ip[2] > 1023)
+			ip[2] = 1023;
+	}
+	return 0;
+}
+
+/*----------------------------------------------------------------*/
+/* abort command in progress */
+int	qlogicfas_abort(Scsi_Cmnd * cmd)
+{
+	qabort = 1;
+	ql_zap();
+	return 0;
+}
+
+/*----------------------------------------------------------------*/
+/* reset SCSI bus */
+int	qlogicfas_reset(Scsi_Cmnd * cmd)
+{
+	qabort = 2;
+	ql_zap();
+	return 1;
+}
+
+/*----------------------------------------------------------------*/
+/* return info string */
+const char	*qlogicfas_info(struct Scsi_Host * host)
+{
+	return qinfo;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = QLOGICFAS;
+
+#include "scsi_module.c"
+#endif
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this