/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION  1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */

/* $Header:scsi.c 12.0$ */
/* $ACIS:scsi.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/caio/RCS/scsi.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:scsi.c 12.0$";
#endif

/*
 * Hard Disk Driver for SCSI IBM 9332 disk units
 *
 * DONE:
 *	Minidisk support is now included.
 *	Set Capacity of each lun at init.
 *	Turn on the automatic BAD Block Forwarding on the disk.
 *	Format ioctl command SCSIIOCFORMAT done.
 *
 * TODO:
 *	Multiple DMA's simultaneously.
 *	Addition Error handling (Check for recovered errors etc.)
 *
 */

/*
 * 	The Controller ic->ic_tab.b_active variable uses the following FLAGS:
 *     		0 = Controller not busy (Tag and Buf on adapter available)
 *    		1 = Controller busy (All tags and/or Bufs are in use)
 *
 * 	The Device dp->b_active variable uses the following FLAGS:
 *     		0 = Drive not on the controller active list
 *    		1 = Drive on the controller active list
 */

#include "sc.h"
#if NSC > 0
#include        "../h/param.h"
#include        "../h/vm.h"
#include        "../h/buf.h"
#include        "../h/time.h"
#include        "../h/proc.h"
#include        "../h/errno.h"
#include        "../machine/pte.h"
#include        "../machine/io.h"

#include	"../machineio/dmavar.h"
#include        "../machineio/scsireg.h"
#include        "../machineio/ibm9332scsi.h"
#include	"../machineio/scsiconfig.h"
#include	"../machineio/scsiio.h"

#include        "../machineio/ioccvar.h"
#include        "../h/kernel.h"
#include	"../h/systm.h"
#include	"../h/cmap.h"
#include        "../h/dk.h"
#include	"../h/ioctl.h"
#include        "../machine/mmu.h"
#include	"../machine/dkio.h"
#include	"../machine/debug.h"

#ifdef FINDITRASH
#include	"../h/inode.h"
#include	"../h/fs.h"
#endif FINDITRASH

/*
 * minor device uses following bits
 *
 * bits 0-2     partition number
 * bit  3-5     drive number
 *
 * drives 0-6 are units 0-6 on controller 0
 * drives 7-13 are units 0-6 on controller 1
 */

#define DRIVE(d)	(minor(d) >> 3)
#define PART(p)		(minor(p) & 0x07)

/*
 * misc defines and declarations
 */

#define PAGESIZE	2048
#define SCSITIMEOUT	10		/* seconds til interrupt known lost */
#define MAX_WAIT_COUNT	5000000		/* Max wait count in scsiwait */
#define NO_ADDR		0		/* flag for no real address yet */
#define SCSI_BSIZE	512		/* SCSI Number of Bytes per Sector */
#define SCSI_BSHIFT	9
#define b_logblk	b_resid		/* Logical block number on device
					   (Called b_cylin in disksort) */

#define OK		0		/* Success */
#define RETRY_LATER	1		/* Might work later */
#define BAD		2		/* It's not going to work */

/*
 * watchdog routine (looks for lost interrupts)
 */

int scsiwstart, scsiwatch();

/*
 * macro for translating virtual buffer address to
 * a real address.
 */

caddr_t real_buf_addr();

#define REAL_BUF_ADDR(bp, bufaddr) (((bp->b_flags & B_PHYS) == 0) ? \
	((caddr_t)vtop(bufaddr)) : real_buf_addr(bp,bufaddr))

/*
 * 9332 error diagnostics
 */
static char bogus_error[] = "Bogus error code";
/*
 * Servo processer error codes
 */
static char *scsi_servo_errors[]= {
		bogus_error,					/* 0x00 */
		"Hall error",					/* 0x01 */
		"VCM driver unsafe",				/* 0x02 */
		"DAC bus parity error",				/* 0x03 */
		"Shorted Data Channel Control",			/* 0x04 */
		"Loss of sync",					/* 0x05 */
		"Brake system error",				/* 0x06 */
		"SID read inhibit error",			/* 0x07 */
		"SID write inhibit error",			/* 0x08 */
		"CMAC read inhibit error",			/* 0x09 */
		"CMAC write inhibit error",			/* 0x0a */
		"Write gate, AED level check and wrap test",	/* 0x0b */
		"Detector module error",			/* 0x0c */
		"Error in safety control line",			/* 0x0d */
		"SID byte parity error",			/* 0x0e */
		"Attention without SID detected error",		/* 0x0f */
		"AE/AGC error - servo card failure"		/* 0x10 */
		"AGC error - open write I select line",		/* 0x11 */
		"Falut in AGC write current source or DE",	/* 0x12 */
		"AE/AGC error bad DE, servo, attachment, or driver board",	/* 0x13 */
		"AE/AGC error bad DE, servo, attachment",	/* 0x14 */
		"Open write data line",				/* 0x15 */
		"AE/AGC error bad DE, servo, attachment, or driver board",	/* 0x16 */
		"AE/AGC error bad DE, servo, attachment, or driver board",	/* 0x17 */
		"AE module error",				/* 0x18 */
		"AE module error",				/* 0x19 */
		bogus_error,					/* 0x1a */
		bogus_error,					/* 0x1b */
		"SID attention with unknown error",		/* 0x1c */
		bogus_error,					/* 0x1d */
		bogus_error,					/* 0x1e */
		bogus_error,					/* 0x1f */
		bogus_error,					/* 0x20 */
		"SID status 5 read error",			/* 0x21 */
		"SID status A read error",			/* 0x22 */
		"Missing sector stuck",				/* 0x23 */
		"Sector stuck aticve before SID enabled",	/* 0x24 */
		"ID in sync stuck active before SID enable",	/* 0x25 */
		bogus_error,					/* 0x26 */
		bogus_error,					/* 0x27 */
		bogus_error,					/* 0x28 */
		"CMAC register read/write error",		/* 0x29 */
		"CMAC ram error",				/* 0x2a */
		"CMAC MCR error",				/* 0x2b */
		"CMAC SAR error",				/* 0x2c */
		bogus_error,					/* 0x2d */
		bogus_error,					/* 0x2e */
		bogus_error,					/* 0x2f */
		bogus_error,					/* 0x30 */
		"Demod 1's wrap error",				/* 0x31 */
		"Demod 0's wrap error",				/* 0x32 */
		bogus_error,					/* 0x33 */
		bogus_error,					/* 0x34 */
		bogus_error,					/* 0x35 */
		bogus_error,					/* 0x36 */
		bogus_error,					/* 0x37 */
		bogus_error,					/* 0x38 */
		"CMAC predriver low gain 00 wrap error",	/* 0x39 */
		"CMAC predriver low gain 50 wrap error",	/* 0x3a */
		"CMAC predriver low gain A8 wrap error",	/* 0x3b */
		"CMAC predriver high gain 00 wrap error",	/* 0x3c */
		"CMAC predriver high gain 50 wrap error",	/* 0x3d */
		"CMAC predriver high gain A8 wrap error",	/* 0x3e */
		bogus_error,					/* 0x3f */
		bogus_error,					/* 0x40 */
		bogus_error,					/* 0x41 */
		"Motor frozen error",				/* 0x42 */
		"Motor acceleration too low",			/* 0x43 */
		"Motor unable to reach speed",			/* 0x44 */
		"SID unable to come into sync",			/* 0x45 */
		"Actuator was not retracted",			/* 0x46 */
		"Actuator was not locked",			/* 0x47 */
		"Guard band not at OD during a recalibrate",	/* 0x48 */
		"Track 0 not found",				/* 0x49 */
		"Motor over speeds on bring-up",		/* 0x41 */
		"Unable to sync with other actuator before unlocking",	/* 0x4b */
		"Unable to sync at OD during recalibrations",		/* 0x4c */
		bogus_error,					/* 0x4d */
		bogus_error,					/* 0x4e */
		bogus_error,					/* 0x4f */
		bogus_error,					/* 0x50 */
		bogus_error,					/* 0x51 */
		bogus_error,					/* 0x52 */
		bogus_error,					/* 0x53 */
		bogus_error,					/* 0x54 */
		bogus_error,					/* 0x55 */
		bogus_error,					/* 0x56 */
		bogus_error,					/* 0x57 */
		bogus_error,					/* 0x58 */
		bogus_error,					/* 0x59 */
		bogus_error,					/* 0x5a */
		bogus_error,					/* 0x5b */
		bogus_error,					/* 0x5c */
		bogus_error,					/* 0x5d */
		bogus_error,					/* 0x5e */
		bogus_error,					/* 0x5f */
		bogus_error,					/* 0x60 */
		"Unsupported command",				/* 0x61 */
		"Invalid head",					/* 0x62 */
		"Invalid track",				/* 0x63 */
		"Invalid command mode",				/* 0x64 */
		"Head shift exceed maximum amount",		/* 0x65 */
		"Didn't recieve serial port reset as first command",	/* 0x66 */
		"Head offset not 0 when entering command mode 0 or 1",	/* 0x67 */
		bogus_error,					/* 0x68 */
		"Invalid spindle motor current levels",		/* 0x69 */
		bogus_error,					/* 0x6a */
		bogus_error,					/* 0x6b */
		bogus_error,					/* 0x6c */
		bogus_error,					/* 0x6d */
		bogus_error,					/* 0x6e */
		bogus_error,					/* 0x6f */
		"Inside one track to go for too long",		/* 0x70 */
		"VCM inhibit",					/* 0x71 */
		"Settle error",					/* 0x72 */
		"VCM integration during settle",		/* 0x73 */
		"VCM current check error (from SAR wrap)",	/* 0x74 */
		"VMC zero offset too high",			/* 0x75 */
		bogus_error,					/* 0x76 */
		bogus_error,					/* 0x77 */
		"24V supply too low",				/* 0x78 */
		"Measured  gamma out of limits",		/* 0x79 */
		"Measured  bias out of limits",			/* 0x7a */
		"Demodulator calibraton of of limits",		/* 0x7b */
		bogus_error,					/* 0x7c */
		bogus_error,					/* 0x7d */
		bogus_error,					/* 0x7e */
		bogus_error,					/* 0x7f */
		bogus_error,					/* 0x80 */
		"Bad coherence during calibration",		/* 0x81 */
		bogus_error,					/* 0x82 */
		bogus_error,					/* 0x83 */
		bogus_error,					/* 0x84 */
		bogus_error,					/* 0x85 */
		"Too many missing sectors, or bad coherence",	/* 0x86 */
		"Demod ready line stuck inactive",		/* 0x87 */
		"Demod ready line stuck active",		/* 0x88 */
};
/*
 * File processor error codes
 */
static char *scsi_file_errors[]= {
		bogus_error,					/* 0x00 */
		"Attention line from both servo cards active",	/* 0x01 */
		"Attention line servo card 0 active",		/* 0x02 */
		"Attention line servo card 1 active",		/* 0x03 */
		"Clock check error from both servo cards",	/* 0x04 */
		"Clock check error from servo card 0",		/* 0x05 */
		"Clock check error from servo card 1",		/* 0x06 */
		"Sector checker error",				/* 0x07 */
		"Wrap PHILO I/O check error",			/* 0x08 */
		"Read & write both active on encoder/decoder",	/* 0x09 */
		bogus_error,					/* 0x0a */
		"Sync byte not found during write",		/* 0x0b */
		"Command sequence error",			/* 0x0c */
		"LRC error",					/* 0x0d */
		"Parity error on encoder/decoder ECC bus",	/* 0x0e */
		"Sector pulse load error",			/* 0x0f */
		"Hardware detected flag byte miscompare",	/* 0x10 */
		"Power supplies failing",			/* 0x11 */
		"Read & write both active on command buffer",	/* 0x12 */
		"ECC hardware error",				/* 0x13 */
		"Read/write counter parity error",		/* 0x14 */
		"Operation too long",				/* 0x15 */
		"Byte clock checker",				/* 0x16 */
		bogus_error,					/* 0x17 */
		"Encoder/decoder error",			/* 0x18 */
		"No hardware error found active in error registers",	/* 0x19 */
		"Hardware detected sectore number miscompare",	/* 0x1a */
		"Index counter out of sync with disk during verify",	/* 0x1b */
		"FP software injected error",			/* 0x1c */
		"Sync byte not found during read",		/* 0x1d */
		"ECC not on the fly error from interrupt",	/* 0x1e */
		"ECC on the fly error not corrected by single burst",	/* 0x1f */
		"Max retries attempted for locate verify",	/* 0x20 */
		"IP write inhibit error",			/* 0x21 */
		"Read inhibit timeout before locate verify",	/* 0x22 */
		"Sector pulse timeout before locate verify",	/* 0x23 */
		"Correct track verify failed",			/* 0x24 */
		bogus_error,					/* 0x25 */
		bogus_error,					/* 0x26 */
		"Sector pulse timeout after start of read/write",	/* 0x27 */
		"Read inhibit timeout after access or head shift",	/* 0x28 */
		"Read inhibit timeout after SP reset",		/* 0x29 */
		bogus_error,					/* 0x2a */
		bogus_error,					/* 0x2b */
		bogus_error,					/* 0x2c */
		"Invalid partial TCB",				/* 0x2d */
		"Negative number of spares",			/* 0x2f */
		"Invalid displacement or defect list in RIP data",	/* 0x2f */
		"Invalid RIP data found",			/* 0x30 */
		"Invalid sector interleave factor",		/* 0x31 */
		"Read inhibit timeoout before format verify",	/* 0x32 */
		"Sector pulse timeoout before format verify",	/* 0x33 */
		"Wrong servo processer response",		/* 0x34 */
		"Correct track verify failed in format",	/* 0x35 */
		"Invalid locate spare parameter",		/* 0x36 */
		"Sector pulse timeout for sync operation",	/* 0x37 */
		"Invalid read modulo prameter",			/* 0x38 */
		"Server processer serial port echo timeout",	/* 0x39 */
		"Invalid read/write sequencer paramter",	/* 0x3a */
		"Invalid parameter for set server sequencer",	/* 0x3b */
		"Invalid move TSM to control store paramter",	/* 0x3c */
		"Invalid cylinder/head byts in sector ID",	/* 0x3d */
		"Invalid sector number in sector ID",		/* 0x3e */
		"Invalid sector number in defect list",		/* 0x3f */
		"Invalid displacement in sector ID",		/* 0x40 */
		bogus_error,					/* 0x41 */
		bogus_error,					/* 0x42 */
		bogus_error,					/* 0x43 */
		"Max sectors loaned exceeded",			/* 0x44 */
		"Negative sectors loaned",			/* 0x45 */
		bogus_error,					/* 0x46 */
		bogus_error,					/* 0x47 */
		"Invalid displacement & loaned combination",	/* 0x48 */
		"Timer/Counter out of bounds during sector setup",	/* 0x49 */
		"Invalid deep DRP restore",			/* 0x4a */
		"Command from IP for down facility",		/* 0x4b */
		"Invalid control store RAM code",		/* 0x4c */
		"Index counter out of sync with disk during format",	/* 0x4d */
		"Initial microcode load check sum error"	/* 0x4e */
		"Invalid DFC command",				/* 0x4f */
		"Invalid sector history",			/* 0x50 */
		"Invalid sector history",			/* 0x51 */
		"Invalid sector history",			/* 0x52 */
		"Invalid defect information during format",	/* 0x53 */
		"Timer/Counter out of bounds during sync",	/* 0x54 */
		"Target sector on wrong track",			/* 0x55 */
		bogus_error,					/* 0x56 */
		"Invalid sector ID, SAT map, or grown defect map",	/* 0x57 */
		"Invalid command for server processor",		/* 0x58 */
		"Invalid device command code",			/* 0x59 */
		bogus_error,					/* 0x5a */
		bogus_error,					/* 0x5b */
		bogus_error,					/* 0x5c */
		"Invalid cylinder/head parameter",		/* 0x5d */
		"Aborted R/W due to an open attention line",	/* 0x5e */
		"Nested SP error",				/* 0x5f */
		"Illegal error code reported to ERP",		/* 0x60 */
		"Error from read/write adjust during FP ERP",	/* 0x61 */
};
/*
 * Interface processor error codes
 */
static char *scsi_ip_errors[]= {
		"Invalid error code or source",			/* 0x00 */
		"FP bring-up failure",				/* 0x01 */
		"FP bring-up failure",				/* 0x02 */
		"FP bring-up failure",				/* 0x03 */
		"Timeout waiting for interrupt",		/* 0x04 */
		"Invalid sector attribute parameter",		/* 0x05 */
		"Data buffer not available",			/* 0x06 */
		"FP timeout",					/* 0x07 */
		"Check sum error in IP code",			/* 0x08 */
		"RAM code level <> ROM code level",		/* 0x09 */
		"Timeout waiting for TCB",			/* 0x0a */
		"Invalid command descripter block",		/* 0x0b */
		"Invalid control byte in command block",	/* 0x0c */
		"Logical block out of range",			/* 0x0d */
		"Logical block transfer length out of range",	/* 0x0e */
		"Logical block address out of range",		/* 0x0f */
		"Logical block transfer length out of range",	/* 0x10 */
		"Timeout occurred while using TCB",		/* 0x11 */
		"FP ended TCB without completing the transfer",	/* 0x12 */
		"Multiple commands to single lun",		/* 0x13 */
		"Unsupported command sent to lun",		/* 0x14 */
		"Error message receive while doing a command",	/* 0x15 */
		"Invalid interleave facter in command block",	/* 0x16 */
		"Invalid data transfered to target",		/* 0x17 */
		"Cannot reassign block",			/* 0x18 */
		"Cannot reassign block",			/* 0x19 */
		"Cannot reassign block",			/* 0x1a */
		"Cannot reassign block",			/* 0x1b */
		"Grown defect map full",			/* 0x1c */
		"Release request rejected",			/* 0x1d */
		"IP reset due to FP control store parity error",/* 0x1e */
		"IP reset due to IP control store parity error",/* 0x1f */
		"CII sequence error",				/* 0x20 */
		"CII host unsuccessful transfer error",		/* 0x21 */
		"CII bus parity error",				/* 0x22 */
		"CII bus control octet out of context error",	/* 0x23 */
		"CII command packet with outstanding error",	/* 0x24 */
		"CII async packet pending not cleared after error",	/* 0x25 */
		"CII facility busy error",			/* 0x26 */
		"CII bus control octet rejected"		/* 0x27 */
		"CII bus control octet rejected"		/* 0x28 */
		"CII bus control octet rejected"		/* 0x29 */
		"CII unsupported command packet length",	/* 0x2a */
		"CII sync out count <> sync in count",		/* 0x2b */
		"CII master termination error",			/* 0x2c */
		"CII bus control octet rejected",		/* 0x2d */
		"CII LRC error",				/* 0x2e */
		"CII internal parity error",			/* 0x2f */
		"CII reported invalid error condition",		/* 0x30 */
		"Lun in degrated mode-FP bringup",		/* 0x31 */
		"Lun in degrated mode-FP code load failure",	/* 0x32 */
		"Lun in degrated mode-IP code load failure",	/* 0x33 */
		"Lun in degrated mode-Attribute load failure",	/* 0x34 */
		"Lun in degrated mode-Format failure",		/* 0x35 */
		"Lun in degrated mode-Reassign block failure",	/* 0x36 */
		"Lun in degrated mode-Deep DRP failure",	/* 0x37 */
		"Lun in degrated mode-Format failure",		/* 0x38 */
		"Lun in degrated mode-Reassign block failure",	/* 0x39 */
		"Lun in degrated mode-Deep DRP failure",	/* 0x3a */
		"Lun in degrated mode-error logging failure",	/* 0x3b */
		bogus_error,					/* 0x3c */
		bogus_error,					/* 0x3d */
		bogus_error,					/* 0x3e */
		"Muiltiple ID messages",			/* 0x3f */
		"Logical block has been reassigned",		/* 0x40 */
		"Automatic block reassignment failed-table full",/* 0x41 */
		"Block reassignment is recommended",		/* 0x42 */
		"Automatic block reassignment failed",		/* 0x43 */
};

#define SCSI_9332_SERVO		0
#define SCSI_9332_FILE		1
#define SCSI_9332_IP		2
#define SCSI_9332_BOGUS		3
	
int scsi_max_error[]= {
	sizeof(scsi_servo_errors)/sizeof(scsi_servo_errors[0]),
	sizeof(scsi_file_errors)/sizeof(scsi_file_errors[0]),
	sizeof(scsi_ip_errors)/sizeof(scsi_ip_errors[0]),
};

char **scsi_error_title[]= {
		scsi_servo_errors,
		scsi_file_errors,
		scsi_ip_errors,
};

/*
 * sense data keys
 */
char *scsi_key_type[] = {
	"No Sense",		/* 0 */
	"Recovered Error",	/* 1 */
	"Not Ready",		/* 2 */
	"Medium Error",		/* 3 */
	"Hardware Error",	/* 4 */
	"Illegal Request",	/* 5 */
	"Unit Attention", 	/* 6 */
	"BOGUS KEY",		/* 7 */
	"BOGUS KEY",		/* 8 */
	"BOGUS KEY",		/* 9 */
	"BOGUS KEY",		/* a */
	"Aborted Command",	/* b */
	"BOGUS KEY",		/* c */
	"BOGUS KEY",		/* d */
	"BOGUS KEY",		/* e */
	"BOGUS KEY",		/* f */
};

/*
 * standard addresses for hard disk controllers
 */

caddr_t scsistd[] = { (caddr_t)0xf0000D52, (caddr_t)0xf0000952, 0 };

#define NSCSISTD	(sizeof(scsistd)/sizeof(scsistd[0]) - 1)

/*
 * drive and controller related structures
 */

struct iocc_device *scsidinfo[NSC];
struct iocc_ctlr *scsiminfo[NSCC];

int scsiprobe(), scsislave(), scsiattach(), scsiint(), scsistrategy();
int scsiprobe(), scsislave(), scsiattach(), scsiint(), scsistrategy(),
    scsidmago();
u_short scsi_cleartag();
void scsiminphys();

short   scsi_chan[] = {
	DMA_CHAN0,
	DMA_CHAN1,
	DMA_CHAN3,
	DMA_CHAN5,
	DMA_CHAN6,
#ifdef	NO_SLOT8		/* only define channel 7 if you know you aren't
				 * going to use the coprocesser slot.
				 */
	DMA_CHAN7,
#endif
	DMA_END_CHAN,
};
short	dma_select_chan();

struct iocc_driver sccdriver = {
	scsiprobe,	/* probe */
	scsislave,	/* slave */
	scsiattach,	/* attach */
        scsidmago,	/* dgo */
	scsistd,	/* addr */
	"sc",		/* dname */
	scsidinfo,	/* dinfo */
	"scc",	/* mname */
	scsiminfo,	/* minfo */
	scsiint,	/* intr */
	0x0,		/* csr */
        0		/* chanrelse */
};

#ifdef FINDITRASH
struct scfs {
	unsigned	fs_bsize;
	unsigned	inode_start;
	unsigned	inode_end;
} scfs[NSC*8];
#endif FINDITRASH

/*
 * generic names for required spl's
 */

#define spl_disk()      _spl4()         /* at ibm032 level 4 */
#define spl_high()      _spl2()         /* at ibm032 level 2 */

/*
 * Partition tables
 */
struct partab {
        struct part_entry {
                daddr_t nblocks;        /* # of blocks in partition */
                int     cyloff;         /* starting cylinder of partition */
        } part[8];
};

/* scsi200: #sectors/track=74, #tracks/cylinder=4 #cylinders=1321 */
/*	    actual usable blocks 391182				  */
/* scsi400: #sectors/track=74, #tracks/cylinder=4 #cylinders=2643 */
/*	    actual usable blocks 782364				  */

struct partab scsi200_sizes = {
	15884,	1,		/* A=cyl 1 thru 54 */
	33440,	55,		/* B=cyl 55 thru 167 */
	391016,	0,		/* C=cyl 0 thru 1320 */
	15884,	168,		/* D=cyl 168 thru 221 */
	55936,	222,		/* E=cyl 222 thru 410 */
	267880,	411,		/* F=cyl 411 thru 1315 */
	339808,	168,		/* G=cyl 168 thru 1315 */
	0,	0,
}, scsi400_sizes = {
	15884,	1,		/* A=cyl 1 thru 54 */
	66880,	55,		/* B=cyl 55 thru 280 */
	782328,	0,		/* C=cyl 0 thru 2642 */
	15884,	1266,		/* D=cyl 1266 thru 1319 */
	307200,	1320,		/* E=cyl 1320 thru 2357 */
	82880,	2358,		/* F=cyl 2358 thru 2637 */
	406112,	1266,		/* G=cyl 1266 thru 2637 */
	291346,	281,		/* H=cyl 281 thru 1265 */
};

/* Actual Partition table (Loaded from minidisk or defaults above) */
struct partab scsioff[NSC];

/*
 * hard disk statics for each drive type
 */

struct scsist {
        struct 	partab *off;	/* 0: patition table */
        u_short ncpd;           /* 4: number of cylinders / drive */
        u_short nbps;           /* 8: number of bytes / sector */
        u_char	nspt;           /* 10: number of sectors / track */
        u_char	ntpc;           /* 11: number of tracks / cylinder */
        u_short nspc;           /* 12: number of sectors / cylinder */
	u_char	type[8];	/* 16: drive type */
} scsist[] = {

/*    off          ncpd    nbps    nspt    ntpc    nspc   type */

{ &scsi200_sizes,  1321,   512,    74,     4,      74*4, "scsi200" },
{ &scsi400_sizes,  2643,   512,    74,     4,      74*4, "scsi400" },
};

#define SCSI200_INDEX	0
#define SCSI400_INDEX	1

/*
 * Model identification match
 */
struct scsi_model {
	char	*model;		/* Model number as returned by INQUIRY CMD */
	char	*desc;		/* Description of drive */
	int	index;		/* Index into scsist table */
} scsi_model[] = {

/*	model		desc		index	*/
{	"0240",	"200 Mbyte Differential", SCSI200_INDEX	},
{	"0260",	"200 Mbyte Single-Ended", SCSI200_INDEX	},
{	"0440",	"400 Mbyte Differential", SCSI400_INDEX	},
{	"0460",	"400 Mbyte Single-Ended", SCSI400_INDEX	},
};


#define NSCSIST		(sizeof(scsist)/sizeof(struct scsist))
#define NSCSIMODEL	(sizeof(scsi_model)/sizeof(struct scsi_model))

/*
 * misc global variables for each drive on controller
 */

struct buf scsiutab[NSC];    	/* start of request queue */
struct buf *scsiutabp[NSC];	/* pointers to scsiutab */
struct buf rscsibuf[NSC];	/* header for raw i/o */
struct buf bscsibuf[NSC];	/* buffer header for reading bad block table */
struct buf *rscsibufp[NSC];	/* pointers to rscsibuf */

struct buf bound_buf[NSC];	/* Spare boundary buffers */

/*
 * misc global variables for each controller
 */

/* Tag information for a xfer */
struct scsi_tag_info {
	u_short	err_cnt;
        short	scsi_timer;		/* timer for watchdog */
	u_char flag;			/* Phase flag (See below) */
	u_char tag;			/* Index reference to itself */
	u_char granularity;		/* Transfer granularity */
	u_char st_bufnum;		/* Starting adapter buffer number */
	u_char cur_bufnum;		/* Current adapter buffer number */
	struct buf *bp;			/* Bp associated with this tag */
	char   *b_addr;			/* Save starting xfer address */
	int    b_bcount;		/* Save starting byte count */
	u_short blkcnt;			/* Current block count */
	struct scsi_tag_info *next;	/* Next tag in queue */
};

/* Flags to go into flag element of scsi_tag_info structure */

/* PHASES of the transfer */
#define SCSI_CMDPHASE		0x01	/* Command to peripheral */
#define SCSI_DMAPHASE		0x02	/* DMA from adapter */
#define	SCSI_BOUNDARY		0x04	/* Transfer runs over lun boundary */
#define	SCSI_MULTIDMA		0x08	/* Means multiple DMAs must occur,
					   happens on partial block transfers */
#define SCSI_INSENSECMD		0x10	/* This drive is doing a sense cmd */

/*
 * software structure per controller
 */

struct scsic_softc {
	int	scsi_flag;		/* Defined below */
	int	scsi_curtag;		/* Current (lowest) tag in use */
	int	scsi_nexttag;		/* Next tag to be allocated */

	/* Head and tail pointers for dma queue (Per controller) */
	struct scsi_tag_info *dmatip_head, *dmatip_tail;
	/* What adapter Buffers are in use */
	char	scsi_buf[SCSI_NCMDBUFS];

	/* Cache of available tag structures and pointers to them */
	struct scsi_tag_info *scsi_tag[SCSI_NTAGS];
	struct scsi_tag_info tag_info[SCSI_NTAGS];

} scsic_softc[NSCC];

/*
 * defines for scsi_flag in scsic_softc
 */
#define SCSIC_ATTACHED	0x01	/* This Controller is allready attached */
#define SCSI_DMABUSY	0x02	/* SCSI DMA in progress */

struct scsi_tag_info *scsi_gettag();

/*
 * software structure per drive contains information
 * particular to a given drive
 */

struct scsi_softc {
        int scsi_flag;			/* flag bits */

	/* Head and tail pointer for scsi cmd queue per lun (drive) */
	struct scsi_tag_info *cmdtip_head, *cmdtip_tail;

        struct partab sizes;		/* partition table for this drive */

	/* Area to read sense information into */
	scsi_sense	sense;

	/* Area to save tip and bp information while we do a sense */
	struct	scsi_tag_info	ti;
	struct	buf b;

} scsi_softc[NSC];

/*
 * defines for scsi_flag in scsi_softc
 */

#define SCSI_CMDBUSY		0x01	/* This drive is busy with SCSI cmd */

/*
 * macro to access drive partition table
 */

#define SCSIPART(drive, partno) \
	((struct part_entry *)&scsi_softc[(drive)].sizes.part[(partno)])


#define SCSI_MAX_ERR 3

/*
 * debug related defines
 */

#ifdef DEBUG
#define SCSIDEBUG(how,stmt) if (scsidebug & (how)) stmt     /* print it etc. */
int scsidebug = 0;
#else !DEBUG
#define SCSIDEBUG(how,stmt)       /* do nothing */
#endif DEBUG

#define scsiwaitvalid(a,b,c) scsiwait (a | SCSI_ST_VALID, b | SCSI_ST_VALID, c)

u_char scsi_dummy;		/* global to keep hc happy */

scsiprobe(reg,ic)
register caddr_t reg;
register struct iocc_ctlr *ic;
{
        register struct scsidevice *scsiaddr;
        register int result;
        u_short err;
        u_short err2;
	static int first_time =0;
	int timeout;

        SCSIDEBUG(SHOW_INIT,printf("scsiprobe(%x) called\n",reg));

	if (first_time++ == 0) {
        	SCSIDEBUG(SHOW_INIT,printf("scsiprobe first time\n",reg));
        	scsisetup();
		scsiaddr->scsi_config &= ~SCSI_CF_IEN;
	}
		
        scsiaddr = (struct scsidevice *) reg;

	/* Magic Reset procedure that documentation says needs to be 
	 * done before any other command can be done after a hardware
	 * reset.
	 */
	scsiaddr->scsi_aux = SCSI_AUX_RESET;

	DELAY (100000);

	/* Wait for Valid */
        scsiwaitvalid(0, 0, scsiaddr);

	/* Quick check to see if a controler is really here */
	if (scsiaddr->scsi_status == 0xFFFF)
		return (PROBE_BAD);

	/* 8 bit read Clears status register */
	scsi_dummy = *(char *) &scsiaddr->scsi_status;

	/* Wait for scsi to become not busy */
	timeout = MAX_WAIT_COUNT;
	while (scsiaddr->scsi_status & SCSI_ST_BUSY) {
		/* Clear status register */
		scsi_dummy = *(char *) &scsiaddr->scsi_status;
		if (--timeout)
			break;
	}

	/* Wait for scsi for not busy to give command */
	if (scsiwait (SCSI_ST_BUSY, 0, scsiaddr))
		return (PROBE_BAD); /* Timed out */

	/* Clear interrupt semaphore, possibly from another probe */
	SCSI_INT11_SEMA = (u_char) 0;
	SCSI_INT12_SEMA = (u_char) 0;

	/* 
	 * Enable Interrupts and set the interrupt level.
	 * legal levels are irq == 12 and irq == 11.
	 * If the user specifies a legal value, use it.
	 * Otherwise the adapters default to:
	 *	adapter 1 -- irq 11.
	 *	adapter 2 -- irq 12.
	 */
	SCSIDEBUG(SHOW_INIT,printf("req irq = %d, ctlr = %d \n",ic->ic_irq,ic->ic_ctlr););
	SCSIDEBUG(SHOW_INIT,printf("Using "););
	if ((ic->ic_irq == 12) || ((ic->ic_irq != 11) && (ic->ic_ctlr == 1))) {
		SCSIDEBUG(SHOW_INIT,printf("interrupt 12\n"););
		scsiaddr->scsi_config = SCSI_CF_IEN | SCSI_CF_INT12;
	} else {
		SCSIDEBUG(SHOW_INIT,printf("interrupt 11\n"););
		scsiaddr->scsi_config = SCSI_CF_IEN | SCSI_CF_INT11;
	}

	/* Perform BAT test (Interrupt will occur when they are done) */
	scsiaddr->scsi_cmd = SCSI_CMD_RBAT;
        
	/* Wait for a while for interrupt */
    PROBE_DELAY(5000000);	

	DELAY(100);

	/* Wait for Valid */
        if (scsiwaitvalid(0, 0, scsiaddr))
		goto bad;	/* Timed out */

	DELAY(100);

        if ((err2 = ((err = scsiaddr->scsi_status) & SCSI_ST_CCSM)) != SCSI_ST_AR) {

                result = PROBE_BAD;

                SCSIDEBUG(
			SHOW_INIT,
			printf("scsicprobe: addr %x diagnose=%x ERROR\n",
                        	scsiaddr, err)
		);
                SCSIDEBUG( SHOW_INIT,
			printf("	err2=%x, cmp=%x\n", err2, SCSI_ST_AR)
		);
        } else  {
                result = PROBE_OK;
		/*
		 * Delay here for a while in case disk has problems and
		 * takes a while to do self diagnostics!
		 */
		DELAY (10000000);
        }
	/* 8 bit read Clears status register */
	scsi_dummy = *(char *) &scsiaddr->scsi_status;

	SCSI_INT11_SEMA = (u_char) 0;
	SCSI_INT12_SEMA = (u_char) 0;

	DELAY(10000)

	scsiaddr->scsi_cmd = SCSI_CMD_MISC;
        
	/* Wait for a while for interrupt */
    PROBE_DELAY(5000000);	

	if (scsiwaitvalid(0, 0, scsiaddr)) { 
		printf("scc%d: Timeout waiting for adapter ROM read!!!\n",
			ic->ic_ctlr);
		/* 8 bit read Clears status register */
		scsi_dummy = *(char *) &scsiaddr->scsi_status;
		goto leave;
	}

	DELAY(100);

	/*
	 * decipher the output rom level
	 */
    if (scsiaddr->scsi_status & SCSI_ST_CCSM) {

		printf("scc%d: Couldn't read adapter ROM level!!!\n",
			ic->ic_ctlr);
	} else {
		u_short romlevel = scsiaddr->scsi_ext;

		if (romlevel < 0x0411) {
			printf("scc%d: DEVELOPEMENT LEVEL ADAPTER %x.%x\n",
			     ic->ic_ctlr,romlevel >> 8, romlevel & 0xff);
		} else {
			printf("scc%d: Adapter Level %x.%x\n",
			     ic->ic_ctlr,romlevel >> 8, romlevel & 0xff);
		}
	}

	/* 8 bit read Clears status register */
	scsi_dummy = *(char *) &scsiaddr->scsi_status;


	goto leave;
bad:
	result = PROBE_BAD;
leave:
	/* disable interrupts */
        scsiaddr->scsi_config &= ~SCSI_CF_IEN;

	/* Clear status register */
	scsi_dummy = *(char *) &scsiaddr->scsi_status;

	/* Clear interrupt semaphore */
	SCSI_INT11_SEMA = (u_char) 0;
	SCSI_INT12_SEMA = (u_char) 0;

        return(result);
}

scsisize(dev)
        dev_t dev;
{
        register int unit = DRIVE(dev);
        register struct iocc_device *iod;

        if (unit >= NSC || (iod=scsidinfo[unit]) == 0 || iod->iod_alive == 0)
                return (-1);

        return ((int)SCSIPART(unit, PART(dev))->nblocks);
}


scsislave(iod, scsiaddr)
register struct scsidevice *scsiaddr;
register struct iocc_device *iod;
{
	register int    unit = iod->iod_unit;
	register struct buf *dp;
	int s = spl_disk();

        SCSIDEBUG( SHOW_INIT,
		printf("scsislave called iod=%x scsiaddr=%x unit=%x\n",
                	iod, scsiaddr, unit)
	);
	/*DELAY (1000000);*/

	dp = &scsiutab[unit];
	dp->b_actf = NULL;
	dp->b_actl = NULL;
	dp->b_active = 0;

	splx(s);			        /* restore original priority */
	/* Always return OK because we can't check drive until attach */
	return(1);
}

scsi_check_status (scsiaddr, unit, statusp)
	register struct scsidevice *scsiaddr;
	register int unit;
	register u_short *statusp;
{
	register int result;
	u_short status;

	DELAY (10000);

	if (scsiwaitvalid(0, 0, scsiaddr)) {

		/* Clear status register */
		scsi_dummy = *(char *) &scsiaddr->scsi_status;

		return(0);
	}

	status = scsiaddr->scsi_status;
	if (statusp)
		*statusp = status;

	/* Clear status register */
	scsi_dummy = *(char *) &scsiaddr->scsi_status;

	if (!(status & SCSI_ST_CCS_VALID)) {
		SCSIDEBUG( SHOW_INIT,
			printf ("scsislave%d: SCSI Error (%x)\n", unit, status);
		);

		result = 0;
	} else if ((status & SCSI_ST_CCSM) != SCSI9332_GOOD) {
		SCSIDEBUG( SHOW_INIT,
			printf("sc%d: SCSI9332 Error (%x)\n",unit,status);
		);
		result = 0;
	} else {
		result = 1;
	}
	return (result);
}


scsiattach(iod)
register struct iocc_device *iod;
{
	int 	 unit = iod->iod_unit;
	register int ctlr = iod->iod_ctlr;
	register struct iocc_ctlr *ic = iod->iod_mi;
	register struct scsic_softc *csc = &scsic_softc [ctlr];
	register struct scsi_softc *cs = &scsi_softc[unit];
	register struct scsidevice *scsiaddr = (struct scsidevice *)ic->ic_addr;
	int	 index = SCSI200_INDEX;
	scsi_inqinfo inq;
	int	 result;
	int	 retry = 3;
	int	 degraded_mode = 0;
	struct	 scsist *st;
	u_short	 status;

        SCSIDEBUG(SHOW_INIT, printf("scsiattach called\n"));

	/* Only need to do this once per controller */
	if (!(csc->scsi_flag & SCSIC_ATTACHED)) {
		csc->scsi_flag |= SCSIC_ATTACHED;
		scsi_config_dma (ic);
	}

	/* Turn off interrupts while we figure this out */
	scsiaddr->scsi_config &= ~SCSI_CF_IEN;

	/* Let's check if the disk is there and ok */

	/* First check LUN 0 */
	do {
		/* Check if disk exist */
		scsicmd(scsiaddr, unit, 0, CMD_TUR, 0, 0, 0, 0, 0, 0, 0);

		if (result = scsi_check_status(scsiaddr, unit, &status)) {
			break;
		} else {
			if (status & SCSI_ST_CCS_VALID) {
				if ((result = scsi_get_sense_data (ic, unit, 0))
				     < 0) {
					DELAY (1000000);
				} else if (result == SCSI_SENSE_NR) {
					degraded_mode = 1;
					break;
				}
			}
		}
		DELAY (100000);
	} while (--retry);

	if (retry == 0)
		goto bad;
	/*
	 * Now lets find out what type of disk this is.
	 */

	/* Do a SCSI INQUIRY Command */
	if (scsi_rd_cmd (ic, unit, 0, CMD_INQUIRY, &inq, SCSI_INQLEN, SCSI_INQLEN, 0) > 0) {
		/* Find out which disk this is */
		index = scsi_check_model(unit, &inq);
	} else {
		printf ("sc%d: INQUIRY Command failed using scsi200 as the default\n",unit);
		index = SCSI200_INDEX;
	}

	/* See if we should check LUN 1 and then check it */
	if (index == SCSI400_INDEX) do {
		/* Check if LUN 1 is ok */
		scsicmd(scsiaddr, unit, 1, CMD_TUR, 0, 0, 0, 0, 0, 0, 0);

		if (result = scsi_check_status(scsiaddr, unit, &status)) {
			break;
		} else {
			if (status & SCSI_ST_CCS_VALID) {
				if ((result = scsi_get_sense_data (ic, unit, 1))
				     < 0) {
					DELAY (1000000);
				} else if (result == SCSI_SENSE_NR) {
					degraded_mode = 1;
					break;
				}
			}
		}
		DELAY (100000);
	} while (--retry);

	if ((retry == 0) || degraded_mode)
		goto bad;

	/* Save the index for this type of drive */
	iod->iod_type = index;
	st = &scsist[index];

	/* Set up the partition table for this disk */
	bzero(&(cs->sizes), sizeof (struct partab));	/* zero paritions */

	/* Get the minidisk off disk and put into partition tables */
	if (!scsigetpart(scsiaddr, ic, unit, st, &(cs->sizes))) {
		/* No minidisk info so use default */
		bcopy((char *)st->off, (char *)&(cs->sizes),
			sizeof(struct partab));
	}

	/* Always want at least the c part'n */
	bcopy(&(st->off->part[2]), &(cs->sizes.part[2]),
		sizeof (struct part_entry));

	/* Set the mode sense (bad block reassignment) for the disk */
	scsi_set_mode_sense(ic, unit, 0);
	if (iod->iod_type == SCSI400_INDEX)
		scsi_set_mode_sense(ic, unit, 1);


	/* Start the scsi watch dog */
        if (scsiwstart == 0) {
                timeout(scsiwatch, (caddr_t)0, hz);
                ++scsiwstart;
        }

	if (iod->iod_dk >= 0) {
		static float mspw = .00116;

		dk_mspw[iod->iod_dk] = mspw;
	}
#ifdef FINDITRASH
	scsi_initfs(ic,unit,st);
#endif FINDITRASH
        return (1);
bad:
	printf ("scsiattach could not find sc%d\n", unit);
	return (0);
}

#ifdef FINDITRASH
/*
 * load the file system parameters into each partition of unit
 */
scsi_initfs(ic, unit, st)
	struct iocc_ctlr *ic;
	int unit;
	struct	 scsist *st;
{
	register struct scsi_softc *sc = &scsi_softc[unit];
	int	i;
	struct fs fs;
	struct fs *fsp = &fs;

	for (i=0; i < 8; i++) {
		if ( sc->sizes.part[i].nblocks == 0) {
			printf("sc%d%c: no such partition\n",unit,i+'a');
			scfs[unit*8 + i].inode_start = 1;
			scfs[unit*8 + i].inode_end = 0;
			return;
		}
		if ( i == 2 ) {
			printf("sc%dc: full disk\n",unit,i+'a');
			scfs[unit*8 + i].inode_start = 1;
			scfs[unit*8 + i].inode_end = 0;
			return;
		}
		if (scsird(ic, unit, SBLOCK+(sc->sizes.part[i].cyloff*st->nspc), SBSIZE, fsp) < 0) {
			printf("sc%d%c: fsread error\n",unit,i+'a');
			scfs[unit*8 + i].inode_start = 1;
			scfs[unit*8 + i].inode_end = 0;
			return;
		}
		if (fs.fs_magic != FS_MAGIC) {
			printf("sc%d%c: no filesystem\n",unit,i+'a');
			scfs[unit*8 + i].inode_start = 1;
			scfs[unit*8 + i].inode_end = 0;
			return;
		}
		scfs[unit*8 + i].inode_start = fsbtodb(fsp,itod(fsp,2));
		scfs[unit*8 + i].inode_end = fsbtodb(fsp,itod(fsp,2)) + 
				((unsigned)fs.fs_ncg*fs.fs_ipg*sizeof(struct dinode)+511)/512;
		scfs[unit*8 + i].fs_bsize = fs.fs_bsize;
	}
}
#endif FINDITRASH


scsi_set_mode_sense(ic, unit, lun)
register struct iocc_ctlr *ic;
register int unit;
int lun;
{
	struct	 mode_sense ms;
	register int nblocks, blen, i;


	/* Find out what the default mode sense for LUN */
	scsi_mode_sense_cmd (ic, unit, lun, &ms);

	nblocks = blen = 0;
	for (i = 0; i < 3; i++) {
		nblocks |= ms.bd.nblocks[i]<<(16-i*8);
		blen |= ms.bd.blen[i]<<(16-i*8);
	}

SCSIDEBUG (SHOW_INIT, {
	printf ("MODE SENSE INFORMATION\n");
	scsi_dumpbuf (&ms, sizeof (struct mode_sense));

	printf ("sdl (%d) bdl (%d) nblocks (%d) blen (%d)\n", ms.msh.sdl,
		ms.msh.bdl, nblocks, blen);
	printf ("drl (%d) drf (0x%x) int_factor (%d)\n", ms.vup.drl, ms.vup.drf,
		ms.vup.int_factor);
	}
);

	/* Set up the error reporting and bad block forwarding */
	ms.msh.sdl = 0;
	ms.msh.bdl = 8;
#ifdef	SCSI_SOFT_ERROR_REPORT
	ms.vup.drf = SCSI_ARRE | SCSI_PER;
#else
	ms.vup.drf = SCSI_ARRE;
#endif
	ms.vup.drl = 0xFF;
	ms.vup.int_factor = 0;
	if (nblocks != SCSI_NBLOCKS_FULL) {
		nblocks = SCSI_NBLOCKS_FULL;
		blen = SCSI_BLEN_FULL;
		for (i = 0; i < 3; i++) {
			ms.bd.nblocks[i] = (nblocks >> (16-i*8)) & 0x0000FF;
			ms.bd.blen[i] = (blen >> (16-i*8)) & 0x0000FF;
		}
	}

	/* Give the drive the new settings */
	scsi_mode_select_cmd (ic, unit, lun, &ms);

	/* See if it changed */
	scsi_mode_sense_cmd (ic, unit, lun, &ms);

SCSIDEBUG (SHOW_INIT, {
	nblocks = blen = 0;
	for (i = 0; i < 3; i++) {
		nblocks |= ms.bd.nblocks[i]<<(16-i*8);
		blen |= ms.bd.blen[i]<<(16-i*8);
	}
	printf ("MODE SENSE INFORMATION\n");
	scsi_dumpbuf (&ms, sizeof (struct mode_sense));

	printf ("sdl (%d) bdl (%d) nblocks (%d) blen (%d)\n", ms.msh.sdl,
		ms.msh.bdl, nblocks, blen);
	printf ("drl (%d) drf (0x%x) int_factor (%d)\n", ms.vup.drl, ms.vup.drf,
		ms.vup.int_factor);
	}
);
}

scsi_check_model(unit, inqp)
register int unit;
register scsi_inqinfo *inqp;
{
	register int i;
	register struct scsi_model *sm;

	for (i = 0; i < NSCSIMODEL; i++) {
		sm = &scsi_model[i];
		if (scsi_bufcmp(inqp->inq_model, sm->model, 4)) {
			printf("sc%d: drive type (%s): %s\n",
				unit, sm->model, sm->desc);
			inqp->inq_ramuid[0] = 0;
			printf("Vid Pid MODEL RLID ROSPL RAMPL (%s)\n",
				inqp->inq_vid);
			return (scsi_model[i].index);
		}
	}
	printf("sc%d: drive type (%s): Unknown using scsi200\n",
		unit, inqp->inq_model);
	inqp->inq_ramuid[0] = 0;
	printf("Vid Pid MODEL RLID ROSPL RAMPL (%s)\n", inqp->inq_vid);
	return (0);
	
}

scsi_bufcmp(s1, s2, n)
register u_char *s1, *s2;
register int n;
{
	while (n--)
		if (*s1++ != *s2++)
			return (0);
	return (1);
}

/* Clear softc structures per controller and device */
scsisetup()
{
        register int i, j;
	register struct scsic_softc *csc;
	register struct scsi_softc *cs;

        if (scsiutabp[0] == 0) {
                for (i = 0; i < NSC; ++i) {
                        scsiutabp[i] = &scsiutab[i];
                        rscsibufp[i] = &rscsibuf[i];
			cs = &scsi_softc[i];
			cs->cmdtip_head = 0;
			cs->cmdtip_tail = 0;
			cs->scsi_flag = 0;
                }
                for (i = 0; i < NSCC; ++i) {
			csc = &scsic_softc[i];
			for (j = 0; j < SCSI_NTAGS; ++j)
				csc->scsi_tag[j] = 0;
			for (j = 0; j < SCSI_NCMDBUFS; ++j)
				csc->scsi_buf[j] = 0;
			csc->dmatip_head = 0;
			csc->dmatip_tail = 0;
			csc->scsi_curtag = 0;
			csc->scsi_nexttag = 0;
			csc->scsi_flag = 0;
		}
	}
}


/*
 *
 * Wait for scsi status to become valid and match passed 
 * mask and compare values.
 *
 */
scsiwait(mask, compare, scsiaddr)
        unsigned char mask;
        unsigned char compare;
        register struct scsidevice *scsiaddr;
{
        register u_short status;
        register unsigned int count = MAX_WAIT_COUNT;

        SCSIDEBUG(SHOW_WAIT,
		printf("scsiwait(%x,%x,%x) ", mask, compare, scsiaddr));

        for (;--count != 0;) {
                status = scsiaddr->scsi_status;
                if ((status & mask) == compare)
                        break;
        }

        if (count == 0)
                printf("scsi: adapter @ %x TIMED OUT (%x & %x != %x)\n",
                        scsiaddr, status, mask, compare);

        SCSIDEBUG(SHOW_WAIT, printf(" complete (%x)\n", status));
	return (!count);
}

scsi_cmd_setup(ic, unit, tip)
        register struct iocc_ctlr *ic;
	register int unit;
	register struct scsi_tag_info *tip;
{
	register struct scsi_softc *cs = &scsi_softc[unit];
	register struct scsi_tag_info *tagh = cs->cmdtip_head;
	register struct scsi_tag_info *tagt = cs->cmdtip_tail;

	/* queue request */
	tip->next = NULL;
	if (tagh == NULL)
		cs->cmdtip_head = tip;
	else
		tagt->next = tip;
	cs->cmdtip_tail = tip;

	/* if unit not busy  process */
	if (!(cs->scsi_flag & SCSI_CMDBUSY)) {
		cs->scsi_flag |= SCSI_CMDBUSY;
		scsi_cmdstart(ic, unit);
	}
	return (0);
}

scsi_cmdstart (ic, unit)
        struct iocc_ctlr *ic;
	register int unit;
{
	register struct scsi_softc *cs = &scsi_softc[unit];
        register struct scsidevice *scsiaddr = (struct scsidevice *)ic->ic_addr;
	register struct scsi_tag_info *tip;
	register struct buf *bp;
	register u_int dir;
	int	 testunit;
	u_short	 blkcnt;
	u_char	 opcode;

	/* Grap the tip off the head of the queue */
	if ((tip = cs->cmdtip_head) == NULL)
		return(0);

	/* Mark that this is in the SCSI CMD PHASE of xfer */
	tip->flag |= SCSI_CMDPHASE;

	/* Kick off the SCSI command */
	bp = tip->bp;

	/* Just in case!!!!!! */
	if (unit != (testunit = DRIVE(bp->b_dev))) {
		printf ("scsi_cmdstart: units don't match (%d) (%d)\n",
			unit, testunit);
		unit = testunit;
	}

	if (tip->blkcnt)
		blkcnt = tip->blkcnt;
	else
		blkcnt = (bp->b_bcount + (SCSI_BSIZE - 1)) >> SCSI_BSHIFT;

	/* Check for Boundary Condition between luns */
	if ((bp->b_logblk < SCSI9332_BPLUN) &&
	    (bp->b_logblk + blkcnt) > SCSI9332_BPLUN) {
		tip->flag |= SCSI_BOUNDARY;
	}

	if (blkcnt == 256)
		blkcnt = 0;

        if (bp->b_flags & B_READ) {
		dir = SCSI_IN;
		if (blkcnt < 256)
			opcode = CMD_READ;
		else
			opcode = CMD_REXT;
		
	} else {
		dir = SCSI_OUT;
		if (blkcnt < 256)
			opcode = CMD_WRITE;
		else
			opcode = CMD_WEXT;
	}
	scsicmd(scsiaddr, unit, 0, opcode, dir, bp->b_logblk, blkcnt,
		tip->cur_bufnum, 0, tip->granularity, tip->tag);

}

scsi_cmd_done (ic, unit)
        register struct iocc_ctlr *ic;
	register int unit;
{
	register struct scsi_softc *cs = &scsi_softc[unit];

	cs->scsi_flag &= ~SCSI_CMDBUSY;

	/* Check if we really should be here */
	if (cs->cmdtip_head == NULL) {
		printf ("In scsi_cmd_done with null head (NOT GOOD)\n");
		return;
	}

	/* Take CMD request off of queue and Mark as done */
	cs->cmdtip_head->flag &= ~SCSI_CMDPHASE;
	cs->cmdtip_head = cs->cmdtip_head->next;

	/* Check if any more work needs to be done */
	if (cs->cmdtip_head != NULL) {
		cs->scsi_flag |= SCSI_CMDBUSY;
		scsi_cmdstart(ic, unit);
	}
}
/* This routine sets up the structures for a paramater list to be
 * passed to the SCSI adapter.
 */

/* Arguments double as format paramaters */
#define LEVEL		lba
#define INTERLEAVE	gran

scsicmd(scsiaddr, unit, lun, opcode, direction, lba, tlen, bufnum, ctrl, gran, tag)
	register struct	scsidevice *scsiaddr;
	register u_int	unit;
	int lun;
	u_char		opcode;
	u_int		direction;
	u_int		lba;
	u_short		tlen;
	u_char		bufnum;
	u_char		ctrl;
	u_char		gran;
	u_char		tag;
{
	scsi_cmdstack cst;
	scsi_opcode oc;
	register u_char *lbap = (u_char *)&lba;
	register u_char *tlenp = (u_char *)&tlen;
	register int i;
	int size;

        SCSIDEBUG(SHOW_GO,
		printf("scsicmd(%d,%d) opcode (%x) dir (%d) lba (%x) tlen (%d) bufnum (%d) ctrl (%x) tag (%d)\n", unit, lun, opcode, direction, lba, tlen, bufnum, ctrl, tag)
		);

	/* Check if this is a data transfer */
	if (tlen > 0) {
		cst.dataxfr = 1;
		cst.dir = direction;
		cst.bufnum = bufnum;
		cst.gran = gran;
		/* Check to see if lun must be adjusted */
		if (lun == 0) {
			if (lba >= SCSI9332_BPLUN) {
				lun = 1;
				lba -= SCSI9332_BPLUN;
			} else {
				register int end = lba + tlen;
				if (end > SCSI9332_BPLUN) {
					tlen = SCSI9332_BPLUN - lba;
				}
			}
		}
	} else {
		cst.dataxfr = 0;
		cst.dir = 0;
		cst.bufnum = 0;
		cst.gran = 0;
	}
	cst.rsvd1 = 0;
	cst.rsvd2 = 0;
	cst.stopxfr = 0;
	cst.id = (unit >= 7 ? unit - 7 : unit);

	oc.charcode = opcode;

	if (opcode == CMD_FU) {
		cst.gran = 0;
		cst.cdb.frmt.opcode = opcode;
		cst.cdb.frmt.lun = lun;
		cst.cdb.frmt.level = LEVEL;
		cst.cdb.frmt.rsvd1 = 0;
		cst.cdb.frmt.rsvd2 = 0;
		cst.cdb.frmt.interleave = INTERLEAVE;
		cst.cdb.frmt.ctrl = ctrl;
		size = SCSI_GROUP0_SIZE;
	} else switch (oc.opcode.group) {
		case SCSI_CMD_GROUP0:
			cst.cdb.cdb6.opcode = opcode;
			cst.cdb.cdb6.lun = lun;
			cst.cdb.cdb6.lba = lba;
			cst.cdb.cdb6.tlen = tlen;
			cst.cdb.cdb6.ctrl = ctrl;
			size = SCSI_GROUP0_SIZE;
			break;
		case SCSI_CMD_GROUP1:
			cst.cdb.cdb10.opcode = opcode;
			cst.cdb.cdb10.lun = lun;
			for (i = 0; i < 4; i++)
				cst.cdb.cdb10.lba[i] = lbap[i];
			cst.cdb.cdb10.tlen[0] = tlenp[0];
			cst.cdb.cdb10.tlen[1] = tlenp[1];
			cst.cdb.cdb10.ctrl = ctrl;
			cst.cdb.cdb10.reladr = 0;
			cst.cdb.cdb10.rsvd1 = 0;
			cst.cdb.cdb10.rsvd2 = 0;
			size = SCSI_GROUP1_SIZE;
			break;
		case SCSI_CMD_GROUP5:
			cst.cdb.cdb12.opcode = opcode;
			cst.cdb.cdb12.lun = lun;
			for (i = 0; i < 4; i++)
				cst.cdb.cdb12.lba[i] = lbap[i];
			cst.cdb.cdb12.tlen[0] = tlenp[0];
			cst.cdb.cdb12.tlen[1] = tlenp[1];
			cst.cdb.cdb12.ctrl = ctrl;
			cst.cdb.cdb12.reladr = 0;
			size = SCSI_GROUP2_5_SIZE;
			break;
		default:
			printf ("scsicmd: illegal command group (%x)\n",
				opcode);
			return (BAD);
			/* break; */
	}
	return (scsi_sendcmd (scsiaddr, SCSI_CMD_SEND, &cst, size+4, size, tag));
}

scsi_sendcmd (scsiaddr, cmd, sendbuf, len, slen, tag)
register struct scsidevice *scsiaddr;
register u_short cmd;
register u_char *sendbuf;
register int len;		/* Actual number of paramater bytes */
register int slen;		/* Number of bytes in SCSI SEND Command */
u_char tag;
{
	u_short scsi_cmd;
	u_short param;
	register int i;

	SCSIDEBUG(SHOW_GO,
		printf("scsi_sendcmd ENTER buf(%x) len (%d) tag (%d)\n",
			sendbuf, len, tag)
		);

	for (i = 0; i < len; i +=2) {
		/* Wait for scsi paramater stack to become not busy */
		if (scsiwait (SCSI_ST_BUSY, 0, scsiaddr))
			break;

		param = sendbuf[i+1] | (sendbuf[i] << 8);

		SCSIDEBUG(SHOW_GO,
			printf(" buf [%d,%d] = 0x(%x)\n", i, i+1, param));

		/* Send buf in order with ONLY 16 bit operations
		 * !!!!!!(NOT PUSH DOWN STACK)!!!!!!
		 */
		scsiaddr->scsi_parm = param;
	}
	if (i < len)
		return (BAD);

	/* Wait for scsi for not busy to give command */
	if (scsiwait (SCSI_ST_BUSY, 0, scsiaddr))
		return (BAD);

	/* Give the adapter a scsi command */
	scsi_cmd = cmd | ((slen << 8) & SCSI_CMD_NBPM) | tag;
        SCSIDEBUG(SHOW_GO, printf("scsi_sendcmd (%x)\n", scsi_cmd));

	scsiaddr->scsi_cmd = scsi_cmd;

	return (OK);
}

/* Reset The SCSI controler */
scsireset(iod, scsiaddr)
        register struct iocc_device *iod;
        register struct scsidevice *scsiaddr;
{
        register int timeout, result;
	u_short status;

	/* Wait for scsi to become not busy */
	timeout = MAX_WAIT_COUNT;
	while (scsiaddr->scsi_status & SCSI_ST_BUSY) {
		/* Clear status register */
		scsi_dummy = *(u_char *) &scsiaddr->scsi_status;

		if (--timeout)
			break;
	}

	/* Perform BAT test */
	scsiaddr->scsi_cmd = SCSI_CMD_RBAT;
        
	DELAY(1000000);

	/* Wait for Valid */
        if (scsiwaitvalid(0, 0, scsiaddr))
		goto bad;

        if ((status = scsiaddr->scsi_status) & SCSI_ST_CCSM != SCSI_ST_AR) {

                result = 0;

                SCSIDEBUG(
			SHOW_INIT,
			printf("scsireset: addr %x diagnose=%x ERROR\n",
                        	scsiaddr, status)
		);
        } else  {
                result = 1;		/* Good */
        }

	goto leave;
bad:
	result = 0;
leave:
	/* Clear the interrupt */
	scsi_dummy = *(u_char *) &scsiaddr->scsi_status;
        return(result);
}

scsiopen(dev)
        dev_t dev;
{
        register int drive = DRIVE(dev);
        register int part = PART(dev);
	register int size = SCSIPART(drive, part)->nblocks;
        register struct iocc_device *iod;
	register struct scsidevice *scsiaddr;

        SCSIDEBUG(SHOW_ORDWR,
		printf("SCSI: JUST ENTERED SCSIOPEN(%x)!!\n", dev));

        if (drive >= NSC || part < 0 || part > 7)
                return (ENXIO);

        if ((iod = scsidinfo[drive]) == 0 || iod->iod_alive == 0 || size == 0)
                return (ENXIO);

	/* Let interrupts go now */
	scsiaddr = (struct scsidevice *) iod->iod_mi->ic_addr;
	scsiaddr->scsi_config |= SCSI_CF_IEN;

        return (0);
}

/*
 *      Raw read routine:
 *
 *        This routine calls physio which computes and validates
 *        a physical address from the current logical address.
 *
 *      Arguments:
 *        Full device number
 *
 *        Functions:
 *          Call physio which does the actual raw (physical) I/O
 *          The arguments to physio are:
 *          	pointer to the strategy routine
 *          	buffer for raw I/O
 *          	device
 *          	read/write flag
 */
scsiread(dev, uio)
        register dev_t dev;
        register struct uio *uio;
{
        register int unit = DRIVE(dev);

        SCSIDEBUG(SHOW_ORDWR, printf("SCSI: JUST ENTERED SCSIREAD()!!\n"));

        if (unit >= NSC)
                return (ENXIO);

        return (physio(scsistrategy, rscsibufp[unit], dev, B_READ, scsiminphys, uio));
}

/*
 *      Raw write routine:
 *
 *      arguments (to scsiwrite):
 *        Full device number
 *
 *      Functions:
 *        Call physio which does the actual raw (physical) I/O
 */
scsiwrite(dev, uio)
        dev_t dev;
        register struct uio *uio;
{
        register int unit = DRIVE(dev);

        SCSIDEBUG(SHOW_ORDWR, printf("SCSI: JUST ENTERED SCSIWRITE()!!\n"));

        if (unit >= NSC)
                return (ENXIO);

        return (physio(scsistrategy, rscsibufp[unit], dev, B_WRITE, scsiminphys, uio));
}



/*
 *      Strategy Routine:
 *
 *      Arguments:
 *        Pointer to buffer structure
 *
 *      Function:
 *        Check validity of request
 *        Queue the request
 *        Start up the device if idle
 *
 *      Note:
 *        1.) Block numbers (in b_blkno) are RELATIVE to the
 *	      start of the partition.
 *        2.) Block numbers (in b_blk) are ABSOLUTE.
 */
scsistrategy(bp)
        register struct buf *bp;
{
	register int scblkoff, nblocks;
	register int drive, part;
        register struct scsist *st;
        register struct buf *dp;
        register struct iocc_device *iod;
        long size, bn;
        int s;

        size = (bp->b_bcount + (SCSI_BSIZE - 1)) >> SCSI_BSHIFT;
	drive = DRIVE(bp->b_dev);
	part = PART(bp->b_dev);
        bn = bp->b_blkno;
        iod = scsidinfo[drive];
        st = &scsist[iod->iod_type];
	nblocks = SCSIPART(drive, part)->nblocks;
	scblkoff = (SCSIPART(drive, part)->cyloff) * st->nspc;

        if (drive < NSC && part < 8 && part >= 0 && bn >= 0 && bn < nblocks &&
	    (((bn + size) <= nblocks) || (bp->b_flags & B_READ)))
	{
                if (bn + size > nblocks)
                        bp->b_bcount = (nblocks - bn) * DEV_BSIZE;
        } else {

                SCSIDEBUG(
			SHOW_ENQ,
			{
			  printf("SCSI: SCSISTRATEGY -- BAD %s REQUEST",
                            (bp->b_flags & B_READ) ? "READ" : "WRITE");
                          printf("\nblkno=%d bcount=%d baddr=0x%x",
                            bp->b_blkno, bp->b_bcount, bp->b_un.b_addr);
                          printf(" bdev=0x%x\n", bp->b_dev);
                          printf("drive=%d part=%d ", drive, part);
                          printf(" nblocks (blocks)=%d", nblocks);
                          printf(" pblk=%d\n", scblkoff);
			}
                );

                bp->b_flags |= B_ERROR;
                iodone(bp);
                return;
        }
#ifdef FINDITRASH
	/*
	 * We need to look for inode trashing. This happens only on writes, and only if
	 * the writes are to the inode.
	 */
	if ((bp->b_flags & B_READ) == 0) {
		int device = minor(bp->b_dev);
		if ((bn >= scfs[device].inode_start) && (bn <= scfs[device].inode_end)) {
			/*
			 * well we have found an inode write, make sure that it is valid,
			 * check the first 2 inodes...
			 */
			struct dinode *dp = bp->b_un.b_dino;
			int	count;

			for(count = 0; count < 6; count++, dp++); {
			    if ((dp->di_mode & IFMT == 0)  && (dp->di_mode)) {
				printf("sc%d%c: Attempt to trash inode @ block %d with buffer=0x%x, len=%d\n",
					drive,part+'a',bn,bp->b_un.b_addr,bp->b_bcount);
				panic("scsi inode trash");
			    }
			    if (scfs[device].fs_bsize != bp->b_bcount) {
				printf("sc%d%c: inode write with funny size@ block %d with buffer=0x%x, len=%d\n",
					drive,part+'a',bn,bp->b_un.b_addr,bp->b_bcount);
			    }
			}
		}
	}
#endif FINDITRASH
	 

	/* Figure out starting disk logical block number and sort on it */
        bp->b_logblk = (bn * (DEV_BSIZE / SCSI_BSIZE)) + scblkoff;

        s = spl_disk();                   /* ibm032 level 3 interrupt */
        dp = scsiutabp[drive];
        disksort(dp, bp);

        SCSIDEBUG(
		SHOW_ENQ,
		{
		  printf("SCSI: SCSISTRATEGY -- ENQ %s REQUEST",
                    (bp->b_flags & B_READ) ? "READ" : "WRITE");
                  printf(" blkno=%d bcount=%d baddr= 0x%x\n",
                    bp->b_blkno, bp->b_bcount, bp->b_un.b_addr);
                  printf("\tb_logblk=%d", bp->b_logblk);
                  printf(" bdev=0x%x", bp->b_dev);
                  printf(" drive=%d part=%d", drive, part);
                  printf(" nblocks= %d", nblocks);
                  printf(" scblkoff=%d\n", scblkoff);
		}
        );

	/*
         * Add this device to the controller queue.
	 * If the controller has room then start the request.
         */

	(void) scsiustart(iod);
	bp = &iod->iod_mi->ic_tab;
	if (bp->b_actf && bp->b_active == 0)
		(void) scsistart(iod->iod_mi);
        else
                SCSIDEBUG(SHOW_ENQ, printf("adapter active\n"));

        splx(s);
}

/*
 *      Device Startup Routine:
 *
 *      Arguments:
 *        Pointer to device structure
 *
 *      Function:
 *        Queue the device to the controller if it is not there allready
 */
scsiustart(iod)
        register struct iocc_device *iod;
{
        register struct buf *dp;
        register struct iocc_ctlr *ic;

        SCSIDEBUG(SHOW_DEQ, printf("scsiustart(%x)\n", iod));

        if (iod == 0)
                return (0);
        ic = iod->iod_mi;
        dp = scsiutabp[iod->iod_unit];

        if (dp->b_actf == NULL) {

                SCSIDEBUG(SHOW_DEQ, printf("scsiustart: actf == NULL\n"));

                goto out;
        }

        /*
         * If Drive is ready to go. Put the device on the ready queue for
	 * the controller (unless it is already there).
         */

        if (dp->b_active != 1) {
                dp->b_forw = NULL;
                if (ic->ic_tab.b_actf == NULL)
                        ic->ic_tab.b_actf = dp;
                else
                        ic->ic_tab.b_actl->b_forw = dp;
                ic->ic_tab.b_actl = dp;
                dp->b_active = 1;
        }

out:
        return (0);
}

/*
 *      Controller Startup Routine:
 *
 *      Arguments:
 *        Pointer to controller structure
 *
 *      Function:
 *        Compute device-dependent parameters
 *        Start up controller
 *        Indicate request to i/o monitor routines
 */
scsistart(ic)
        register struct iocc_ctlr *ic;
{
	struct scsi_tag_info *tip;
        register struct buf *bp, *dp = 0;
        register struct scsidevice *scsiaddr = (struct scsidevice *)ic->ic_addr;
	register struct scsic_softc *csc = &scsic_softc[ic->ic_ctlr];
        register int unit;
        struct iocc_device *iod;

        SCSIDEBUG(SHOW_DEQ, printf("scsistart(%x)\n", ic));

loop:
        /*
         * Pull a device off the controller queue.
         * If that device has work to do, do it;
         * otherwise check the next device in the
	 * queue. Note that scsiint takes care of
	 * rotating the controller between devices
	 * that need servicing.
         */

        if ((dp = ic->ic_tab.b_actf) == NULL)
                return (0);

        if ((bp = dp->b_actf) == NULL) {
                ic->ic_tab.b_actf = dp->b_forw;
		dp->b_active = 0;
                goto loop;
        }

        unit = DRIVE(bp->b_dev);

        SCSIDEBUG(SHOW_LED, DISPLAY(minor(bp->b_dev)));

        SCSIDEBUG(
		SHOW_DEQ,
		{
                  printf("SCSI: SCSISTART -- DEQ %s REQUEST",
                    (bp->b_flags & B_READ) ? "READ" : "WRITE");
                  printf("\nblkno=%d bcount=%d baddr=0x%x",
                    bp->b_blkno, bp->b_bcount, bp->b_un.b_addr);
                  printf(" bdev=0x%x", bp->b_dev);
                  printf(" b_logblk=%d\n", bp->b_logblk);
        	}
        );

	/* Get a free tag number for command */
	if ((tip = scsi_gettag (csc)) == 0) {
		ic->ic_tab.b_active = 1;     /* Mark controller active/full */
		return (RETRY_LATER);
	}

	/* Save Original bp information and pointer to bp in tag info */
	tip->bp = bp;
        tip->b_addr = bp->b_un.b_addr;
        tip->b_bcount = bp->b_bcount;
	tip->blkcnt = (bp->b_bcount + (SCSI_BSIZE - 1)) >> SCSI_BSHIFT;

	if ((tip->st_bufnum = scsi_getbufnum (csc, tip)) == SCSI_NOBUFS) {
		tip->flag = 0;
		scsi_freetag (ic, tip);
		ic->ic_tab.b_active = 1;     /* Mark controller active/full */
		return (RETRY_LATER);
	}
	tip->cur_bufnum = tip->st_bufnum;

	/* We have now assigned a bp to a tag so take it off queue */
	dp->b_actf = bp->av_forw;


        SCSIDEBUG(
		SHOW_XFER,
		printf("SCSISTART: starting %s\n",
            	(bp->b_flags & B_READ) ? "read" : "write")
	);

	iod = scsidinfo[unit];
        if (iod->iod_dk >= 0) {
                dk_busy |= 1<<iod->iod_dk;
                dk_seek[iod->iod_dk]++;
                dk_xfer[iod->iod_dk]++;
                dk_wds[iod->iod_dk] += bp->b_bcount >> 6;
        }

        if (bp->b_flags & B_READ) {
                SCSIDEBUG(SHOW_ORDWR | SHOW_REGS,
                    printf("SCSISTART: STARTING READ CMD...\n"));
                SCSIDEBUG(SHOW_REGS, scsistatus(bp, scsiaddr));

		scsi_cmd_setup(ic, unit, tip);

        } else {
                SCSIDEBUG(SHOW_ORDWR | SHOW_REGS,
                    printf("SCSISTART: STARTING WRITE CMD...\n"));
                SCSIDEBUG(SHOW_REGS, scsistatus(bp, scsiaddr));

		scsi_dma_setup (ic, tip);
        }
	
	goto loop;
}

scsi_dma_setup(ic, tip)
        register struct iocc_ctlr *ic;
	register struct scsi_tag_info *tip;
{
	register struct scsic_softc *csc = &scsic_softc[ic->ic_ctlr];
	register struct scsi_tag_info *tagh = csc->dmatip_head;
	register struct scsi_tag_info *tagt = csc->dmatip_tail;

	/* queue request */
	tip->next = NULL;
	if (tagh == NULL)
		csc->dmatip_head = tip;
	else
		tagt->next = tip;
	csc->dmatip_tail = tip;

	/* if channel  not busy  process */
	if (!(csc->scsi_flag & SCSI_DMABUSY)) {
		csc->scsi_flag |= SCSI_DMABUSY;
		scsi_dmastart(ic);
	}
	return (0);
}

scsi_dma_done(ic)
        register struct iocc_ctlr *ic;
{
	register struct scsic_softc *csc = &scsic_softc[ic->ic_ctlr];
	struct scsidevice *scsiaddr = (struct scsidevice *)ic->ic_addr;

	csc->scsi_flag &= ~SCSI_DMABUSY;

	/* Take DMA request off of queue and Mark as done */
	if (csc->dmatip_head == NULL) {
		printf ("In scsi_dma_done with null head (NOT GOOD)\n");
		return (0);
	}

	dma_done(ic->ic_dmachannel);
	/* have scsi release the channel */
	scsiaddr->scsi_config = 
	  ((DMA_CHAN4 << SCSI_CF_DMASFT) & SCSI_CF_DMASM) |
	  (scsiaddr->scsi_config & ~SCSI_CF_DMASM);
	csc->dmatip_head->flag &= ~SCSI_DMAPHASE;
	csc->dmatip_head = csc->dmatip_head->next;

	/* Check if any more work needs to be done */
	if (csc->dmatip_head != NULL) {
		csc->scsi_flag |= SCSI_DMABUSY;
		scsi_dmastart(ic);
	}
}


/* Set up TCW's for 1'st Party DMA for SCSI controller */
scsi_dmastart (ic)
        struct iocc_ctlr *ic;
{
	register struct scsi_tag_info *tip;
        register struct buf *bp;
	int len;
	struct scsic_softc *csc = &scsic_softc[ic->ic_ctlr];

	ic->ic_dmachannel = dma_select_chan(scsi_chan);

	/* Grap the tip off the head of the queue */
	if ((tip = csc->dmatip_head) == NULL)
		return(0);

	/* Mark that this is in the DMA PHASE of xfer */
	tip->flag |= SCSI_DMAPHASE;

	/* Get the bp */
	bp = tip->bp;
	len = bp->b_bcount;

	/*
	 * Check if this is more than 1 block transfer and is not
	 * a multiple of SCSI_BSIZE (Partial block transfer over 512).
	 */
	 if ((len > 512) && (len % SCSI_BSIZE)) {
		/*
		 * Partial block xfer over 512, we must split up into 2
		 * DMAs since we cannot tell the adapter to transfer
		 * just this amount.
		 */
		tip->flag |= SCSI_MULTIDMA;
	}

	SCSIDEBUG(SHOW_DMA,
		printf("SCSI config = %x\n",
		 ((struct scsidevice *)ic->ic_addr)->scsi_config);
	);
	ic->ic_dmabuf = bp;
	SCSIDEBUG(SHOW_DMA,
		printf("SCSI config = %x\n",
		 ((struct scsidevice *)ic->ic_addr)->scsi_config);
	);
	dma_setup(ic);
}

/*
 *	Routine to actually start a dma operation:
 *
 *	Funtion:
 */
scsidmago (ic, len, ioaddr, bp)
	struct iocc_ctlr *ic;
	unsigned len;
	unsigned ioaddr;
	register struct buf *bp;
{
	register struct scsic_softc *csc = &scsic_softc[ic->ic_ctlr];
	register int retval;
	struct scsidevice *scsiaddr = (struct scsidevice *)ic->ic_addr;
	register char chan = ic->ic_dmachannel;
	scsi_rwdmastack dmast;

	struct scsi_tag_info *tip;

	tip = csc->dmatip_head;

	/* Configure the DMA adapter hardware */
	scsiaddr->scsi_config |=  SCSI_CF_DMAEN;
	scsiaddr->scsi_dma_mode = SCSI_BURST_LEN4;
	((struct scsidevice *)ic->ic_addr)->scsi_config = 
	  ((chan << SCSI_CF_DMASFT) & SCSI_CF_DMASM) |
	  (((struct scsidevice *)ic->ic_addr)->scsi_config & ~SCSI_CF_DMASM);


	dma_go(chan);

	/*
	 * Set the DMA direction to the SCSI adapter and
	 */
	dmast.flag = (bp->b_flags&B_READ)? SCSI_DMADIR_RD : SCSI_DMADIR_WR;

	/* Get the assigned buf number */
	dmast.bufnum = tip->cur_bufnum;

	/* Set the number of 512 byte blocks to be transfered */
	if (len < 512) {
		dmast.flag |= SCSI_DMABYTCNT;
		dmast.bcount = (len+1)/2;	/* Adapter wants half */
	} else {
		dmast.flag |= SCSI_DMABLKCNT;
		/*
		 * Round down number of blocks wanted (MULTIDMA will do the
		 * rest latter if partial block needed).
		 */
		dmast.bcount = len >> SCSI_BSHIFT;
		dmast.bcount -= 1;	/* Adapter wants -1 from actual */
	}


	/* Set the DMA address in stack to pass to SCSI */
	dmast.dma_resid = 0;
	dmast.dma_hb = (ioaddr >> SCSI_HI_SHIFT) & SCSI_BYTE_MASK;
	dmast.dma_mb = (ioaddr >> SCSI_MID_SHIFT) & SCSI_BYTE_MASK;
	dmast.dma_lb = (ioaddr >> SCSI_LOW_SHIFT) & SCSI_BYTE_MASK;

	retval = scsi_sendcmd (scsiaddr, SCSI_CMD_RW, (u_char *)&dmast, SCSI_DMAST_SIZE, 0, tip->tag);

	return (retval);
}

/*
 *      Interrupt Routine:
 *
 *	Funtion:
 *        Check completion status
 *        Indicate completion to i/o monitor routines
 *        log errors
 *        start next request
 *
 */

#define NO_TAGS_ACTIVE(csc)	(csc->scsi_curtag == csc->scsi_nexttag)

scsiint(ctlr)
int ctlr;
{
        register struct buf *bp, *dp;
        register struct iocc_ctlr *ic = scsiminfo[ctlr];
	register struct scsic_softc *csc = &scsic_softc[ctlr];
        register struct scsidevice *scsiaddr;
        register u_short status;
	register struct scsi_softc *cs;

	/*
	 * check status info to print out if we fail the sense data
	 */

        struct iocc_device *iod;
	struct scsi_tag_info *tip;
	u_char	tag;
        int unit;

	/* Get the status of this interrupt */
        scsiaddr = (struct scsidevice *)ic->ic_addr;
	status = scsiaddr->scsi_status;

	/* Is this really our interrupt?? */
	if (NO_TAGS_ACTIVE(csc)) {

                SCSIDEBUG( SHOW_STRAY_INTR,
			{
			  printf("SCSI: UNKNOWN INTERRUPT\n");
			  printf("SCSI: NO I/O IN PROGRESS!!\n\n");
			}
		);

                return(1);
        }
	/* If status is not valid then we shouldn't have this interrupt */
	if ((status & SCSI_ST_VALID) == 0) {
                SCSIDEBUG( SHOW_STRAY_INTR,
			printf ("scc%d: (scsiint) nonvalid interrupt (%x)\n",
				ctlr, status)
		);
		return(1);
	}
		
	/* Our status so Clear it now */
	scsi_dummy = *(u_char *) &scsiaddr->scsi_status;

	/* Get tag out of status */
	tag = status & SCSI_ST_CTM;

	/* Check the tag and get the bp information for the tag_info struct */
	if (tag >= SCSI_NTAGS) {
		printf ("scc%d: unexpected condition tag status (0x%b)\n",
			ctlr, status, SCSI_ST_BITS);
		if (status & SCSI_ST_CCS_VALID) {
			/* Don't know which DRIVE so check all (TBD) */
			scsi_get_sense_data(ic, 0, 0);
			scsi_get_sense_data(ic, 1, 0);
		}
		return(0);
	}
	tip = csc->scsi_tag[tag];
	if (tip && tip->bp) {
		bp = tip->bp;
		unit = DRIVE(bp->b_dev);
	} else {
		/* This should never happen!!!! */
		printf ("sc%d: no tip for tag (%d) status 0x%b\n", ctlr,
			tag, status, SCSI_ST_BITS);
		return(0);
	}

	/* Show that we got an interrupt and didn't time out */
	tip->scsi_timer = 0;

	if (tip->flag == 0) {
		printf ("SCSIINT: for tag (%d) with no flag\n",  tip->tag);
#ifdef DEBUG
		scsi_CHECK_routine();
#endif DEBUG
		return(0);
	}

	/* Get the softc structure for this drive */
	cs = &scsi_softc[unit];

	/* Check for a SCSI Peripheral or adapter return status */ 
        if (status & SCSI_ST_CCS_VALID) switch ((status & SCSI_ST_CCSM)>>8) {

	/* Check condition (some error or information from drive) */
	case (SCSI9332_CHECK):

		/* Check if reached maximum Errors reached and clear buf */
		if (++tip->err_cnt > SCSI_MAX_ERR) {
			printf (
		   "sc%d: unrecoverable CHECK condition status (0x%b) (%s)\n",
				unit, status, SCSI_ST_BITS, 
					(bp->b_flags & B_READ)?"READ":"WRITE"
			);
			bp->b_flags |= B_ERROR;
			bp->b_error = EIO;
			scsi_cmd_done (ic, unit);
			break;
		} else {
			/* Bad block handling is automatically done now
			 * so lets check if that was the error
			 */
			scsi_start_sense (ic, unit, tip);
			return(0);
		}
	/* Means that the scsi Command has been completed */
        case (SCSI9332_GOOD):

		/*
		 * A scsi command is done for this lun so flag it as done
		 * and start a new one if there are any.
		 */
		if (tip->flag & SCSI_INSENSECMD) {

			/* Save Current tip and bp information */
			cs->ti = *tip;
			cs->b = *bp;

			/* Set up sense dma request */
			tip->flag = SCSI_INSENSECMD;
			tip->st_bufnum = tip->cur_bufnum = SENSE_BUFNUM(unit);
			tip->bp = &cs->b;		/* use local buffer */
			tip->bp->b_flags = B_READ;
			tip->bp->b_un.b_addr = (caddr_t) &(cs->sense);
			tip->bp->b_bcount = SCSI_SENSESZ;
			tip->blkcnt = 0;

			/* Do dma to get sense information */
			scsi_dma_setup (ic, tip);

			return(0);
		}
scsi_continue:

		if ((tip->flag & SCSI_CMDPHASE) == 0) {
			panic("scsiint: cmd phase flag == 0");
		}
		/* If we have a boundary condition then we are not done yet */
		if (tip->flag & SCSI_BOUNDARY) {
			/*
			 * If this was a boundary condition do the rest of
			 * the transfer now.
			 */
			register int end;
			SCSIDEBUG(SHOW_BOUNDARY,
				printf ("SCSIINT: Boundary xfer blk %d for %d\n", bp->b_logblk, 
					tip->blkcnt)
				);
			/* Adjust values for rest of xfer */
			end = bp->b_logblk + tip->blkcnt;
			tip->cur_bufnum += SCSI9332_BPLUN - bp->b_logblk;
			bp->b_logblk = SCSI9332_BPLUN;
			tip->blkcnt = end - SCSI9332_BPLUN;
			if (tip->blkcnt <= 0) {
				printf("sc%d: errant boundary flag bufnum=%d curbufnum=%d end=%d\n",
					unit,tip->st_bufnum,tip->cur_bufnum,end);
				panic("scsi errant flag");
			}
			tip->flag &= ~SCSI_BOUNDARY;
			scsi_cmdstart(ic, unit);

			return(0);
		} else {
			scsi_cmd_done (ic, unit);
		}

                if (bp->b_flags & B_READ) {

                        SCSIDEBUG(
				SHOW_INTR,
				{
                                  printf("SCSI: DRQ INTERRUPT --> (READ)");
                                  printf(" b_addr=0x%x status=0x%x\n",
				  bp->b_un.b_addr, status);
                                  scsistatus(bp, scsiaddr);
                        	}
			);
			tip->cur_bufnum = tip->st_bufnum;
			scsi_dma_setup (ic, tip);
                } else {

                        SCSIDEBUG(
				SHOW_INTR,
				{
                                  printf("SCSI: DRQ INTERRUPT --> (WRITE)");
                                  printf(" b_addr=0x%x status=0x%x\n",
				  bp->b_un.b_addr, status);
                                  scsistatus(bp, scsiaddr);
                        	}
			);
			break;
                }
		return(0);
		/* break; */
	case (SCSI9332_BUSY):
		printf ("sc%d: BUSY condition status (0x%b)\n", unit, status, SCSI_ST_BITS);
		return(0);
		/* break; */
	/* We will get these when we start using granularity for larg xfers */
	case (SCSI9332_INTER):
		printf ("sc%d: INTERMEDIATE condition status (0x%b)\n", unit, status, SCSI_ST_BITS);
		return(0);
		/* break; */
	case (SCSI9332_RESCFT):
		printf ("sc%d: RESERVE CONFLICT condition status (0x%b)\n", unit, status, SCSI_ST_BITS);
		return(0);
		/* break; */
	default:
		printf ("sc%d: unknown condition status (0x%b)\n", unit, status, SCSI_ST_BITS);
		return(0);
		/* break; */

        } else {
		/* Should check if this really was for DMA */

		/* The adapter sent this return code so check out */
		if (status & SCSI_ST_CCSM) {
			bp->b_flags |= B_ERROR;
			bp->b_error = EIO;
			printf("sc%d: scsiint FAILED: tag (%d) stat 0x(%b)\n",
				unit, tag, status, SCSI_ST_BITS);
			if (tip->flag & SCSI_DMAPHASE) {
				printf("sc%d: scsiint clearing active dma\n",unit);
				scsi_dma_done(ic);
			}
			if (tip->flag & SCSI_CMDPHASE) {
				printf("sc%d: scsiint clearing active cmd\n",unit);
				scsi_cmd_done(ic,unit);
			}
		} else {

			if ((tip->flag & SCSI_DMAPHASE) == 0) {
				panic("scsiint: cmd phase flag == 0");
			}

			/* Check if this was DMA to get sense information */
			if (tip->flag & SCSI_INSENSECMD) {
				register int key;

				/* Kick off next DMA on queue */
				scsi_dma_done (ic);


				/* Analyse the result */
				key = scsi_print_sense (unit,status, bp,
					scsiaddr,&(cs->sense));

				/* Restore tip information */
				*tip = cs->ti;

				/* Sense command over */
				tip->flag &= ~SCSI_INSENSECMD;

				/* If command was ok - end it and start DMA */
				if (key == SCSI_SENSE_RE) {
					goto scsi_continue;
				}
				/* Otherwise try the command again */
				else {
					scsi_cmdstart (ic, unit);
					return(0);
				}
			}

			/* Check if this transfer needs multiple DMA commands. */
			if (tip->flag & SCSI_MULTIDMA) {
				register int todo_len, done_len;

				/* Adjust bp values for rest of DMA transfer */
				todo_len = bp->b_bcount % SCSI_BSIZE;
				done_len = (bp->b_bcount - todo_len);
				bp->b_un.b_addr += (done_len);
				bp->b_bcount = todo_len;
				tip->cur_bufnum += (done_len >> SCSI_BSHIFT);
				tip->flag &= ~SCSI_MULTIDMA;
				dma_done(ic->ic_dmachannel);

				/* Do the rest of this DMA xfer */
				scsi_dmastart (ic);
				return(0);
			} 

			/* Kick off next DMA on queue */
			scsi_dma_done (ic);

			if (bp->b_flags & B_READ) {

                        	SCSIDEBUG(
					SHOW_INTR,
					{
                                  	printf("SCSI: DMA INTERRUPT --> (READ)");
                                  	printf(" b_addr=0x%x status=0x%x\n",
				  	bp->b_un.b_addr, status);
                                  	scsistatus(bp, scsiaddr);
                        		}
				);

                	} else {

                        	SCSIDEBUG(
					SHOW_INTR,
					{
                                  	printf("SCSI: DMA INTERRUPT --> (WRITE)");
                                  	printf(" b_addr=0x%x status=0x%x\n",
				  	bp->b_un.b_addr, status);
                                  	scsistatus(bp, scsiaddr);
                        		}
				);

				/* Done with DMA do the scsi write */
				tip->cur_bufnum = tip->st_bufnum;
				scsi_cmd_setup(ic, unit, tip);
				return(0);
                	}
		}
	}

        /*
         *      Flag current request complete
         */
	ic->ic_tab.b_active = 0;

	/* If there is anything on the queue rotate it */
	if (ic->ic_tab.b_actf) {
		/*
		 * rotate to next drive if there is one
		 * (if not then don't bother)
		 */
		dp = ic->ic_tab.b_actf;
		if (dp->b_forw) {
			ic->ic_tab.b_actf = dp->b_forw;
			dp->b_forw = NULL;
			ic->ic_tab.b_actl->b_forw = dp;
			ic->ic_tab.b_actl = dp;
		}
	}

	/* Flag this device request complete */
        dp = scsiutabp[unit];

	/* Restore ther original bp values */
	bp->b_bcount = tip->b_bcount;
	bp->b_un.b_addr = tip->b_addr;

	if (bp->b_flags & B_ERROR) {
		bp->b_resid = bp->b_bcount;	/* Error! */
	} else {
		bp->b_resid = 0;	/* Everything Transfered */
	}

	/* Free up the buffers and tag so some else can use them */
	scsi_freebuf (csc, tip->st_bufnum, bp->b_bcount);
	tip->flag = 0;
	scsi_freetag (ic, tip);
	iodone(bp);

	/*
	 * If more work to do on this drive, restart it.
	 */

	iod = scsidinfo[unit];

	if (iod->iod_dk >= 0)
		dk_busy &= ~(1 << iod->iod_dk);



	if ((dp->b_actf) && (dp->b_active == 0)) {
		(void) scsiustart(iod);
	}

        /*
         * If the controller is not transferring, but there are devices
         * ready to transfer, start the controller. Otherwise, if the other
	 * controller is waiting for a chance then start it. (this is not
	 * very fair as the other controller can be "locked out" if the
	 * queue never is emptied).
         */

        if (ic->ic_tab.b_actf)
                (void) scsistart(ic);
        
        return (0);
}


#ifdef DEBUG
scsistatus(bp, scsiaddr)
        register struct buf *bp;
        register struct scsidevice *scsiaddr;
{
        register status = scsiaddr->scsi_status;

        if (bp) {
                printf("sc%d%c Block Number = %d ", DRIVE(bp->b_dev),
			PART(bp->b_dev) + 'a', bp->b_logblk);
		printf("b_flags (%x) b_addr (%x)\n", bp->b_flags,
			bp->b_un.b_addr);
	} else {
        	printf("scsi: ");
	}

        printf("status = 0x%b\n", status, SCSI_ST_BITS);
}
#endif DEBUG


/*
 * Simple Minded tag allocation scheme for now, which allocates from
 * 0 -> (SCSI_NSTAGS - 1) and then waits for all to finish and then starts
 * over again.  If curtag catches up to nexttag both go back to ZERO
 */
struct scsi_tag_info *
scsi_gettag (csc)
register struct scsic_softc *csc;
{
	register struct scsi_tag_info *tip = 0;
	register int tag, s;

	s = spl_disk();

	if (csc->scsi_nexttag < SCSI_NSTAGS) {
		tag = csc->scsi_nexttag;
		csc->scsi_nexttag++;
	} else {
		splx(s);
		return (0);
	}

	tip = &csc->tag_info [tag];
	tip->tag = (u_char) tag;
	csc->scsi_tag[tag] = tip;

	splx(s);
	return (tip);
}

scsi_freetag (ic, tip)
struct iocc_ctlr *ic;
struct scsi_tag_info *tip;
{
	register int unit = DRIVE(tip->bp->b_dev);
	register struct scsic_softc *csc = &scsic_softc[ic->ic_ctlr];
	u_char tag = tip->tag;
	register int s = spl_disk();


	if (tip->flag & SCSI_CMDPHASE) {
		printf ("Freetag: pulling (%x) off cmd queue\n", tag);
		scsi_cmd_done (ic, unit);
	} else if (tip->flag & SCSI_DMAPHASE) {
		printf ("Freetag: pulling (%x) off dma queue\n", tag);
		scsi_dma_done (ic);
	}

	/* Free this tag */
	csc->scsi_tag[tag] = (struct scsi_tag_info *) 0;
	bzero (tip, sizeof(struct scsi_tag_info));

	/* Update what next tag will be used */
	while (csc->scsi_curtag < csc->scsi_nexttag) {
		if (csc->scsi_tag[csc->scsi_curtag])
			break;
		csc->scsi_curtag++;
	}

	if (csc->scsi_curtag == csc->scsi_nexttag) {
		csc->scsi_curtag = 0;
		csc->scsi_nexttag = 0;
	}
	splx(s);
}


scsi_freebuf (csc, bufnum, bcount)
	register struct scsic_softc *csc;
	u_char bufnum;
	int bcount;
{
	register char *sb;
	register int s = spl_disk();
	register int blkcnt;

	blkcnt = (bcount + (SCSI_BSIZE - 1)) >> SCSI_BSHIFT;
	sb = &csc->scsi_buf[bufnum];

	SCSIDEBUG( SHOW_BUF,
		printf ("SCSIfreebuf (%d) for (%d)\n", bufnum, blkcnt)
		);

	while (blkcnt--) {
		*sb++ = 0;
	}
	splx(s);
	return (0);
}

scsi_getbufnum (csc, tip)
register struct scsic_softc *csc;
struct scsi_tag_info *tip;
{
	register int blkcnt, savecnt;
	register char *sb, *sb_start, *sb_end, *sb_bufstart;
	int start, end;
	int s = spl_disk();

	tip->granularity = 0;

	savecnt = (tip->bp->b_bcount + (SCSI_BSIZE - 1)) >> SCSI_BSHIFT;
	sb_bufstart = sb = csc->scsi_buf;
	sb_end = &sb_bufstart[SCSI_NCMDBUFS-1];

	while (sb <= sb_end) {
		if (*sb == 0) {
			blkcnt = savecnt - 1;
			sb_start = sb;
			while (blkcnt > 0) {
				if (++sb > sb_end)
					break;
				if (*sb != 0)
					break;
				blkcnt--;
			}
		} else {
			sb++;
			continue;
		}

		if (blkcnt == 0) {
			sb = sb_start;
			blkcnt = savecnt;
			while (blkcnt--)
				*sb++ = 1;

			start = sb_start - sb_bufstart;
			end = sb - sb_bufstart - 1;
			SCSIDEBUG( SHOW_BUF,
				printf ("SCSIgetbuf (%d) -> (%d)\n", start, end)
				);
			splx(s);
			return (start);
		}
	}

	splx(s);
	SCSIDEBUG( SHOW_BUF, printf("SCSIgetbuf no buf available\n"));
	return (SCSI_NOBUFS);
}


/*
 * watchdog timer - checks to see if we've lost an interrupt
 * by having the timer click SCSITIMEOUT times without being cleared
 * (by interrupt routine)
 */
/*ARGSUSED*/
scsiwatch(reg)
        caddr_t reg;
{
        register struct scsic_softc *csc;
        register struct iocc_ctlr *ic;
        register int i, j;
	register struct scsi_tag_info *tip;
        register int s = spl_disk();
        struct buf *bp;
	int anytimeout = 0;

        for (i = 0; i < NSCC; ++i) {
                csc = &scsic_softc[i];

                if ((ic = scsiminfo[i]) == 0 ) {
			continue;
		}
		if (ic->ic_alive == 0 || csc->scsi_nexttag == 0 ) {
			if (ic->ic_tab.b_active) {
				printf("scsiwatch: active but no tags!!!\n");
				scsi_clearbufs(csc);
				ic->ic_tab.b_active = 0;
				(void) scsistart(ic);
			}
                        continue;         /* not doing anything */
                }

		for (j = csc->scsi_curtag; j < csc->scsi_nexttag; j++) {
			tip = &csc->tag_info [j];
			if (!tip->flag) {
				tip->scsi_timer = 0;
				continue;
			}
			if (++tip->scsi_timer > SCSITIMEOUT) {
				u_short status;

				anytimeout++;
				bp = tip->bp;
				if (!bp) {
					tip->flag = 0;
					continue;
				}

				printf("sc%d:lost int tag (%d) (%d)\n",
					DRIVE(bp->b_dev), j, tip->tag);
#ifdef DEBUG
				scsistatus(bp,(struct scsidevice *)ic->ic_addr);
#endif DEBUG

				status = scsi_cleartag(ic->ic_ctlr, tip->tag);

				/* Check if it really didn't happen */
				if ((status & SCSI_ST_CCSM) != 0x4600) {
					bp->b_flags |= B_ERROR;
					bp->b_error = EIO;
					printf ("SCSI error cmd never finished\n");
				} else {
					printf("SCSI cmd DID finish\n");
				}
				printf ("	status (%b)\n", status, SCSI_ST_BITS);
				printf("bufnum (%d) bcount (%d) baddr (%x)\n",
					tip->cur_bufnum, bp->b_bcount,
					bp->b_un.b_addr);
#ifdef DEBUG
				scsi_CHECK_routine();
#endif DEBUG
				/* Restore their original bp values */
				bp->b_bcount = tip->b_bcount;
				bp->b_un.b_addr = tip->b_addr;

				scsi_freebuf(csc, tip->st_bufnum, bp->b_bcount);
				scsi_freetag (ic, tip);

				bp->b_resid = 0;

				iodone(bp);
			}

		}

		/*
		 * See if more work to do on this drive.
		 */
		if (anytimeout) {
			ic->ic_tab.b_active = 0;
			(void) scsistart(ic);
		}
        }

        timeout(scsiwatch, (caddr_t)0, hz);
        splx(s);
}

/* Maximum amount of data buffer on scsi adapter */
#define	SCSIMAXPHYS	(SCSI_NCMDBUFS * 512)

void
scsiminphys(bp)
	struct buf *bp;
{
	if (bp->b_bcount > SCSIMAXPHYS)
		bp->b_bcount = SCSIMAXPHYS;
}

#ifdef DEBUG
/* Handy routine to set a break point in while debugging */
scsi_CHECK_routine()
{
}

char scsi_printbuf[4096];
char *scsi_pbuf = scsi_printbuf;
char *scsi_spbuf = scsi_printbuf;
char *scsi_epbuf = &scsi_printbuf[4095];
char *itox();

scsi_print (s, v)
register char *s;
register int v;
{
	char vs[30];
	register char *vsp = vs;

	scsi_pbuf--;
	while (*s) {
		*scsi_pbuf++ = *s++;
		if (scsi_pbuf >= scsi_epbuf)
			scsi_pbuf = scsi_spbuf;
	}
	itox (v, vsp);
	while (*vsp) {
		*scsi_pbuf++ = *vsp++;
		if (scsi_pbuf >= scsi_epbuf)
			scsi_pbuf = scsi_spbuf;
	}
}
static char *
itox(n, str)
	u_int n;
	char *str;
{
	char prbuf[30];
	register char *cp;

	cp = prbuf;
	do {
		*cp++ = "0123456789ABCDEF"[n%16];
		n /= 16;
	} while (n);
	do {
		*str++ = *--cp;
	} while (cp > prbuf);
	*str++ = '$';
	*str = 0;
	return (str);
}
#endif DEBUG


scsi_print_sense (unit,status,bp,scsiaddr,sen)
int	unit;
u_short	status;
struct buf *bp;
struct scsidevice *scsiaddr;
register scsi_sense *sen;
{
	register scsi_err_rec *erec;
	register int add_len = sen->add_len - 0x10;
	register int errn = 0;
	register int ret,idtype;
	union	lba {
		char	c[4];
		int	i;
	} lba;

	ret = sen->key;

	SCSIDEBUG(SHOW_SENSE, printf ("Error Sense Key (%x)", ret));

#ifndef	SCSI_SOFT_ERROR_REPORT
	if (sen->key == SCSI_SENSE_RE) {
		return(ret);
	}
#endif /* SCSI_SOFT_ERROR_REPORT */
	if (sen->key == SCSI_SENSE_UA) {
		return(ret);
	}

	printf ("sc%d: CHECK condition status (0x%b)",
	      unit, status, SCSI_ST_BITS);
	if (bp) {
		printf(" (%s)\n",(bp->b_flags& B_READ)?"READ":"WRITE");
	} else {
		printf("\n");
	}
#ifdef DEBUG
        scsi_CHECK_routine();
        scsistatus(bp, scsiaddr);
#endif DEBUG

	if (sen->key == SCSI_SENSE_NO) {
		return (ret);
	}
	printf ("scsi error sense data---\n");
	printf("Class 0x%x Code 0x%x key 0x%x (%s)\n",
	  sen->Err_class,sen->Err_code, sen->key, scsi_key_type[sen->key]);

	if (add_len >= SCSI_ERRRECSZ) {
		printf(" scsi error record data---\n");
		while ((add_len -= SCSI_ERRRECSZ) >= 0) {
			int elun;

			printf ("Error record %d:\n", errn);
			erec = &sen->err_rec[errn++];

			/*
			 * print and decode the unit reference code
			 */
			printf("Unit Reference Code 0x%x (",
			    (erec->detect_id << 8) | erec->err_number);
			switch (erec->detect_id) {
			case SCSI_9332_SERVO_LUN0:
				printf("Server Processor lun 0:");
				idtype = SCSI_9332_SERVO;
				break;
			case SCSI_9332_SERVO_LUN1:
				printf("Server Processor lun 1:");
				idtype = SCSI_9332_SERVO;
				break;
			case SCSI_9332_FILE_LUN0:
				printf("File Processor lun 0:");
				idtype = SCSI_9332_FILE;
				break;
			case SCSI_9332_FILE_LUN1:
				printf("File Processor lun 1:");
				idtype = SCSI_9332_FILE;
				break;
			case SCSI_9332_IP_PROC:
				printf("Interface Processor:");
				idtype = SCSI_9332_IP;
				break;
			default:
				printf("Bogus id/type)\n");
				idtype= SCSI_9332_BOGUS;
				break;
			}
			if (idtype != SCSI_9332_BOGUS) {
				if (erec->err_number >= scsi_max_error[idtype]) {
					printf("Bogus error code)\n");
				} else {
					printf("%s)\n",
						scsi_error_title[idtype]
							[erec->err_number]
					);
				}
			}


			/* print the recovery result properly */
			printf(" recovery result 0x%x (%s,%d retries",
		 	   erec->rec_res, (erec->rec_res & 0x80 ? "succeded":
				"FAILED"), erec->rec_res & 0xf
			);
			if (elun = (erec->rec_res & 0x70)) {
				switch (elun) {
				case 0x30:
					printf (",both luns effected");
					break;
				case 0x60:
					printf (",niether luns effected");
					break;
				case 0x10:
					elun = 0;
					printf (",affected lun %d", elun);
					break;
				case 0x20:
					elun = 1;
					printf (",affected lun %d", elun);
					break;
				defalut:
					printf (",illegal lun affected field (%x)\n", elun >> 4);
					break;
				}
			}
			printf(")\n");


			/* Print lba if valid */
			if (sen->adval) {
				register int i;

				for (i = 0; i < 4; i++) {
					lba.c[i] = erec->lba[i];
				}
				if (elun)
					lba.i += SCSI9332_BPLUN;
				printf ("lba (%d)\n", lba.i);
			}
			if ((erec->detect_id) && 
			    (sen->add_code != SCSI_ADSEN_RUT))
				ret = SCSI_SENSE_NR;

			/* Soft error result overrides key */
			if (erec->rec_res & 0x80)
				ret = SCSI_SENSE_RE;
		}
	}
	SCSIDEBUG(SHOW_SENSE,scsi_dumpbuf (sen, SCSI_SENSESZ););

	return (ret);
}

/* The way this is done right now might make us miss an interrupt??? */
u_short
scsi_cleartag(ctlr, tag)
register int ctlr;
u_char tag;
{
	register struct scsidevice *scsiaddr;
	u_short	 status;
	register int s = spl_disk();
	int	 inton = 0;

	scsiaddr = (struct scsidevice *)(scsiminfo[ctlr]->ic_addr);
	
	/* Disable interrupts while clearing tag */
	if (scsiaddr->scsi_config & SCSI_CF_IEN) {
		inton = 1;
		scsiaddr->scsi_config &= ~SCSI_CF_IEN;
	}

	scsi_sendcmd (scsiaddr, SCSI_CMD_CLRT, &tag, 1, 0, SCSI_CMD_TAG);

	DELAY (10000);

	scsiwaitvalid(0, 0, scsiaddr);

	/* Save the status then Clear the interrupt */
	status = scsiaddr->scsi_status;
	scsi_dummy = *(u_char *) &scsiaddr->scsi_status;

	/* Enable interrupts again */
	if (inton)
		scsiaddr->scsi_config |= SCSI_CF_IEN;

	splx(s);
	return (status);
}

scsi_dumpbuf (buf, len)
register char *buf;
register int len;
{
	while (len--)
		printf ("(%x) ", *buf++);
	printf ("\n");
}

scsi_config_dma (ic)
	register struct iocc_ctlr *ic;
{
	/* Tell the system what kind of DMA device we are */
	ic->ic_dmaflags = (DMA_CASCADE | DMA_PAGE | DMA_PHYSICAL);
}

/*
 * process ioctl's
 * the only one supported at the moment returns the partition size and
 * geometry information for newfs.
 */
/*ARGSUSED*/
scsiioctl(dev, cmd, data, flag)
	caddr_t data;
	dev_t dev;
{
	register int unit = DRIVE(dev);
	register struct iocc_device *iod;
	register struct scsist *st;
	register struct part_entry *off;
	register struct iocc_ctlr *ic;

	if(unit >= NSC || (iod = scsidinfo[unit]) == 0 || iod->iod_alive == 0)
		return (ENODEV);

	ic = iod->iod_mi;
	if (cmd == DKIOCGPART) {
		register struct dkpart *dk = (struct dkpart *) data;

		st = &scsist[iod->iod_type];
		off = SCSIPART(unit, PART(dev));
		dk->dk_size = off->nblocks;		/* size */
		dk->dk_start = off->cyloff * st->nspc;	/* start */
		dk->dk_blocksize = st->nbps;		/* device blocksize */
		dk->dk_ntrack = st->ntpc;		/* tracks */
		dk->dk_nsector = st->nspt;		/* sectors */
		dk->dk_ncyl = off->nblocks / st->nspc;	/* number of cyls */
		strcpy(dk->dk_name,scsist[iod->iod_type].type); /* copy name */
		return(0);
	} else if (cmd == SCSIIOCFORMAT) {
		register struct scsiformat *fmt = (struct scsiformat *) data;

		scsi_do_format (ic, unit, 0, fmt);
		if (iod->iod_type == SCSI400_INDEX)
			scsi_do_format (ic, unit, 1, fmt);
		return (0);
	} else
		return(ENOTTY);		/* sigh */
}

/*
 * read in VRM minidisk directory and look for minidisks of
 * the appropriate name (scsi?[a-h])
 * and build off entries for the given partitions.
 */
scsigetpart(scsiaddr,ic,unit,st,off)
struct scsidevice *scsiaddr;
register struct iocc_ctlr *ic;
register int unit;
struct scsist *st;
register struct partab *off;
{
	int buf[MINISIZE/sizeof (int)];
	struct minidirectory *minidirectory = (struct minidirectory *)buf;
	register int n;
	register int next;
	register struct minidisk *disk;
	register int found=0;
	register int cnt;
	int cylsize = st->nspc;

	if (scsird(ic, unit, MINIDISK_BLOCK, MINISIZE, buf) < 0 ||
	    minidirectory->header.number <= 0 ||
	    minidirectory->header.number >= MAXDISKS) {

	SCSIDEBUG(SHOW_DUMP,
		{
		  printf ("minidirectory no good\n");
		  scsi_dumpbuf ((char *)minidirectory, sizeof (struct minidirectory));
		}
	);

		return(0);		/* didn't read it yet */
	}

	SCSIDEBUG(SHOW_DUMP,
		{
		  printf ("minidirectory good\n");
		  scsi_dumpbuf ((char *)minidirectory, sizeof (struct minidirectory));
		}
	);

	cnt = minidirectory->header.number;
	for (next=minidirectory->header.first; next >= 0; next=disk->next)
	{
		if (--cnt < 0 || next > MAXDISKS)
			return(0);	/* directory corrupted */
		disk = &(minidirectory->minidisk[next]);
		if (ISFREE(disk))
			continue;	/* skip free space */
		if ((disk->name[0] == 's' && disk->name[1] == 'c' &&
		    (n = disk->name[3]-'a') >= 0 && n < 8) ||
		    (disk->type == TYPE_PAGE && (n = 1)))
		{
			++found;
			off->part[n].cyloff = (disk->start+cylsize - 1)/cylsize;
			off->part[n].nblocks = disk->size - (off->part[n].cyloff*cylsize - disk->start);

			SCSIDEBUG(SHOW_CONFIG, printf("found sc%d%c start %d size %d\n",unit,n+'a',off->part[n].cyloff, off->part[n].nblocks));
		}
	}
	return(found);
}

scsird(ic, unit, block, len, buf)
register struct iocc_ctlr *ic;
register int unit;
register int block, len;
register caddr_t buf;
{
	int blkcnt = (len + (SCSI_BSIZE - 1)) >> SCSI_BSHIFT;

	return (scsi_rd_cmd (ic, unit, 0, CMD_READ, buf, blkcnt, len, block));
}

/* Get sense data from SCSI */
scsi_get_sense_data (ic, unit, lun)
register struct iocc_ctlr *ic;
register int unit;
int lun;
{
	static scsi_sense sense;
	int ret;

	scsi_rd_cmd(ic,unit,lun, CMD_RS, &sense, SCSI_SENSESZ, SCSI_SENSESZ, 0);

	ret = scsi_print_sense (unit,
		((struct scsidevice *)ic->ic_addr)->scsi_status,
				(struct buf *)0,
				(struct scsidevice *)ic->ic_addr,&sense);

	return (ret);
}

scsi_mode_sense_cmd (ic, unit, lun, msp)
register struct iocc_ctlr *ic;
register int unit;
int lun;
struct mode_sense *msp;
{
	return (
	  scsi_rd_cmd(ic, unit, lun, CMD_MS, msp, MODE_SENLEN, MODE_SENLEN,0)
	);
}

scsi_mode_select_cmd (ic, unit, lun, msp)
register struct iocc_ctlr *ic;
register int unit;
int lun;
struct mode_sense *msp;
{
	return (
	  scsi_wr_cmd(ic,unit,lun,CMD_MODE_SEL,msp,MODE_SENLEN,MODE_SENLEN,0)
	);
}

#define INTON	0x01	/* Interrupts where on */
#define CMDON	0x02	/* CMD was in progress */

/* Generic routine that performs SCSI 'cmd' and dma's 'len' into 'buf'
 * Issues the SCSI read comand and waits for it
 * finish and then DMAs the result to the specified buffer.  Since nothing
 * else should be going on now we use adapter buffer 0 and tag 0.
 */
scsi_rd_cmd (ic, unit, lun, cmd, buf, cmdlen, dmalen, block)
register struct iocc_ctlr *ic;
register int unit;
int lun;
u_char cmd;
register caddr_t buf;
register int cmdlen, dmalen;
int block;
{
	register struct scsic_softc *csc = &scsic_softc[ic->ic_ctlr];
	register struct scsi_softc *cs = &scsi_softc[unit];
        register struct scsidevice *scsiaddr = (struct scsidevice *)ic->ic_addr;
	register int s = spl_disk();
	int	 flag = 0;
	int	 ret = BAD;
	struct	 scsi_tag_info ti;
	struct	 buf b;
	u_short	 status;

SCSIDEBUG(SHOW_CONFIG,
printf("scsi_rd_cmd%d: lun %d cmd %x buf %x cmdlen %d dmalen %d block %d\n",
		unit, lun, cmd, buf, cmdlen, dmalen, block)
	);

	/* Make sure interrupts are off while we do the command and DMA */
	if (scsiaddr->scsi_config & SCSI_CF_IEN) {
		flag |= INTON;
		scsiaddr->scsi_config &= ~SCSI_CF_IEN;
	}

	/* Check if a cmd was going on and cancel it if so */
	if (cs->scsi_flag & SCSI_CMDBUSY) {
		printf ("scsi_read_cmd: CLearing active CMD\n");
		scsi_cleartag(ic->ic_ctlr, cs->cmdtip_head->tag);
		cs->scsi_flag &= ~SCSI_CMDBUSY;
		flag |= CMDON;
	}

	/* Check if a dma was going on and cancel it if so */
	if (csc->scsi_flag & SCSI_DMABUSY) {
		printf ("scsi_read_cmd: CLearing active DMA\n");
		scsi_cleartag(ic->ic_ctlr, csc->dmatip_head->tag);
		csc->scsi_flag &= ~SCSI_DMABUSY;
	}

	/* Do the SCSI read type 'cmd' */
	scsicmd(scsiaddr, unit, lun, cmd, SCSI_IN, block, (u_short) cmdlen,
		SENSE_BUFNUM(unit), 0, 0, SCSI_CMD_TAG);

	/* Check if SCSI return status is OK */
	if (scsi_check_status(scsiaddr, unit, &status)) {

		/* Set up a DMA to READ Data from the adapter */
		b.b_flags = B_READ;
		ti.err_cnt = 0;
		ti.flag = 0;
		ti.tag = SCSI_CMD_TAG;
		ti.granularity = 0;
		ti.st_bufnum = ti.cur_bufnum = SENSE_BUFNUM(unit);
		ti.bp = &b;
		b.b_un.b_addr = buf;
		b.b_bcount = dmalen;
		ti.blkcnt = 0;

		/* Put this DMA at head of queue and kick it off */
		if ((ti.next = csc->dmatip_head) == NULL)
			csc->dmatip_tail = &ti;
		csc->dmatip_head = &ti;
		csc->scsi_flag |= SCSI_DMABUSY;
		scsi_dmastart(ic);

		DELAY (10000);

		if (!scsiwaitvalid(0, 0, scsiaddr)) {
			if (!((status = scsiaddr->scsi_status) & SCSI_ST_CCSM)){
				ret = OK;
			}
		}

		/* Clear status register */
		scsi_dummy = *(u_char *) &scsiaddr->scsi_status;

		scsi_dma_done (ic);
	} else {
		printf ("OH NO (%x) command didn't work (%x)!!!\n", cmd,status);
	}

	/* Turn interrupts back on if they were on */
	if (flag & INTON)
		scsiaddr->scsi_config |= SCSI_CF_IEN;


	/* Kick off command we interrupted */
	if (flag & CMDON) {
		cs->scsi_flag |= SCSI_CMDBUSY;
		scsi_cmdstart (ic, unit);
	}

	splx(s);

	if (ret == BAD)
		return (-1);
	else
		return (dmalen);
}
/* Generic routine that dma's 'len' into 'buf' performs SCSI 'cmd'.
 */
scsi_wr_cmd (ic, unit, lun, cmd, buf, cmdlen, dmalen, fmt)
register struct iocc_ctlr *ic;
register int unit;
int lun;
u_char cmd;
register caddr_t buf;
register int cmdlen, dmalen;
struct scsiformat *fmt;
{
        register struct scsidevice *scsiaddr = (struct scsidevice *)ic->ic_addr;
	register int s = spl_disk();
	int	 flag = 0;
	int	 ret = BAD;
	struct	 scsi_tag_info ti;
	struct	 buf b;
	u_short	 status;

SCSIDEBUG(SHOW_CONFIG,
	printf("scsi_wr_cmd%d: lun %d, cmd %x, buf %x, cmdlen %d, dmalen %d\n",
		unit, lun, cmd, buf, cmdlen, dmalen)
	);

	/* Make sure interrupts are off while we do the command and DMA */
	if (scsiaddr->scsi_config & SCSI_CF_IEN) {
		flag |= INTON;
		scsiaddr->scsi_config &= ~SCSI_CF_IEN;
	}

	/* Set up a DMA to Write Data to the adapter */
	if (dmalen > 0) {
		b.b_flags = B_WRITE;
		ti.err_cnt = 0;
		ti.flag = 0;
		ti.tag = SCSI_CMD_TAG;
		ti.granularity = 0;
		ti.st_bufnum = 0;
		ti.cur_bufnum = 0;
		ti.bp = &b;
		b.b_un.b_addr = buf;
		b.b_bcount = dmalen;
		ti.blkcnt = 0;

		/* Nothing else should be going on so kick off the DMA */
		scsi_dma_setup (ic, &ti);

		DELAY (10000);

		if (!scsiwaitvalid(0, 0, scsiaddr)) {
			if (!((status = scsiaddr->scsi_status) & SCSI_ST_CCSM)){
				ret = OK;
			}
		}

		/* Clear status register */
		scsi_dummy = *(u_char *) &scsiaddr->scsi_status;

		scsi_dma_done (ic);
	} else
		ret = OK;

	if (ret == OK) {
		/* Do the SCSI write type 'cmd' */
		if (cmd == CMD_FU) {
			register int maxtime;
			scsicmd(scsiaddr, unit, lun, cmd, SCSI_OUT,
				fmt->level, (u_short) cmdlen, 0, 0, 
				fmt->interleave, SCSI_CMD_TAG);

			maxtime = 10 * MAX_WAIT_COUNT;
			while (!(scsiaddr->scsi_status & SCSI_ST_VALID))
				if (--maxtime <0)
					break;
			
		} else {
			scsicmd(scsiaddr, unit, lun, cmd, SCSI_OUT, 0,
				(u_short) cmdlen, 0, 0, 0, SCSI_CMD_TAG);
		}

		/* Check if SCSI return status is OK */
		if (!scsi_check_status(scsiaddr, unit, &status)) {
			if (status != SCSI9332_CHECK) {
				printf ("scsi_wr_cmd: cmd %x bad status %x\n",
					cmd, status);
				ret = BAD;
			}
		}
	}

	/* Turn interrupts back on if they were on */
	if (flag & INTON)
		scsiaddr->scsi_config |= SCSI_CF_IEN;
	splx(s);

	if (ret == BAD)
		return (-1);
	else
		return (dmalen);
}

char format_buf[4] = {0,0,0,0};

scsi_do_format (ic, unit, lun, fmt)
register struct iocc_ctlr *ic;
register int unit, lun;
struct scsiformat *fmt;
{

	switch (fmt->level) {
	case FL_CHIL:
		scsi_wr_cmd (ic, unit, lun, CMD_FU, 0, 0, 0, fmt);
		break;
	case FL_PANDG:
	case FL_ONLYP:
		scsi_wr_cmd (ic, unit, lun, CMD_FU, format_buf, 4, 4, fmt);
		break;
	default:
		return (EIO);
	}
	return (0);
}

scsi_clearbufs(csc)
register struct scsic_softc *csc;
{
	register int j;

	for (j = 0; j < SCSI_NCMDBUFS; ++j) {
		if (csc->scsi_buf[j]) {
			printf ("Buf (%d) was set\n", j);
			csc->scsi_buf[j] = 0;
		}
	}

}

scsi_start_sense (ic, unit, tip)
register struct iocc_ctlr *ic;
register int unit;
register struct scsi_tag_info *tip;
{
	register struct scsidevice *scsiaddr = (struct scsidevice *)ic->ic_addr;
	register int lun;
	register struct buf *bp = tip->bp;

	if (bp->b_logblk < SCSI9332_BPLUN) {
		lun = 0;
	} else {
		lun = 1;
	}

	tip->flag |= SCSI_INSENSECMD;

	/* Do the SCSI read sense command */
	scsicmd(scsiaddr, unit, lun, CMD_RS, SCSI_IN, 0, (u_short) SCSI_SENSESZ,
		SENSE_BUFNUM(unit), 0, 0, tip->tag);

}
#endif NSC
