/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: sd.c 1.59 90/05/04 $"

/*
 * sd.c 
 *	SCSI disk device driver
 */

/* $Log:	sd.c,v $
 */

#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/vtoc.h"
#include "sys/sysmacros.h"
#include "sys/signal.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/mutex.h"
#include "sys/user.h"
#include "sys/errno.h"
#include "sys/conf.h"
#include "sys/buf.h"
#include "sys/uio.h"
#include "sys/vnode.h"
#include "sys/systm.h"
#include "sys/vm.h"
#include "sys/stats_disk.h"
#include "sys/sysmacros.h"
#include "sys/cmn_err.h"
#include "sys/ioctl.h"

#include "sys/engine.h"
#include "sys/slic.h"

#include "sys/pte.h"
#include "sys/intctl.h"
#include "sys/plocal.h"
#include "sys/file.h"
#include "sys/timer.h"
#include "sys/proc.h"

#include "sys/sec.h"			/* SCSI common data structures */
#include "sys/sec_ctl.h"		/* SCSI drivers' common stuff */
#include "sys/scsidisk.h"
#include "sys/scsiioctl.h"
#include "sys/sec_device.h"
#include "sys/sd.h"			/* driver local structures */
#include "sys/sd_device.h"
#include "sys/scsi.h"

#include "sys/autoconf.h"

/*
 * Externs and global data structures.
 */
extern	struct	sd_bconf sdbconf[];	/* Binary configuration info (bc)*/
struct	sd_info	**sdifd;		/* Unit interrupt mapping base ptr */
extern	unchar *SEC_rqinit();
extern	caddr_t topmem;			/* highest addressable location */
extern	unchar sddevtype;		/* byte 0 of INQUIRY return data */
extern	unchar sdinq_targformat;	/* byte3 of above on target adaptors */
extern	unchar sdinq_ccsformat;		/* byte 3 of above on embedded SCSI */
extern	bool_t CCS_present;		/* is there an embedded SCSI drive */

extern int	sdsensebuf_sz;		/* Sense buffer info - bc */
extern int	sdmaxminor;		/* Device numbering info - bc */
extern int	sdretrys;		/* Number of retrys before giving up */

extern int sd_b_major;
extern int nulldev();


#ifdef SDDEBUG
#define SCSIFIRMWARE_SANITY 
#endif	/* SDDEBUG  */

int	sd_baselevel = -1;		/* Base interrupt lvl for all devices*/

struct dev *sd_dev;			  /* to be allocated in sd_init */

/* Forward references */
int	sdstrat(), sdiatsz();
int sdopen(), sdminphys(), sdsize(), sdprint(), sdread(), sdwrite(), sdioctl();
int sdfirstsect();
int sdclose();
void sd_intr();
daddr_t sdreadc();

extern struct devops sddevops;

/*
 * Driver development helpers.
 */
#ifdef DEBUG
int	sd_debug = 0;	/* 0=off 1=little 2=more 3=lots >3=all */
#define SDPRINTF	if (sd_debug) CPRINTF
#endif	/* DEBUG  */

/*
 * The following data structures are only used for probing
 */
struct sec_dev_prog sd_devprog;	/* one device program */
unchar *sd_datap = 0;
unchar *sd_data_queue;

/*
 * sd_probe(sd) - probe procedure 
 *	struct sec_dev *sd;
 *
 * This procedure polls a device with the test unit ready command
 * to determine if the device is present.
 *
 * Note: This procedure tracks the header file sec.h
 * which must track the firmware for queue size information.
 */
sd_probe(device)
	struct sec_scsi_device *device;
{
	int resp;
	struct scrsense *sense;
	struct scinq_dev *sdinq;
	struct sec_probe probe;
	struct sec_probe *sed = &probe;
	
	/* First, set sd_req_size, sd_doneq_size, sd_destslic & sd_flags */
	device->sec_dev->sd_req_size = sd_req_size;
	device->sec_dev->sd_doneq_size = sd_doneq_size;
	device->sec_dev->sd_destslic = SL_GROUP | TMPOS_GROUP;
	device->sec_dev->sd_flags = sd_flags;

	/* Next, make it look like the old way */
	sed->secp_desc = device->sec_dev->sd_desc;
	sed->secp_puq =  device->sec_dev->sd_desc->sec_powerup;
	sed->secp_target = device->sec_dev->sd_target;
	sed->secp_unit = device->sec_dev->sd_unit;
	sed->secp_chan = device->sec_dev->sd_chan;
	sed->secp_flags = device->sec_dev->sd_flags;


	if (! sd_datap) {
		sd_datap = (unchar *) calloc(SIZE_MAXDATA);
		sd_data_queue = SEC_rqinit(sd_datap, SIZE_MAXDATA);
	}

	if ((resp = sd_docmd_pr(SCSI_TEST, sed)) == 2) {

		/*
		 * could be a CCS disk - if so, we need to clear out
		 * the unit attention error condition.
		 */

		if (sd_docmd_pr(SCSI_RSENSE, sed) != 0)
			return(SECP_NOTFOUND);

		sense = (struct scrsense *) sd_datap;
		if ((sense->rs_key & RS_SENSEKEYMASK) != RS_UNITATTN)
			return(SECP_NOTFOUND);

	/*
	 * timeout occurred - no target present
	 */
	}
        else if (resp == 1) {
		return(SECP_NOTFOUND|SECP_NOTARGET);

	} else if (resp != 0) {
		return(SECP_NOTFOUND);
	}

	/*
	 * discover the type of target adaptor
	 */

	if (sd_docmd_pr(SCSI_INQUIRY, sed) != 0)
		return(SECP_NOTFOUND);
	sdinq = (struct scinq_dev *) sd_datap;

	/*
	 * confirm that this is a disk.  This keeps the sd driver
	 * from responding to a SCSI tape.
	 */

	if (sdinq->sc_hdr.ih_devtype != sddevtype)
		return(SECP_NOTFOUND);

	/*
	 * non-CCS format disk
	 */

	if (sdinq->sc_hdr.ih_reserved == sdinq_targformat)
		return(SECP_FOUND);

	if (sdinq->sc_hdr.ih_reserved != sdinq_ccsformat) {
		cmn_err( CE_WARN,
		 "sd: not a supported embedded SCSI drive, target %d, unit %d",
			sed->secp_target, sed->secp_unit);
		/*
		 *+ While the sd driver was probing for SCSI disks on an SEC, 
		 *+ it found a drive 
		 *+ that does not have the right format for the CCS drives.
		 *+ The driver supports only CCS drives.  Use a supported drive.
		 */
		return(SECP_NOTFOUND|SECP_ONELUN);
	}

	/*
	 * we have found a CCS drive.
	 */

	CCS_present = 1;

	return(SECP_FOUND|SECP_ONELUN);
}

/*
 * sd_init - initialize all channels of this device driver.
 *
 * Called once after all probing has been done.
 *
 * This procedure initializes and allocates all device driver data
 * structures based off of the configuration information passed in from
 * autoconfig() and from the device drivers binary configuration tables.
 * The boot procedure also maps interrupt levels to unit number by
 * placing the channels communications structure pointer into a
 * major/minor number mapped dynamically allocated array (sdifd[]).
 */
void
sd_init( driver )
	struct driver_module *driver;
{
	register struct sec_dev *sed;
	register struct	sd_info *ifd;
	register struct	seddc *dc;
	struct	sd_bconf    *bconf;	/* Binary configuration info */
	int dev;
	daddr_t	capacity;
	int ndevs = driver->numdev;
	int alive_seen = 0;
	struct sec_scsi_device *devs
		= (struct sec_scsi_device *) driver->devices;
	struct sec_scsi_device *sd;

	sdifd = (struct sd_info **)calloc(((sdmaxminor+1) * (sizeof(struct sd_info *))));

#ifdef DEBUG
	SDPRINTF( "sd_init: %d of %d device alive.\n", driver->nalive, ndevs);
#endif /* DEBUG */
	for (dev = 0; dev < ndevs; dev++) {
		/* Allocate and initialize interrupt bins. */
		devs[dev].sec_dev->sd_vector = ivecall( sd_bin );
		ivecinit( sd_bin, devs[dev].sec_dev->sd_vector, sd_intr);
	}

	if ( ndevs )
		sd_baselevel = devs[0].sec_dev->sd_vector;
	
	sd_dev = (struct dev *)calloc( ndevs * sizeof (struct dev) );
	if ( !sd_dev ) {
		cmn_err( CE_WARN, "sd_init:  No memory for dev structures");
		/*
		 *+ The kernel ran out of memory when trying to allocate space
		 *+ for the sd driver's (devsw) dev structures.  Either
		 *+ something has incorrectly consumed memory space or more
		 *+ memory needs to be added to your system.
		 */
		for ( dev = 0; dev < ndevs; dev++ )
			driver->devices[dev].driver
				= (struct driver_module *) 0;
		return;
	}

	/*
	 * for each configured device
	 */
	for (dev = 0; dev < ndevs; ++dev) {
		/*
		 * NOTE:
		 * The original driver assumed that all found devices were in
		 * contiguous records in the sec_sd table, the equivalent of
		 * the sd_devices table in the new scheme.  Since, to prevent
		 * name space slippage, this is not necessarily the case, we
		 * can only trim off the end of the table if we've seen all the
		 * alive devices there are.
		 */
		sd = &devs[dev];
#ifdef DEBUG
		SDPRINTF("sd_init: sd%d%s found\n",
		       dev, (sd->driver? "" : " not"));
#endif /* DEBUG */
		if ( sd->driver )
			alive_seen++;
		else if ( alive_seen == driver->nalive )
			break;
		sed = sd->sec_dev;
		
#ifdef DEBUG
		if(sd_debug) {
			CPRINTF("sd_init: info=0x%x cib=0x%x ta=%d un=%d ",
				sdifd,
				sed->sd_cib,
				sed->sd_target,
				sed->sd_unit);
			CPRINTF("bin=%d slic=%d vec=%d, alive is %s\n",
				sed->sd_bin,
				sed->sd_desc->sec_slicaddr,
				sed->sd_vector,
				(sed->sd_alive ? "Found" : "Not Found")
			);
		}
#endif	/* DEBUG  */
		if(!sed->sd_alive)
			continue;

		/*
		 * Verify that this device has been included into
		 * the devices binary configuration table. 
		 */
		if(dev >= sdmaxminor) {
			CPRINTF("sd%d: non-binary configured device found in config table ... deconfiguring.\n", dev);
			sed->sd_alive = 0;	/* force deconfigure */
			continue;
		}

		if (topmem > (caddr_t) MAX_SCED_ADDR_MEM) {
			CPRINTF("sd%d: Unsupported memory configuration with SCSI disks ... deconfiguring.\n", dev);
			sed->sd_alive = 0;
			continue;
		}

		/*
		 * determine the disk's usable size by doing a
		 * READ CAPACITY scsi command and shaving off the
		 * last three cylinders, (used by diagnostics)
		 */


		if ((capacity = sdreadc(sed)) <= 0) {
			CPRINTF("sd%d: problem doing READ CAPACITY %s\n",
			dev, "... deconfiguring");
			sed->sd_alive = 0;
			continue;
		}

		/*
		 * Initialize the device structure and
		 * copy pertenent info into it for faster/easier access.
		 */

		bconf = &sdbconf[dev];

		/*
		 * round number of iat's up to minimum boundary
		 */
		bconf->bc_num_iat = MAX(4, bconf->bc_num_iat);

		/*
		 * Info structure.
		 */
		sdifd[dev] = ifd = (struct sd_info *)calloc(sizeof(struct sd_info));
#ifdef DEBUG
		if(sd_debug)
			CPRINTF("ifd=0x%x, info=0x%x\n", ifd, sdifd[dev]);
#endif	/* DEBUG  */
		ifd->sd_desc = sed;
		ifd->sd_lun = sed->sd_unit<<5;
		ifd->sd_capacity = capacity + 1;
		ifd->sd_retrys = sdretrys;
		ifd->sd_thresh = MIN(bconf->bc_thresh, sed->sd_doneq_size);
		ifd->sd_low = MIN(bconf->bc_low, sed->sd_doneq_size);
		if(ifd->sd_thresh <= 0)
			CPRINTF("sd%d: Warning queueing disabled\n", dev);

		/*
		 * Ioctl raw buffer.
		 */
		ifd->sd_rawbuf = (caddr_t)calloc(bconf->bc_rawbuf_sz * sizeof(unsigned char)); /* next free mem */

		/*
		 * Init locks, semas ...
		 */
		init_lock(&ifd->sd_lock);
		init_sema(&ifd->sd_sema, 0);
		bufinit(&ifd->sd_rbufh);

		/*
		 * Init channel structure.
		 */
		ifd->sd_dc = dc = (struct seddc *)calloc(sizeof(struct seddc));		/* next free mem */

		/* 
		 * Device program for a request sense operation
		 * (separate to avoid fussing diq ptrs too much)
		 */
		dc->dc_sense = (struct sec_dev_prog *)calloc(sizeof(struct sec_dev_prog)); /* sense info */

		/*
		 * Set up the iat table area
		 */
		dc->dc_iat = (struct sec_iat *) calloc(sizeof(struct sec_iat) * bconf->bc_num_iat); /* next free mem */
		dc->dc_isz = bconf->bc_num_iat;
		dc->dc_ifree = dc->dc_isz;

		/*
		 * Device input queue.
		 */
		dc->dc_diq = sed->sd_requestq;
		SEC_fill_progq(dc->dc_diq, sed->sd_req_size, sizeof(struct sec_dev_prog));
		dc->dc_devp = dc->dc_diq->pq_un.pq_progs[0];
		dc->dc_dsz = sed->sd_req_size;
		dc->dc_dfree = dc->dc_dsz;
		ASSERT(dc->dc_dsz > 0, "sdboot: no device programs");
		/*
		 *+ There is a problem in initializing the
		 *+ sd driver.  The number of device programs
		 *+ configured for this device is not valid.
		 */

		/*
		 * Device Output queue.
		 */
		dc->dc_doq = sed->sd_doneq;

		/*
		 * Request sense information buffer,
		 * one per channel.
		 * Note: Assumes channel can only generate one fault
		 * at a time.
		 */
		ifd->sd_sensebuf = (unchar *)calloc(sizeof(unchar) * sdsensebuf_sz);
		ifd->sd_sensebufptr = SEC_rqinit(ifd->sd_sensebuf, sdsensebuf_sz);

#ifdef DEBUG
		if(sd_debug>1) {
			CPRINTF("diq0x%x,doq0x%x,cib0x%x,bin%d,device%d,vector%d, all at 0x%x\n",dc->dc_diq,
			dc->dc_doq, sed->sd_cib, sed->sd_bin, dev, sed->sd_vector);
			CPRINTF("info=0x%x, ifd=0x%x\n",ifd,sdifd[dev]);
		}
#endif	/* DEBUG  */
		/*
		 * Initialize the statistics 
		 */
		ifd->sd_dstats = ds_alloc_stats("sd", (ulong)ifd->sd_capacity,
			dev, DS_BLK_HIST);
		/* If we get here, device is O.K.--add it to devsw */
		sd_dev[dev].d_dev = makedev( sd_b_major,
					    unittominor(dev) | V_RAW_MINOR);
		sd_dev[dev].d_refcnt = 1;
		sd_dev[dev].d_flags = D_ASYNC;
		sd_dev[dev].d_ops = &sddevops;
		if ( devsw_add( &sd_dev[dev] ) < 0 ) {
			CPRINTF( "sd%d: can't add dev 0x%x to devsw\n",
			       dev, &sd_dev[dev]);
			continue;
		}
		dev_deref(&sd_dev[dev]);
	}
}

/*
 *	sdopen - open a channel
 *
 *	Check for validity of device and partition.
 *
 *	Tmp assumptions: No mods needed for TMP because sdopen doesn't
 *	*modify* any concurrently accessed common data structures.
 *
 *	This assumes that the device is formated in DEV_BSIZE size blocks
 *	and won't work other wise.
 */

sdopen(dp, flags)
	struct dev *dp;
	int flags;
{
	dev_t dev = dp->d_dev;
	register int unit;
	register struct sd_info	*ifd;
	int err = 0;
	spl_t s;

#ifdef DEBUG
	if(sd_debug)
		CPRINTF("O");
#endif	/* DEBUG  */
	unit = SCSID_DRIVE(dev);


	ifd = sdifd[unit];
	if ((ifd == NULL)		  /* Configured properly? */
	||  (unit >= sdmaxminor)	  /* Valid channel number? */
	|| (!ifd->sd_desc->sd_alive))	  /* Passed probing? */
		return(ENXIO);

	s = p_lock(&ifd->sd_lock, SDSPL);
	if (ifd->sd_flags & SDF_EXCLUSIVE) {
#ifdef DEBUG
		SDPRINTF("sdopen: exclusive set - EBUSY\n");
#endif /* DEBUG */
		err = EBUSY;
	} else if (flags & FEXCL) {
#ifdef DEBUG
		SDPRINTF("sdopen: FEXCL\n");
#endif /* DEBUG */
		if (!suser()) {
#ifdef DEBUG
			SDPRINTF("sdopen: !suser\n");
#endif /* DEBUG */
			err = EPERM;
		} else if (ifd->sd_nopen) {
#ifdef DEBUG
			SDPRINTF("sdopen: EBUSY\n");
#endif /* DEBUG */
			err = EBUSY;
		} else {
			ifd->sd_flags |= SDF_EXCLUSIVE;
		}
	}
	if (!err)
		ifd->sd_nopen++;
#ifdef DEBUG
	SDPRINTF("sdopen: dev 0x%x -- %d opens\n", dev, ifd->sd_nopen);
#endif /* DEBUG */
	v_lock(&ifd->sd_lock, s);
	return(err);
}

/*
 * sdclose - close a channel 
 *
 * Decrement reference count, make sure temporary settings
 * are set back to their default values.
 */
/*ARGSUSED*/
sdclose(dp, flag)
	struct dev *dp;
	int flag;
{
	dev_t dev = dp->d_dev;
	struct sd_info *ifd;
	spl_t s;

	ifd = sdifd[SCSID_DRIVE(dev)];
	s = p_lock(&ifd->sd_lock, SDSPL);
	ifd->sd_nopen--;
	if (ifd->sd_flags & SDF_EXCLUSIVE) {
		ifd->sd_flags &= ~SDF_EXCLUSIVE;
	}
	v_lock(&ifd->sd_lock, s);
#ifdef DEBUG
	SDPRINTF("sdclose: dev 0x%x - nopens = %d\n", dev, ifd->sd_nopen);
#endif /* DEBUG */
}

/*
 * sdminphys - correct for too large a request.
 *
 * Note: IATSIZE should reflect typical iat table entry size (currently 1k)
 */
/*ARGSUSED*/
sdminphys(dp, bp)
	struct dev *dp;
	register struct buf *bp;
{
	struct sd_info *ifd;
	struct seddc	*dc;

	ifd = sdifd[SCSID_DRIVE(bp->b_dev)];
	dc = ifd->sd_dc;

#ifdef DEBUG
	if(bp->b_bcount<=0) {
		CPRINTF("sdminphys: bad bp detected, bcount=%d\n", bp->b_bcount);
		bp->b_bcount = 0;
	}
#endif	/* DDEBUG  */
	
#ifdef SCSIFIRMWARE_SANITY 

	/*
	 * The iat size is enough except for the use of an extra
	 * iat used for debug in sdiatsz() for driver fault firmware
	 * tests.
	 *
	 * Added space for an iat entry so that #define SCSIFIRWARE_SANITY
	 * doesn't deadlock on iat allocation in sdstart call.
	 *
	 * buf_iat(bp)+2	1 for scsifirmwaresanity 1 for deadlock
	 * dc_isz-IATVARIANCE-4 (above adds total of 4) plus 1 so that
	 *			non-aligned xfers don't get rounded up.
	 */
	if(buf_iatsz(bp)+2 >= dc->dc_isz)		
		bp->b_bcount = (dc->dc_isz-IATVARIANCE-4)*IATBYTES;
#else

	/*
	 * Waste an extra couple of iat's to insure driver doesn't
	 * deadlock if a request tries to allocate the entire iat
	 * list to a single request.
	 */
	if(buf_iatsz(bp)+1 >= dc->dc_isz)		
		bp->b_bcount = (dc->dc_isz-IATVARIANCE-3)*IATBYTES;
#endif	/* SCSIFIRMWARE_SANITY  */

#ifdef DEBUG
	if(sd_debug>1) 
		CPRINTF("sdminphys: cnt=%d, ni=%d\n", bp->b_bcount, buf_iatsz(bp));
	else if(sd_debug)
		CPRINTF("M");
#endif	/* DEBUG  */

	/*
	 * Limit the xfer (because of SCSI command spec)
	 * to no more that 255 blocks
	 * at a time (note: 256 blocks are possible).
	 * 255 limit is based on paranoia. A zero count
	 * in the command structure results in xfer of 256 blocks.
	 */
	if(bp->b_bcount > 255*DEV_BSIZE)
		bp->b_bcount = 255*DEV_BSIZE;	
}


/*
 * sdwrite	-	Standard raw write procedure.
 */
sdwrite(dp, uio, flags)
	struct dev *dp;
	struct uio *uio;
	int flags;
{
	dev_t dev = dp->d_dev;
	int err, diff;
	off_t lim;

#ifdef DEBUG
	ASSERT(SCSID_DRIVE(dev) <sdmaxminor, "sdwrite dev");
	if(sd_debug>1)
		CPRINTF("Dev=0x%x\n", dev);
#endif	/* DEBUG  */

		lim = sdifd[SCSID_DRIVE(dev)]->sd_capacity;
	lim <<= DEV_BSHIFT;
	err = physck(lim, uio, B_WRITE, &diff);
	if (err != 0) {
		if (err == -1)	/* not an error, but request of 0 bytes */
			err = 0;
		return(err);
	}
	if (flags & IO_ASYNC) {
		err = aphysio(dp, B_WRITE, uio, diff, flags);
	} else {
		err = physio(dp, (struct buf *)0, B_WRITE, uio);
		uio->uio_resid += diff;
	}
	return(err);
}


/*
 * sdread	-	Standard raw read procedure.
 */
sdread(dp, uio, flags)
	struct dev *dp;
	struct uio *uio;
	int flags;
{
	dev_t dev = dp->d_dev;
	int err, diff;
	off_t lim;

#ifdef DEBUG
	ASSERT(SCSID_DRIVE(dev) < sdmaxminor, "sdread dev");
	if(sd_debug>1)
		CPRINTF("Dev=0x%x\n", dev);
#endif	/* DEBUG  */

		lim = sdifd[SCSID_DRIVE(dev)]->sd_capacity;
	lim <<= DEV_BSHIFT;
	err = physck(lim, uio, B_READ, &diff);
	if (err != 0) {
		if (err == -1)	/* not an error, but request of 0 bytes */
			err = 0;
		return(err);
	}
	if (flags & IO_ASYNC) {
		err = aphysio(dp, B_READ, uio, diff, flags);
	} else {
		err = physio(dp, (struct buf *)0, B_READ, uio);
		uio->uio_resid += diff;
	}
	return(err);
}


/*
 * sdstrat - SCSI strategy routine.
 *	check the block sizes and limits.
 *
 * TMP assumptions:
 *	Only one processor can access the sd_info structure for the
 *	state variable on each devices channel. Interrupts will block
 *	and spin on this lock for each channel. 
 *
 *	Concurrent interrupts on different channels is assumed possible.
 */
/*ARGSUSED*/
sdstrat(dp, bp, offset)
	struct dev *dp;
	register struct	buf	*bp;
	daddr_t	offset;
{
	register struct	sd_info	*ifd;
	register struct	buf *bufh;
	int	x;

	bp->b_status = B_STATUS_OK;	  /* default status */
	ifd = sdifd[SCSID_DRIVE(bp->b_dev)];

#ifdef DEBUG
	if(sd_debug >1) {
		CPRINTF("ifd=0x%x, un=%d ", ifd, SCSID_DRIVE(bp->b_dev));
		CPRINTF("sdstrat(%s) bp=0x%x, dev=0x%x, cnt=%d, blk=0x%x, vaddr=0x%x\n",
			(bp->b_flags & B_READ) ? "READ" : "WRITE",
			bp, bp->b_dev, bp->b_bcount, bp->b_blkno + offset,
			bp->b_un.b_addr);
		ASSERT(ifd!=0,"sdstrat: ifd ptr zero");
		/*
		 *+ The system is attempting to do I/O on a
		 *+ device which doesn't exist or isn't configured.
		 *+ The system is in a bad internal state.
		 */
	} else if(sd_debug)
		CPRINTF("%s", (bp->b_flags & B_READ) ? "r" : "w");
#endif	/* DEBUG  */

	bufh = &ifd->sd_bufh;

	/*
	 * Size and partitioning check.
	 *
	 * Fail request if bogus byte count, if address not aligned to
	 * SD_ADDRALIGN boundary, or if transfer is not entirely within a
	 * disk partition.
	 */
	if (bp->b_bcount <= 0
	||  (bp->b_bcount & DEV_BSIZE - 1) != 0
	||  bp->b_blkno + offset + howmany(bp->b_bcount, DEV_BSIZE)
							> ifd->sd_capacity
	||  (bp->b_iotype == B_RAWIO 
	     && ((int)bp->b_un.b_addr & SD_ADDRALIGN - 1) != 0)) {
		bp->b_resid = 0;		/* must be 0 when done! */
		bp->b_flags |= B_ERROR;
		bp->b_error = EINVAL;
		iodone(bp);
		return;
	}

	bp->b_disksortkey = bp->b_blkno + offset;	/* for disksort */

	/*
	 * Note: To this point in the procedure only static data
	 * has been accessed.
	 */
	 
	x = p_lock(&ifd->sd_lock, SDSPL);
	disksort(bufh, bp);
	DS_QUEUED_TIME(bp);
	if(bufh->b_active == SDS_IDLE) {
		bufh->b_active = SDS_BUSY;
		sdstart(bufh, ifd);
	}
	DS_Q(ifd->sd_dstats);
	v_lock(&ifd->sd_lock, x);
}

/*
 * sdstart - start a request to a channel.
 *
 * TMP concerns - bufh access mutexed above.
 * Assumptions:
 *	device program pointers are filled in at boot time
 *	iat table pointers in ifd->sd_dc->dc_iat are filled in at boot time.
 *
 */

sdstart(bufh, ifd)
	register struct buf *bufh;
	register struct sd_info *ifd;
{
	register struct	seddc	*dc;		/* Device control block */
	register struct	sec_dev_prog *devp;	/* Device program pointer */
	register struct	buf	*bp;		/* First request from system */
	struct	buf		*qbp;		/* Last request made to device*/
	struct	sec_iat		*iat;		/* Base iat pointer */
	struct	sec_progq	*diq;		/* Device input queue */
	unsigned char		head;		/* Diq head value */
	int	numiat;				/* Number of iats needed */
	int	numqueue;			/* Number taken off each start */
	int	dstart;				/* Begining of empty slots */


	if(bufh->b_actf == NULL)		/* Check for requests */
		return;
	
	numqueue = ifd->sd_thresh;

	qbp = &ifd->sd_bp;
	dc = ifd->sd_dc;
	dstart = dc->dc_dfree;
	
	/*
	 * We have at least one request to the disk so start it up.
	 * Does not assume there is space for a request in the input queue.
	 */
	for(;;)	{					/* ever */
		/*
		 * All completed?
		 */
		if((bp=bufh->b_actf) == NULL)		/* No more to queue */
			break;

#ifdef DEBUG
	if(sd_debug>1) 
		CPRINTF("bp=0x%x,ifd=0x%x,bufh=0x%x,bp=0x%x\n",bufh->b_actf, ifd ,bufh, bp);
#endif	/* DEBUG  */

		/*
		 * See if there is room on the devices input queue
		 * and allocate the number of iat's needed for this
		 * request.
		 */
		if(dc->dc_dfree<=2) {
			ASSERT(qbp->b_actf != NULL, "sd: deadlock, no room & no requests");
			/*
			 *+ The sd driver is in a bad internal state.
			 *+ Although there are no outstanding requests,
			 *+ all of its device programs appear to be in use.
			 */
			ASSERT(dc->dc_dfree>=0, "sd: dfree < zero");
			/*
			 *+ The sd driver is in a bad internal state;
			 *+ its count of free device programs
			 *+ is less than zero. 
			 */

			/*
			 * Check to see if the device has plenty of devp's 
			 * in the queue and if the queue is full wait till 
			 * it drains down so that disksorting is even more 
			 * affective.
			 */
			ifd->sd_flags |= SDF_NOSTART;
#ifdef DEBUG
			if(sd_debug)
				CPRINTF("F");
#endif	/* DEBUG  */
			break;
		}
		
		/* 
		 * Allocate the iat's and handle wrap around.
		 */
		numiat = sdiatsz(bp, dc);
		if(numiat==0) {
#ifdef DEBUG
			if(sd_debug)
				CPRINTF("i");
#endif	/* DEBUG  */
			break;				/* Not enough for this one */
		}
		
		/*
		 * Mark the idle to non idle time stamp on all units.
		 */
		if(dc->dc_dfree == dc->dc_dsz)	 	/* all device programs free */
			DS_IOSTART_TIME(ifd->sd_dstats);
			
		dc->dc_dfree--;				/* Allocate a device program */
#ifdef DEBUG
		if((dc->dc_dfree+1) == dc->dc_dsz)	
			ASSERT(qbp->b_actf == NULL, "sdstart: qbp Non NULL");
#endif	/* DEBUG  */

		/*
		 * Seems there is room on the input queue, so do this request.
		 */
		diq = dc->dc_diq;
		iat = dc->dc_istart;
		head = diq->pq_head;
		devp = (diq->pq_un.pq_progs)[head];
#ifdef DEBUG
	if(sd_debug>2)
		CPRINTF("devp=0x%x diq=0x%x head=%d numiat=%d\n", devp, diq, head, numiat);
#endif	/* DEBUG  */
		


		/*
		 * fill out the iat and the device program to be loaded
		 */
		if (!(bp->b_flags & B_IOCTL && bp->b_bcount == 0))
			buf_iat(bp, iat, numiat);
		devp->dp_un.dp_iat = (struct sec_iat *)((int)iat | SEC_IAT_FLAG);
		devp->dp_data_len = roundup(bp->b_bcount, DEV_BSIZE);
		devp->dp_cmd_len = (bp->b_flags&B_IOCTL) ? 
				   SCSI_IOCTLLEN(bp) : SCSI_CMD6SZ;
#ifdef DEBUG
		if (bp->b_flags & B_IOCTL)
			SDPRINTF("sdstart: cmdlen = %d\n", devp->dp_cmd_len);
#endif /* DEBUG */
		devp->dp_status1 = NULL;
		devp->dp_status2 = NULL;

		ASSERT(devp->dp_next==NULL,"sd: next_com ");
		/*
		 *+ The device program indicates that another command
		 *+ is linked.  This functionality is not currently
		 *+ supported by the driver.
		 */

		/*
		 * The SCSI command itself.
		 */
		if(bp->b_flags & B_IOCTL) {		/* SCSI command */
			bcopy((caddr_t)
			      (&((struct scsiioctl *)bp->b_sioctl)->sio_cmd),
			      (caddr_t)devp->dp_cmd,
			      (uint)devp->dp_cmd_len);
#ifdef DEBUG
			SDPRINTF("sdstart: dp_cmd = 0x%x\n", devp->dp_cmd[0]);
#endif /* DEBUG */
		}else{
			ASSERT(bp->b_bcount>0, "sd: bcount");
			/*
			 *+ The sd driver received an I/O request 
			 *+ with a count
			 *+ of zero.  This is illegal.  
			 */
			ds_access_stats(ifd->sd_dstats,
					(ulong)bp->b_disksortkey);
			devp->dp_cmd[0]  =  (bp->b_flags&B_READ) ? SCSI_READ : SCSI_WRITE;	/* install cmd */
			devp->dp_cmd[1] = ifd->sd_lun | (unchar)((int)bp->b_disksortkey >> 16);	/* msb blk number */
			devp->dp_cmd[2] = (unchar)((int)bp->b_disksortkey >> 8);
			devp->dp_cmd[3] = (unchar)(bp->b_disksortkey);				/* lsb blk number */
			devp->dp_cmd[4] = (unchar)howmany(bp->b_bcount,DEV_BSIZE);			/* number of blks */
			devp->dp_cmd[5] = 0;							/* status, always 0 */
		}
		
		/*
		 * Increment the device input queue.
		 */
		diq->pq_head = (head+1) % dc->dc_dsz;

		/* 
		 * dequeue this request from the main buffer queue
		 */
#ifdef DEBUG
	if(sd_debug>3)
		CPRINTF("qbp=0x%x, b_actf=0x%x, b_actl=0x%x, bp=0x%x, f=0x%x, l=0x%x\n",
			qbp, qbp->b_actf, qbp->b_actl, bp, bp->b_actf, bp->b_actl);
	else if(sd_debug)
		CPRINTF("s");
#endif	/* DEBUG  */
		if(qbp->b_actf == NULL)
			qbp->b_actf = bp;
		else
			qbp->b_actl->b_actf = bp;
		qbp->b_actl = bp;
		bufh->b_actf = bp->b_actf;
		bp->b_actf = NULL;			/* Interrupt sanity */
#ifdef DEBUG
		if(bufh->b_actf == NULL)		/* XXX */
			ASSERT(qbp->b_actf != NULL, "sdstart:qbp NULL bufh NULL");
#endif	/* DEBUG  */

		if(--numqueue <= 0)			/* Only take sd_thresh at a time, and at least one! */ 
			break;
	}
	
	/*
	 * To kick or not to kick, that is the question...
	 *
	 * Here we decide if there is a need to interrupt
	 * the board. Here send an interrupt on the first
	 * two queued request then none until it drains off.
	 */
	if(dstart+1 >=dc->dc_dsz) {

		ifd->sd_stat = 0;			/* zap return status */

		/*
		 * Start the request's out.
		 */
		sec_startio(	SINST_STARTIO, 		/* command */
				&ifd->sd_stat,		/* return status loc */
				ifd->sd_desc
			);

		if(ifd->sd_stat != SINST_INSDONE) {
			cmn_err(CE_NOTE, "sd: Device Status Error 0x%x SCED dev#=0x%x\n",
				ifd->sd_stat,	ifd->sd_desc->sd_chan
			);
			/*
			 *+ The SCED returned a bad completion
			 *+ status on the specified command.
			 */
			cmn_err(CE_PANIC, "sd: SCED hard error status");
			/*
			 *+ The operation being performed received a bad completion status 
			 *+ from the SCED.  This
			 *+ indicates a hard SCED error.
			 */

		}
	}
}

/*
 * sdiatsz(bp, dc) -	get the number of iats needed for this request
 *			and adjust the iat queue.
 * returns the number of iat's used.
 */
int
sdiatsz(bp, dc)
	register struct buf *bp;
	register struct seddc *dc;
{
	register int tail;
	register int needed;
	int	head;

	needed = buf_iatsz(bp);		/* How many needed for this request */
#ifdef SCSIFIRMWARE_SANITY
	needed++;
#endif	/* SCSIFIRMWARE_SANITY  */
	/* 
	 * check for wraparound.
	 */
	head = dc->dc_ihead;
	tail = dc->dc_itail;

	/*
	 * If the head and tail pointers are equal then
	 * start over at the beginning to handle the largest
	 * size request generated by minphys. This allows
	 * the device to block waiting for iats to freeup
	 * because they aren't contiguous.
	 */
	if(head==tail) 
		head = tail = dc->dc_ihead = dc->dc_itail = 0;
#ifdef DEBUG
	if(head==tail)
		ASSERT(dc->dc_ifree==dc->dc_isz, "head says free");
#endif	/* DEBUG  */
	

	/*
	 * See if there is enough room.
	 * Note: It is possible to have a case
	 * where the number of iat's goes off the
	 * end and even though there are enough iats
	 * for the request the iat's aren't contiguous,
	 * the second test handles this.
	 */
	if( dc->dc_ifree<=needed
	|| ((tail + needed > dc->dc_isz)
	&& (needed >= head))) {
#ifdef DEBUG
		if(sd_debug>1)
			CPRINTF("ifree %d<%d needed\n", dc->dc_ifree, needed);
#endif	/* DEBUG  */
		return(0);		/* Not enough */
	}
	
	if(tail+needed > dc->dc_isz) {
		dc->dc_ifree -= (dc->dc_isz - tail + needed);
		tail = needed;		/* Wrap it around */
		dc->dc_istart = (struct sec_iat *)&(dc->dc_iat)[0];
	}else{
		tail = (dc->dc_itail+needed) % dc->dc_isz;
		dc->dc_istart = (struct sec_iat *)&(dc->dc_iat)[dc->dc_itail];/* tell the start */
		dc->dc_ifree -= needed;
	}
	dc->dc_itail = tail;			/* reflect the allocation */

#ifdef SCSIFIRMWARE_SANITY
	/*
	 * zero all iat's before filling them in to insure that the last one
	 * contains a count of zero for the firmware sanity to be enabled.
	 */
	bzero((char *) dc->dc_istart, (uint) needed*sizeof(struct sec_iat));
#endif	/* SCSIFIRMWARE_SANITY  */

#ifdef DEBUG
	if(sd_debug>1)
		CPRINTF("Need %d iat's starting at 0x%x\n", needed, dc->dc_istart);
	ASSERT(dc->dc_itail<dc->dc_isz, "sdiat: iat overflow");
#endif	/* DEBUG  */

	return(needed);
}

/*
 * sdiatfree(bp, dc) -	free the number of iats needed for this request
 *			and adjust the iat queue.
 * Assumes that this is in sync with sdiatsz above for wrap around.
 */
sdiatfree(bp, dc)
	register struct buf *bp;
	register struct seddc *dc;
{
	register int free;
	register int head;

	free = buf_iatsz(bp);		/* How many needed for this request */
#ifdef SCSIFIRMWARE_SANITY
	free++;
#endif	/* SCSIFIRMWARE_SANITY  */
	/* 
	 * check for wraparound.
	 */
	head = dc->dc_ihead;
	
	if(head+free > dc->dc_isz) {
		dc->dc_ifree += (dc->dc_isz - head + free);
		head = free;		/* Wrap it around */
	}else{
		head = (head+free) % dc->dc_isz;
		dc->dc_ifree += free;
	}
	dc->dc_ihead = head;			/* reflect the allocation */

#ifdef DEBUG
	if(sd_debug>1)
		CPRINTF("freed %d iat's\n", free);
	ASSERT(dc->dc_ihead<dc->dc_isz, "sdiatfree: iat underflow");
	ASSERT(dc->dc_ifree<=dc->dc_isz,"sdintr: ring overrun");
	ASSERT(dc->dc_ifree>=0,"sdintr: ring underflow");
#endif	/* DEBUG  */
}

#undef SCSIFIRMWARE_SANITY

/*
 * sdintr(level) - interrupt routine.
 *
 * While there are outstanding requests that have completed
 * service and complete them.
 */

void
sd_intr( level)
	int	level;
{
	register struct sd_info *ifd = sdifd[level-sd_baselevel];
	register struct seddc *dc;
	register struct	buf *bp;
	register struct sec_progq *doq;
	register struct sec_dev_prog *devp;
	struct	buf *qbp;
	struct	buf *bufh;
	int	tail;
	spl_t	x;

#ifdef DEBUG
	int	dfreexxx;		/* XXXX */

	ASSERT(level-sd_baselevel>=0, "sdintr: config bad, level negative check baselevel");
	ASSERT(level-sd_baselevel<sdmaxminor, "sdintr: config bad, vec maps no-device");
	if(sd_debug>2)
		CPRINTF("sdintr: ifd=0x%x, level %d\n",ifd, level-sd_baselevel);
	else if(sd_debug)
		CPRINTF("I");
#endif	/* DEBUG  */


	ASSERT(ifd!=0,"sdintr: interrupt to non-configured device");
	/*
	 *+ The sd driver received an interrupt from a
	 *+ nonconfigured device.  This can indicate
	 *+ that the system interrupt vector table has
	 *+ been corrupted.
	 */

	qbp = &ifd->sd_bp;
	dc = ifd->sd_dc;
	doq = dc->dc_doq;
	bufh = &ifd->sd_bufh;

	x = p_lock(&ifd->sd_lock, SDSPL);

#ifdef DEBUG
	for(dfreexxx=dc->dc_dfree, bp=qbp->b_actf; dfreexxx<dc->dc_dsz; bp=bp->b_actf,dfreexxx++)						/* XXXX */
		ASSERT(bp != NULL, "sdintr:Lost a bp");		/* XXXX */
	ASSERT(bp==NULL,"sdintr:Extra bps");			/* XXXX */
#endif	/* DEBUG  */


	for(;;)	{		/* ever */
		
		if((bp = qbp->b_actf) == NULL)	/* current request */
			break;				/* all done... */

#ifdef DEBUG
		if (bp->b_flags & B_IOCTL)
			SDPRINTF("<I>\n");
#endif /* DEBUG */
		if(doq->pq_tail == doq->pq_head)	/* All done */
			break;

		tail = doq->pq_tail;
		devp = (doq->pq_un.pq_progs)[tail];
		doq->pq_tail = (tail+1) % dc->dc_dsz;	/* tail ptr */
		/*
		 * Check for command completion
		 */
		if(devp->dp_status1 != SEC_ERR_NONE) {
			cmn_err(CE_NOTE, "sdintr: status1=%d\n", devp->dp_status1);
			/*
			 *+ An error occurred on a SCSI command
			 *+ from the sd driver.
			 */

			/*
			 * Insert a rezero unit command iff it is known that
			 * this command will not hard error the device and the
			 * command is not a rezero command!
			 */
			if(ifd->sd_retrys > 0 && devp->dp_cmd[0] != SCSI_REZERO)
				sd_rezero_insert(dc->dc_diq, devp->dp_cmd[1]&SDLUNMSK, dc->dc_dsz);
			ifd->sd_savedp = devp;		/* save it to print it later */
			ifd->sd_flags |= SDF_SENSE;	/* DO request sense */
			devp->dp_status1 = 0;		/* remove error condition */
			/*
			 * fill in request sense dp and issue
			 * a request sense command.
			 *
			 * Todo: move next 9 lines to sdboot()
			 * because it's static (get it working first!)
			 */
			ifd->sd_statb.rs_dev_prog.dp_un.dp_data = (unchar *)ifd->sd_sensebufptr;
			ifd->sd_statb.rs_dev_prog.dp_next = 0;
			ifd->sd_statb.rs_dev_prog.dp_count = 0;
			ifd->sd_statb.rs_dev_prog.dp_data_len = sdsensebuf_sz;
			ifd->sd_statb.rs_dev_prog.dp_cmd_len = 6;
			ifd->sd_statb.rs_dev_prog.dp_status1 = 0;
			ifd->sd_statb.rs_dev_prog.dp_status2 = 0;
			ifd->sd_statb.rs_dev_prog.dp_cmd[0] = SCSI_RSENSE; 
			ifd->sd_statb.rs_dev_prog.dp_cmd[4] = (unchar)sdsensebuf_sz;
			ifd->sd_statb.rs_dev_prog.dp_cmd[1] = devp->dp_cmd[1] & SDLUNMSK; /* Masked lun in */
			ifd->sd_statb.rs_status = 0;

			sec_startio( SINST_REQUESTSENSE,
				(int	*)&ifd->sd_statb,
				ifd->sd_desc
			);
			v_lock(&ifd->sd_lock, x);
			return;
		}

		if(ifd->sd_flags & SDF_SENSE) {
			ifd->sd_flags &= ~SDF_SENSE;
#ifdef DEBUG
			if(sd_debug) {
				int	i;

				CPRINTF("Request sense bytes: ");
				for(i=0; i<sdsensebuf_sz; i++)
					CPRINTF("0x%x ", ifd->sd_sensebuf[i]);
				CPRINTF("\n");
				CPRINTF("Device prog: ");
				for(i=0; i<6; i++)
					CPRINTF("0x%x ", devp->dp_cmd[i]);
				CPRINTF("\n");
			}
#endif	/* DEBUG  */
			devp->dp_status1 = (unchar)(((ifd->sd_sensebuf[0]&RS_ERRCLASS) == RS_CLASS_EXTEND) ? ifd->sd_sensebuf[2]:ifd->sd_sensebuf[0]);

#ifdef notdef
			if (((ifd->sd_sensebuf[0] & RS_ERRCLASS) == RS_CLASS_EXTEND) &&
			  ((ifd->sd_sensebuf[2] & RS_SENSEKEYMASK) == RS_RECERR)) {

				/*
				 * recovered error on CCS disks.  Means the
				 * drive discovered an error, but corrected it,
				 * and we need not try it again.
				 */
				cmn_err(CE_WARN, 
"Soft error dev <0x%x,0x%x>, blk=0x%x, cnt=%d,\n err=0x%x (drive recovered)\n",
					major(bp->b_dev), minor(bp->b_dev),
					bp->b_disksortkey, bp->b_bcount,
					devp->dp_status1
				);
				/*
				 *+ An I/O error occurred on the sd device
				 *+ whose major and minor numbers are shown.
				 *+ The drive was able to correct the 
				 *+ error and recover valid data.
				 */

				/*
				 * Print out all information about
				 * the error as possible.
				 */
				{
					int	i, j;
					
					i = sdsensebuf_sz;
					cmn_err(CE_CONT, "LBA: %d\n",
						XgetLBA(ifd->sd_sensebuf));
					cmn_err(CE_CONT, "Request sense bytes: ");
					for(j=0; j<i; j++)
						cmn_err(CE_CONT, "0x%x ", ifd->sd_sensebuf[j]);
					cmn_err(CE_CONT, "\n");
					/*
					 * print out the error'ing device program
					 */
					cmn_err(CE_CONT, "Device prog: ");
					for(i=0; i<6; i++)
						cmn_err(CE_CONT, "0x%x ", ifd->sd_savedp->dp_cmd[i]);
					cmn_err(CE_CONT, "\n");
				}

				devp->dp_status1 = SEC_ERR_NONE; /* fake it */

				bp->b_status |= B_S_MEDIA_ERR;

				sec_startio( SINST_RESTARTIO,
					&ifd->sd_stat,
					ifd->sd_desc
				);
			} else
#endif	/* notdef  */
			if(ifd->sd_retrys--) {		/* retry it */

				cmn_err(CE_WARN, 
     "Soft error dev <0x%x,0x%x>, blk=0x%x, cnt=%d,\n err=0x%x (will retry)\n",
					major(bp->b_dev), minor(bp->b_dev),
					bp->b_disksortkey, bp->b_bcount,
					devp->dp_status1
				);
				/*
				 *+ An I/O error occurred on the sd device
				 *+ whose major and minor numbers are shown.
				 *+ The command that resulted in the error 
				 *+ will be retried.
				 */

				/*
				 * Print out all information about
				 * the error as possible.
				 */
				{
					int	i, j;
					
					if((ifd->sd_sensebuf[0]&RS_ERRCLASS) == RS_CLASS_EXTEND){
						i = sdsensebuf_sz;
						cmn_err(CE_CONT, "LBA: %d\n",
							XgetLBA(ifd->sd_sensebuf));
					}else{
						i = 4;
						cmn_err(CE_CONT, "LBA: %d\n",
							getLBA(ifd->sd_sensebuf));
					}
					cmn_err(CE_CONT, "Request sense bytes: ");
					for(j=0; j<i; j++)
						cmn_err(CE_CONT, "0x%x ", ifd->sd_sensebuf[j]);
					cmn_err(CE_CONT, "\n");
					/*
					 * print out the error'ing device program
					 */
					cmn_err(CE_CONT, "Device prog: ");
					for(i=0; i<6; i++)
						cmn_err(CE_CONT, "0x%x ", ifd->sd_savedp->dp_cmd[i]);
					cmn_err(CE_CONT, "\n");
				}

				devp->dp_status1 = SEC_ERR_NONE; /* fake it */
				sec_startio( SINST_RESTARTCURRENTIO,
					&ifd->sd_stat,
					ifd->sd_desc
				);

				v_lock(&ifd->sd_lock, x);
				return;
			}else{
				cmn_err(CE_WARN, "Hard error dev <0x%x,0x%x>, blk=0x%x, cnt=%d err=0x%x\n",
					major(bp->b_dev), minor(bp->b_dev),
					bp->b_disksortkey, bp->b_bcount,
					devp->dp_status1
				);
				/*
				 *+ On an sd device, either a hard 
				 *+ error occurred or a soft error 
				 *+ that could not be 
				 *+ corrected by retrying the command occurred
				 *+ (retry limit exceeded).  Corrective action:  consider
				 *+ adding the offending block to the
				 *+ defect list for the device.
				 */

				/*
				 * Print out all information about
				 * the error as possible.
				 */
				{
					int	i, j;
					
					if((ifd->sd_sensebuf[0]&RS_ERRCLASS) == RS_CLASS_EXTEND){
						i = sdsensebuf_sz;
						cmn_err(CE_CONT, "LBA: %d\n",
							XgetLBA(ifd->sd_sensebuf));
					}else{
						i = 4;
						cmn_err(CE_CONT, "LBA: %d\n",
							getLBA(ifd->sd_sensebuf));
					}
					cmn_err(CE_CONT, "Request sense bytes: ");
					for(j=0; j<i; j++)
						cmn_err(CE_CONT, "0x%x ", ifd->sd_sensebuf[j]);
					cmn_err(CE_CONT, "\n");
					/*
					 * print out the error'ing device program
					 */
					cmn_err(CE_CONT, "Device prog: ");
					for(i=0; i<6; i++)
						cmn_err(CE_CONT, "0x%x ", ifd->sd_savedp->dp_cmd[i]);
					cmn_err(CE_CONT, "\n");
				}

				devp->dp_status1 = SEC_ERR_NONE; /* fake it */

				/* 
				 * Uncorrectable error occured.
				 */
				bp->b_flags |= B_ERROR;
				bp->b_status |= B_S_XFER_ERR;
				sec_startio( SINST_RESTARTIO,
					&ifd->sd_stat,
					ifd->sd_desc
				);
			}
		}

		devp->dp_status1 = SEC_ERR_NONE;		/* Paranoid */

		/*
		 * Avoid looping forever on retries.
		 */
		if(devp->dp_cmd[0] == SCSI_REZERO)
			continue;

		ifd->sd_retrys = sdretrys;
		/*
		 * Adjust queues
		 */
		qbp->b_actf = bp->b_actf;
		bp->b_actf = 0;
		dc->dc_dfree++;

		DS_IODONE_TIME(ifd->sd_dstats);
		
		sdiatfree(bp, dc); 		/* free used iats */

		bp->b_resid = 0;
#ifdef DEBUG
		if (bp->b_flags & B_IOCTL)
			SDPRINTF("<D>\n");
#endif /* DEBUG */
		ds_completion_stats(ifd->sd_dstats, bp);
		iodone(bp);

	}	/* end forever loop */
	
	/*
	 * If non-startable check to see if its time to start again
	 * else Start any out standing requests
	 */
	if(ifd->sd_flags & SDF_NOSTART) {
		if((dc->dc_dsz - dc->dc_dfree) <= ifd->sd_low) {
			ifd->sd_flags &= ~SDF_NOSTART;
#ifdef DEBUG
			SDPRINTF("<S>\n");
#endif /* DEBUG */
			sdstart(bufh, ifd);
		}
	}else{
#ifdef DEBUG
		SDPRINTF("<S>\n");
#endif /* DEBUG */
		sdstart(bufh, ifd);
	}

	/*
	 * Device will remain busy until bufhq is sucked dry
	 */
	if(qbp->b_actf == NULL)
		bufh->b_active = SDS_IDLE;

	v_lock(&ifd->sd_lock, x);
}

/*
 * sdioctl
 */
/* ARGSUSED */
sdioctl(dp, cmd, addr, flag)
	struct dev *dp;
	int	cmd;
	caddr_t	addr;
	int 	flag;
{
	dev_t	dev = dp->d_dev;
	struct sd_info *ifd;
	struct scsiioctl sioctl;
	struct scsidev scsidev;

	ifd = sdifd[SCSID_DRIVE(dev)];

	switch (cmd) {

	case RIOFIRSTSECT: {
		int firstsect = 0;

		if (copyout((caddr_t)&firstsect, addr, sizeof(int)))
			return(EFAULT);
		return(0);
	}

	case RIODRIVER:		/* success indicates we're a driver! */
		return(0);

	case SIOCDEVDATA:
#ifdef DEBUG
		SDPRINTF("sdioctl: SIOCDEVDATA\n");
#endif /* DEBUG */
		scsidev.scsi_devno = (ifd->sd_desc->sd_target << 3)
				     | ifd->sd_desc->sd_unit;
		scsidev.scsi_ctlr = ifd->sd_desc->sd_sec_idx;
		if (copyout((caddr_t)&scsidev, (caddr_t)addr, 
			     sizeof(struct scsidev)))
			return(EFAULT);
		return(0);

		
	case SIOCSCSICMD:
#ifdef DEBUG
		SDPRINTF("sdioctl: SCSICMD\n");
#endif /* DEBUG */
		if (!suser())
			return(EACCES);
		/* get ioctl args */
		if (copyin((caddr_t)addr, (caddr_t)&sioctl,
			   sizeof(struct scsiioctl)))
			return(EFAULT);
		/* SCSI commands */
		return(sd_ioctlcmd(dp, ifd, &sioctl));

	default:
#ifdef DEBUG
		SDPRINTF("sdioctl: default -- EINVAL\n");
#endif /* DEBUG */
		return(EINVAL);		/* not supported yet */

	} /* end switch */
	/* NOTREACHED */
	return(EINVAL);
}

static
sd_ioctlcmd(dp, ifd, sio)
	struct dev *dp;
	struct sd_info *ifd;
	struct scsiioctl *sio;
{
	struct buf *bp;			/* ioctl buffer */
	int dir = 0;			/* zero == READ */
	int error;
	spl_t s;

	bp = &ifd->sd_rbufh;

	/* make sure command is for the current device! */
	if (SCSI_UNIT(sio->sio_cmd6.cmd_lun >> SCSI_LUNSHFT) != 
		SCSI_UNIT(ifd->sd_lun >> SCSI_LUNSHFT)) {
#ifdef DEBUG
		SDPRINTF("sd_ioctl: bad lun 0x%x - for 0x%x\n",
			  sio->sio_cmd6.cmd_lun, ifd->sd_lun);
#endif /* DEBUG */
		return(EINVAL);
	}
	if ((int)sio->sio_datalength < 0) {		/* invalid parm */
#ifdef DEBUG
		SDPRINTF("sd_ioctl: negative data length\n");
#endif /* DEBUG */
		return(EINVAL);
	}

	switch(sio->sio_cmd6.cmd_opcode) {

	case SCSI_FORMAT:
#ifdef DEBUG
		SDPRINTF("sd_ioctlcmd: SCSI_FORMAT\n");
#endif /* DEBUG */
		sio->sio_cmdlen = SCSI_CMD6SZ;
		break;

	case SCSI_INQUIRY:
#ifdef DEBUG
		SDPRINTF("sd_ioctlcmd: SCSI_INQUIRY\n");
#endif /* DEBUG */
		sio->sio_cmdlen = SCSI_CMD6SZ;
		break;

	case SCSI_READC:
#ifdef DEBUG
		SDPRINTF("sd_ioctlcmd: SCSI_READC\n");
#endif /* DEBUG */
		sio->sio_cmdlen = SCSI_CMD10SZ;
		break;

	case SCSI_REASS:
#ifdef DEBUG
		SDPRINTF("sd_ioctlcmd: SCSI_REASS\n");
#endif /* DEBUG */
		sio->sio_cmdlen = SCSI_CMD6SZ;
		dir = 1;
		break;

	case SCSI_READ_DEFECTS:
#ifdef DEBUG
		SDPRINTF("sd_ioctlcmd: SCSI_READ_DEFECTS\n");
#endif /* DEBUG */
		sio->sio_cmdlen = SCSI_CMD10SZ;
		break;

	default:
#ifdef DEBUG
		SDPRINTF("sd_ioctlcmd: default - not supported yet\n");
#endif /* DEBUG */
		return(EINVAL);

	} /* end switch */

#ifdef DEBUG
	SDPRINTF("addr = 0x%x, datalength = %d\n", sio->sio_addr,
		  sio->sio_datalength);
#endif /* DEBUG */
	/*
	 * Make sure user has access to specified data buffer
	 */
	if (sio->sio_datalength)
		if (!useracc((char *)sio->sio_addr,
			     (uint)sio->sio_datalength,
			     ((dir) ? B_WRITE : B_READ))) {
#ifdef DEBUG
			SDPRINTF("sd_ioctl: no user access\n");
#endif /* DEBUG */
			return(EFAULT);
		}

	/*
	 * Allocate buffer and set fields
	 */
	bufalloc(bp);
	bp->b_flags = ((dir) ? B_WRITE : B_READ) | B_IOCTL;
	bp->b_bcount = sio->sio_datalength;
	bp->b_un.b_addr = (caddr_t)sio->sio_addr;
	bp->b_sioctl = (long)sio;
	bp->b_devp = dp;
	bp->b_blkno = 0;
	bp->b_error = 0;
	bp->b_proc = u.u_procp;
	bp->b_iotype = B_RAWIO;
	BIODONE(bp) = 0;
	/*
	 * Lock process and specified pages in memory
	 */
#ifdef DEBUG
	SDPRINTF("sd_ioctl: locking proc and pages\n");
#endif /* DEBUG */
	++u.u_procp->p_noswap;
	if (bp->b_bcount)
		vslock(bp->b_un.b_addr, (int)bp->b_bcount,
			(bool_t)(bp->b_flags & B_READ));

	/*
	 * Insert request at front of queue, set retry count to 0
	 */
	s = p_lock(&ifd->sd_lock, SDSPL);
#ifdef DEBUG
	SDPRINTF("sd_ioctl: insert in queue\n");
#endif /* DEBUG */
	bp->b_actf = ifd->sd_bufh.b_actf;
	ifd->sd_bufh.b_actf = bp;
	ifd->sd_retrys = 0;
	DS_QUEUED_TIME(bp);
	if (ifd->sd_bufh.b_active == SDS_IDLE) {
#ifdef DEBUG
		SDPRINTF("sd_ioctl: call sdstart\n");
#endif /* DEBUG */
		ifd->sd_bufh.b_active = SDS_BUSY;
		sdstart(&ifd->sd_bufh, ifd);
	}
	DS_Q(ifd->sd_dstats);
	v_lock(&ifd->sd_lock, s);

#ifdef DEBUG
	SDPRINTF("sd_ioctl: wait\n");
#endif /* DEBUG */
	biowait(bp);
	--u.u_procp->p_noswap;
	error = geterror(bp);
	buffree(bp);
#ifdef DEBUG
	SDPRINTF("sd_ioctl: return %d\n", error);
#endif /* DEBUG */
	return(error);
}
			     
/*
 * sdsize()
 *	Used for swap-space partition calculation.
 *
 * Doesn't assume that the device is open or alive at all
 * Hence there is more parameter checks than really needed.
 * Partitions can be designated as going out to the end of the
 * drive by specifying the length as SCSID_END.  When this is done,
 * the actual drive capacity is used.
 */
 
sdsize(dp)
	struct dev *dp;
{
	dev_t	dev = dp->d_dev;
	int	unit;
	register struct	sd_info	*ifd;

	unit = SCSID_DRIVE(dev);
	ifd = sdifd[unit];

	if(ifd == NULL				/* set in boot? */
	|| unit >= sdmaxminor			/* Binary conf'd? */
	|| !ifd->sd_desc->sd_alive		/* Passed probing? */
	|| ifd->sd_capacity == 0)
		return(-1);
	return(ifd->sd_capacity);
}

/*
 * Rezero insert 
 *
 * Insert a device program into the device input queue
 * ahead of the current request to recalibrate the device
 * (rezero unit) before a retry operation. The insertion
 * must be done before the request sense command is issued
 * to the device so that when the device restarts the 
 * recalibration is automatic and requires no further fuss.
 *
 * Note: Because of this 'recommended' condition the device
 * input queue may never be allowed to get empty below two
 * open positions, 1 for a rezero and one for wrap testing
 * for the firmware.
 */

sd_rezero_insert(diq, lun, size)
	register	struct sec_progq *diq;
	unchar		lun;
	int		size;
{
	register	struct sec_dev_prog *idevp;
	register	tail;

	tail = diq->pq_tail;
	if(tail==0)		/* handle wrap around */
		tail = size-1;
	else 
		tail--;
	/*
	 * get previous now unused device program pointer and use
	 * it for the rezero command that is being inserted.
	 */
	idevp = (diq->pq_un.pq_progs)[tail];
	idevp->dp_cmd[0] = SCSI_REZERO;
	idevp->dp_cmd[1] = lun;
	bzero((char *)&idevp->dp_cmd[2], 4);
	idevp->dp_un.dp_iat = 0;
	idevp->dp_data_len = 0;
	idevp->dp_cmd_len = 6;
	idevp->dp_status1 = NULL;
	idevp->dp_status2 = NULL;

	/*
	 * adjust diq ptrs to reflect new device program inserted.
	 */
	diq->pq_tail = tail;
}

daddr_t
sdreadc(sed)
	struct sec_dev *sed;
{
	struct screadcap *sdcap;
	ulong	nblocks = 0;		/* number of blocks on disk */

	if (sd_docmd(SCSI_READC, sed) != 0)
		return((daddr_t) -1);

	sdcap = (struct screadcap *)sd_datap;

	/*
	 * Reverse the byte order
	 */

	nblocks = (sdcap->rc_nblocks0 << 24) |
		  (sdcap->rc_nblocks1 << 16) |
		  (sdcap->rc_nblocks2 << 8)  |
		  (sdcap->rc_nblocks3) ;

	return((daddr_t) nblocks);
}


/*
 * Do an sd_docmd from sdprobe().  There is not a sec_dev structure availble
 * for the device at this point, so we must transmogrify the sec_probe
 * struct into a sec_dev struct for sd_docmd().
 */

sd_docmd_pr(cmd,sed)
	unchar cmd;
	struct sec_probe *sed;
{
	struct sec_dev sdev;

	sdev.sd_desc	= sed->secp_desc;
	sdev.sd_target	= sed->secp_target;
	sdev.sd_unit	= sed->secp_unit;
	sdev.sd_chan	= sed->secp_chan;
	sdev.sd_flags	= sed->secp_flags;
	sdev.sd_bin	= SD_ANYBIN;

	return(sd_docmd(cmd, &sdev));
}

/*
 * Do a general SCSI command on the target adaptor.
 * NOTE:
 * The status address MUST be below 4 Meg because the h/w
 * dma can not address above 4 Meg.
 */

sd_docmd(cmd, sed)
	unchar cmd;
	struct sec_dev *sed;
{
	register struct	sec_powerup *iq = sed->sd_desc->sec_powerup;
	register int	i;
	unchar		data_length;
	unchar		cmd_length;
	int		stat = 0;

#ifdef DEBUG
	if(sd_debug>3) {
		CPRINTF("sd_docmd: init_q 0x%x, slicid 0x%x stat=0x%x,",
			(int)iq, sed->sd_desc->sec_slicaddr, &stat);

		CPRINTF(" device=%d\n", sed->sd_chan);
		CPRINTF("sd_docmd: cmd=0x%x\n", cmd);
	}
#endif	/* DEBUG  */
	ASSERT_DEBUG((int)&stat < 0x400000, "configuration problem:sd_docmd() address of \"stat\" is greater than 0x400000");

	bzero((caddr_t)&sd_devprog, sizeof(struct sec_dev_prog));

	switch (cmd) {

	case SCSI_READC:
		data_length = SCSI_L_READC;
		cmd_length  = SCSI_CMD10SZ;
		break;

	case SCSI_TEST:
		data_length = SCSI_L_TEST;
		cmd_length  = SCSI_CMD6SZ;
		break;

	case SCSI_RSENSE:
		data_length = SCSI_L_REQSEN;
		cmd_length  = SCSI_CMD6SZ;
		break;

	case SCSI_MSENSE:
		sd_devprog.dp_cmd[2] = MSENSE_ERROR;
		data_length = SCSI_L_MODE;
		cmd_length  = SCSI_CMD6SZ;
		break;

	case SCSI_MODES:
		sd_devprog.dp_cmd[1] = MODE_UN_PF;
		data_length = SCSI_L_MODE;
		cmd_length  = SCSI_CMD6SZ;
		break;

	case SCSI_INQUIRY:
		data_length = SCSI_L_INQ;
		cmd_length  = SCSI_CMD6SZ;
		break;
	}

	/*
	 * Fill out the device program for a single command.
	 */

	sd_devprog.dp_un.dp_data = sd_data_queue;
	sd_devprog.dp_data_len = data_length;
	sd_devprog.dp_cmd_len = cmd_length;

	/*
	 * fill out the CDB - command goes in byte 0 and the logical unit
	 * number in byte 1
	 */

	sd_devprog.dp_cmd[0] = cmd;
	sd_devprog.dp_cmd[1] |= (unsigned char)(sed->sd_unit<<5);

	/*
	 * Some commands take an allocation length in byte 4 of the CDB.
	 */

	switch (cmd) {
	case SCSI_RSENSE:
	case SCSI_MODES:
	case SCSI_MSENSE:
	case SCSI_INQUIRY:
		sd_devprog.dp_cmd[4] = data_length;
	}

	/*
	 * Insert device program into SCED queue and mark I/O in progress.
	 */

	i = iq->pu_requestq.pq_head;
	iq->pu_requestq.pq_un.pq_progs[i] = &sd_devprog;
	iq->pu_requestq.pq_head = (i+1) % SEC_POWERUP_QUEUE_SIZE;

	SEC_startio(SINST_STARTIO, 			/* command */
		    &stat,				/* return status loc */
		    sed->sd_bin, 			/* bin number on SEC */
		    sed->sd_chan,			/* Disk channel # */
		    &iq->pu_cib, 			/* device input q loc */
		    (unchar)sed->sd_desc->sec_slicaddr	/* SEC slic id number */
		);

	/*
	 * SCSI command in progress
	 */

	if(stat == SINST_INSDONE) {
		
		/*
		 * spin, waiting for command to complete
		 */

		while(iq->pu_doneq.pq_tail == iq->pu_doneq.pq_head)
			;

		/*
		 * Inform the SCED that the I/O is done by updating the
		 * tail of the device queue.
		 */

		iq->pu_doneq.pq_tail = 
			(iq->pu_doneq.pq_tail + 1) % SEC_POWERUP_QUEUE_SIZE;

	} else {

		iq->pu_requestq.pq_tail = iq->pu_requestq.pq_head = NULL;
		cmn_err(CE_WARN, "sd: probe issued a totally bad command (params)\n");
		/*
		 *+ An internal sd driver error occurred.  The
		 *+ sd driver might be in an inconsistent state.
		 */
		cmn_err(CE_CONT, "sd: stat = 0x%x\n", stat);
		return(stat);
	}

	if(sd_devprog.dp_status1 != SEC_ERR_NONE) {
		int cmdret;

#ifdef DEBUG
		if(sd_debug)
			CPRINTF("sd: err status=0x%x\n",
				sd_devprog.dp_status1);
#endif	/* DEBUG  */
		cmdret = sd_devprog.dp_status1 ;
		sd_devprog.dp_status1 = 0;

		/*
		 * After an error, we need to tell the SCED to keep going
		 * with a RESTARTIO.
		 */

		SEC_startio(SINST_RESTARTIO, &stat, sed->sd_bin, sed->sd_chan,
			&iq->pu_cib, (unchar)sed->sd_desc->sec_slicaddr);

		return(cmdret);
	}
	return(0);
}

/*
 * print information.
 *	not complete yet.  Does not print valid info
 */
sdprint (dp,str)
	struct dev *dp;
	char *str;
{
	cmn_err(CE_CONT,
		"%s on scsi disk %d\n",
		str, SCSID_DRIVE(dp->d_dev));
}

/*
 * sdfirstsect
 *
 *	return the first usable sector of the device.  Always 0 on an sd.
 */
/* ARGSUSED */
int
sdfirstsect(dp)
	struct	dev *dp;
{
	return(0);
}
