/* 
 * Mach Operating System
 * Copyright (c) 1991,1990 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */
/*
 *	File: rz_disk.c
 * 	Author: Alessandro Forin, Carnegie Mellon University
 *	Date:	10/90
 *
 *	Top layer of the SCSI driver: interface with the MI.
 *	This file contains operations specific to disk-like devices.
 *
 *	Modified by Kevin T. Van Maren to use a common partition code
 *	with the ide driver, and support 'slices'.
 */


#include <scsi/scsi.h>
#if (NSCSI > 0)

#include <device/buf.h>
#include <device/disk_status.h>
#include <device/device_types.h>
#include <device/param.h>
#include <device/errno.h>

#include <kern/time_out.h>
#include <machine/machspl.h>            /* spl definitions */
#include <mach/std_types.h>
#include <platforms.h>

#include <scsi/compat_30.h>
#include <scsi/scsi.h>
#include <scsi/scsi_defs.h>
#include <scsi/rz.h>
#include <scsi/rz_labels.h>

#ifdef  MACH_KERNEL
#else   /*MACH_KERNEL*/
#include <sys/kernel.h>         /* for hz */
#endif  /*MACH_KERNEL*/

#include <sys/types.h>
#include <sys/ioctl.h>
#include "vm_param.h"
#include <vm/vm_kern.h>
#include <vm/pmap.h>

extern void     scdisk_read(), scdisk_write(),
                scsi_long_read(), scsi_long_write();

void scdisk_start(); /* forwards */
void scdisk_start_rw();
unsigned dkcksum();

#if 0
struct diskpart scsi_array[8*64];
#endif 0


/* THIS IS THE BOTTOM LAYER FOR THE SCSI PARTITION CODE */
typedef struct scsi_driver_info {
  target_info_t *tgt;
  io_req_t ior;
  void (*readfun)();
  int sectorsize;
} scsi_driver_info;    

int scsi_read_fun(scsi_driver_info *param, int sectornum, void *buff)
{
  char *psave; 
  int result = TRUE; /* SUCCESS */
        psave=param->ior->io_data; /* is this necessary ? */
  
        param->ior->io_data=buff; 
        param->ior->io_count = param->sectorsize;
        param->ior->io_op = IO_READ;
        param->ior->io_error = 0; 
        param->tgt->ior = param->ior;
   
        (*param->readfun)( param->tgt, sectornum, param->ior); 
        iowait(param->ior);
 
	param->ior->io_data=psave;   /* restore the io_data pointer ?? */
  return(result);
}



/*
 * Specialized side of the open routine for disks
 */
scsi_ret_t scdisk_open(tgt, req)
	target_info_t		*tgt;
	io_req_t		req;
{
	register int	 	i, dev_bsize;
	scsi_ret_t		ret = /* SCSI_RET_SUCCESS; */ -1;
	unsigned int		disk_size, secs_per_cyl, sector_size;
	scsi_rcap_data_t	*cap;
	struct disklabel	*label;
	io_req_t		ior;
	void			(*readfun)() = scdisk_read;
	char			*data = (char *)0;

	int numpart;

	scsi_driver_info scsi_info; 
	char drive_name[10];  /* used for disklabel strings */

	if (tgt->flags & TGT_ONLINE)
		return SCSI_RET_SUCCESS;

	/*
	 * Dummy ior for proper sync purposes
	 */
	io_req_alloc(ior,0);
	ior->io_next = 0;
	ior->io_count = 0;

	/*
	 * Set the LBN to DEV_BSIZE with a MODE SELECT.
	 * If this fails we try a couple other tricks.
	 */
	dev_bsize = 0;
	for (i = 0; i < 5; i++) {
		ior->io_op = IO_INTERNAL;
		ior->io_error = 0;
		ret = scdisk_mode_select(tgt, DEV_BSIZE, ior, 0, 0, 0);
		if (ret == SCSI_RET_SUCCESS) {
			dev_bsize = DEV_BSIZE;
			break;
		}
		if (ret == SCSI_RET_RETRY) {
			timeout(wakeup, tgt, 2*hz);
			await(tgt);
		}
		if (ret == SCSI_RET_DEVICE_DOWN)
			goto done;
	}
#if 0
	if (ret != SCSI_RET_SUCCESS) {
		scsi_error( tgt, SCSI_ERR_MSEL, ret, 0);
		ret = D_INVALID_SIZE;
		goto done;
	}
#endif
	/*
	 * Do a READ CAPACITY to get max size. Check LBN too.
	 */
	for (i = 0; i < 5; i++) {
		ior->io_op = IO_INTERNAL;
		ior->io_error = 0;
		ret = scsi_read_capacity(tgt, 0, ior);
		if (ret == SCSI_RET_SUCCESS)
			break;
	}
	if (ret == SCSI_RET_SUCCESS) {
		int			val;

		cap = (scsi_rcap_data_t*) tgt->cmd_ptr;
		disk_size = (cap->lba1<<24) |
			    (cap->lba2<<16) |
			    (cap->lba3<< 8) |
			     cap->lba4;
		if (scsi_debug)
			printf("rz%d holds %d blocks\n", tgt->unit_no, disk_size);
		val = (cap->blen1<<24) |
		      (cap->blen2<<16) |
		      (cap->blen3<<8 ) |
		       cap->blen4;
		if (dev_bsize == 0)
			dev_bsize = val;
		else
		if (val != dev_bsize) panic("read capacity bad");

		if (disk_size > SCSI_CMD_READ_MAX_LBA)
			tgt->flags |= TGT_BIG;

	} else {
		printf("Unknown disk capacity??\n");
		disk_size = -1;
	}
	/*
	 * Mandatory long-form commands ?
	 */
	if (BGET(scsi_use_long_form,(unsigned char)tgt->masterno,tgt->target_id))
		tgt->flags |= TGT_BIG;
	if (tgt->flags & TGT_BIG)
		readfun = scsi_long_read;

	/*
	 * Some CDROMS truly dislike 512 as LBN.
	 * Use a MODE_SENSE to cover for this case.
	 */
	if (dev_bsize == 0) {
		scsi_mode_sense_data_t *m;

		ior->io_op = IO_INTERNAL;
		ior->io_error = 0;
		ret = scsi_mode_sense(tgt, 0/*pagecode*/, 32/*?*/, ior);
		if (ret == SCSI_RET_SUCCESS) {
			m = (scsi_mode_sense_data_t *) tgt->cmd_ptr;
			dev_bsize =
				m->bdesc[0].blen_msb << 16 |
				m->bdesc[0].blen <<  8 |
				m->bdesc[0].blen_lsb;
		}
	}

	/*
	 * Find out about the phys disk geometry -- scsi specific
	 */

	ior->io_op = IO_INTERNAL;
	ior->io_error = 0;
	ret = scsi_read_capacity( tgt, 1, ior);
	if (ret == SCSI_RET_SUCCESS) {
		cap = (scsi_rcap_data_t*) tgt->cmd_ptr;
		secs_per_cyl =	(cap->lba1<<24) | (cap->lba2<<16) |
				(cap->lba3<< 8) |  cap->lba4;
		secs_per_cyl += 1;
		sector_size =	(cap->blen1<<24) | (cap->blen2<<16) |
				(cap->blen3<<8 ) |  cap->blen4;
	} else {
		sector_size = dev_bsize ? dev_bsize : DEV_BSIZE;
		secs_per_cyl = disk_size;
	}
	if (dev_bsize == 0)
		dev_bsize = sector_size;

	if (scsi_debug)
		printf("rz%d: %d sect/cyl %d bytes/sec\n", tgt->unit_no,
			secs_per_cyl, sector_size);

	/*
	 * At this point, one way or other, we are committed to
	 * a given disk capacity and sector size.
	 */
	tgt->block_size = dev_bsize;

	/*
	 * Get partition table off pack
	 */

#ifdef  MACH_KERNEL
        ior->io_data = (char *)kalloc(sector_size);
#endif  /*MACH_KERNEL*/

	scsi_info.tgt=tgt;
	scsi_info.ior=ior;
	scsi_info.readfun=readfun;
	scsi_info.sectorsize=sector_size;

	/* label has NOT been allocated space yet!  set to the tgt disklabel */
	label=&scsi_info.tgt->dev_info.disk.l;

	sprintf(drive_name, "sd%d:", tgt->unit_no);

	if (scsi_debug)
		printf("Using bogus geometry: 32 sectors/track, 64 heads\n");

	fudge_bsd_label(label, DTYPE_SCSI, disk_size /* /(32*64)*/ ,
		64, 32, sector_size, 8);

	numpart=get_only_partition(&scsi_info, (*scsi_read_fun),
		tgt->dev_info.disk.scsi_array, MAX_SCSI_PARTS, disk_size, drive_name);

	printf("%s %d partitions found\n",drive_name,numpart);

	ret=SCSI_RET_SUCCESS;  /* if 0, return SCSI_RET_SUCCESS */


done:
	io_req_free(ior);

	return(ret);
}


/*
 * Disk strategy routine
 */
int scdisk_strategy(ior)
	register io_req_t	ior;
{
	target_info_t  *tgt;
	register scsi_softc_t	*sc;
	register int    i = ior->io_unit, part;
	register unsigned rec, max;
	spl_t		s;
	struct diskpart *label;

	sc = scsi_softc[rzcontroller(i)];
	tgt = sc->target[rzslave(i)];
	part = rzpartition(i);

	/*
	 * Validate request 
	 */

	/* readonly ? */
	if ((tgt->flags & TGT_READONLY) &&
	    (ior->io_op & (IO_READ|IO_INTERNAL) == 0)) {
		ior->io_error = D_READ_ONLY;
		ior->io_op |= IO_ERROR;
		ior->io_residual = ior->io_count;
		iodone(ior);
		return ior->io_error;
	}

	rec = ior->io_recnum;

	label=lookup_part(tgt->dev_info.disk.scsi_array, part);
	if (!label) {
		if (scsi_debug)
			printf("sc strategy -- bad partition\n");
                ior->io_error = D_INVALID_SIZE;
                ior->io_op |= IO_ERROR;
                ior->io_residual = ior->io_count;
                iodone(ior);
                return ior->io_error;
	}
	else max=label->size;
	if (max == -1)  /* what about 0? */
		max = tgt->dev_info.disk.l.d_secperunit -

			label->start;

	i = (ior->io_count + tgt->block_size - 1) / tgt->block_size;
	if (((rec + i) > max) || (ior->io_count < 0) ||
#if later
	    ((rec <= LABELSECTOR) && ((tgt->flags & TGT_WRITE_LABEL) == 0))
#else
	    FALSE
#endif
	    ) {
		ior->io_error = D_INVALID_SIZE;
		ior->io_op |= IO_ERROR;
		ior->io_residual = ior->io_count;
		iodone(ior);
		return ior->io_error;
	}
	/*
	 * Find location on disk: secno and cyl (for disksort) 
	 */
	rec += label->start;
	ior->io_residual = rec / tgt->dev_info.disk.l.d_secpercyl;

	/*
	 * Enqueue operation 
	 */
	s = splbio();
	simple_lock(&tgt->target_lock);
	if (tgt->ior) {
		disksort(tgt->ior, ior);
		simple_unlock(&tgt->target_lock);
		splx(s);
	} else {
		ior->io_next = 0;
		tgt->ior = ior;
		simple_unlock(&tgt->target_lock);
		splx(s);

		scdisk_start(tgt,FALSE);
	}

	return D_SUCCESS;
}

/*#define CHECKSUM*/
#ifdef	CHECKSUM
int max_checksum_size = 0x2000;
#endif	CHECKSUM

/*
 * Start/completion routine for disks
 */
void scdisk_start(tgt, done)
	target_info_t	*tgt;
	boolean_t	done;
{
	register io_req_t	ior = tgt->ior;
	register unsigned	part;
#ifdef	CHECKSUM
	register unsigned	secno;
#endif
	struct diskpart *label;

	if (ior == 0)
		return;

	if (tgt->flags & TGT_BBR_ACTIVE)
	{
		scdisk_bbr_start(tgt, done);
		return;
	}

	if (done) {
		register unsigned int	xferred;
		unsigned int		max_dma_data;

		max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data;
		/* see if we must retry */
		if ((tgt->done == SCSI_RET_RETRY) &&
		    ((ior->io_op & IO_INTERNAL) == 0)) {
			delay(1000000);/*XXX*/
			goto start;
		} else
		/* got a bus reset ? pifff.. */
		if ((tgt->done == (SCSI_RET_ABORTED|SCSI_RET_RETRY)) &&
		    ((ior->io_op & IO_INTERNAL) == 0)) {
			if (xferred = ior->io_residual) {
				ior->io_data -= xferred;
				ior->io_count += xferred;
				ior->io_recnum -= xferred / tgt->block_size;
				ior->io_residual = 0;
			}
			goto start;
		} else
		/*
		 * Quickly check for errors: if anything goes wrong
		 * we do a request sense, see if that is what we did.
		 */
		if (tgt->cur_cmd == SCSI_CMD_REQUEST_SENSE) {
			scsi_sense_data_t	*sns;
			unsigned int		blockno;
			char			*outcome;

			ior->io_op = ior->io_temporary;

			sns = (scsi_sense_data_t *)tgt->cmd_ptr;
			if (sns->addr_valid)
				blockno = sns->u.xtended.info0 << 24 |
					  sns->u.xtended.info1 << 16 |
					  sns->u.xtended.info2 <<  8 |
					  sns->u.xtended.info3;
			else {
				part     = rzpartition(ior->io_unit);
				label = lookup_part(tgt->dev_info.disk.scsi_array, part);
				blockno = label->start;
				blockno += ior->io_recnum;
			if (!label) blockno=-1;
			}

			if (scsi_check_sense_data(tgt, sns)) {
				ior->io_error = 0;
				if ((tgt->done == SCSI_RET_RETRY) &&
		                    ((ior->io_op & IO_INTERNAL) == 0)) {
		                        delay(1000000);/*XXX*/
		                        goto start;
				}
				outcome = "Recovered";
			} else {
				outcome = "Unrecoverable";
				ior->io_error = D_IO_ERROR;
				ior->io_op |= IO_ERROR;
			}
			if ((tgt->flags & TGT_OPTIONAL_CMD) == 0) {
			    printf("%s Error, rz%d: %s%s%d\n", outcome,
				tgt->target_id + (tgt->masterno * 8),
				(ior->io_op & IO_READ) ? "Read" :
				 ((ior->io_op & IO_INTERNAL) ? "(command)" : "Write"),
				" disk error, phys block no. ", blockno);

			    scsi_print_sense_data(sns);

			    /*
			     * On fatal read/write errors try replacing the bad block
			     * The bbr routine will return TRUE iff it took control
			     * over the target for all subsequent operations. In this
			     * event, the queue of requests is effectively frozen.
			     */
			    if (ior->io_error && 
				((sns->error_class == SCSI_SNS_XTENDED_SENSE_DATA) &&
				 ((sns->u.xtended.sense_key == SCSI_SNS_HW_ERR) ||
				  (sns->u.xtended.sense_key == SCSI_SNS_MEDIUM_ERR))) &&
			    	scdisk_bad_block_repl(tgt, blockno))
				    return;
			}
		}

		/*
	 	 * See if we had errors
		 */
		else if (tgt->done != SCSI_RET_SUCCESS) {

			if (tgt->done == SCSI_RET_NEED_SENSE) {

				ior->io_temporary = ior->io_op;
				ior->io_op = IO_INTERNAL;
				scsi_request_sense(tgt, ior, 0);
				return;

			} else if (tgt->done == SCSI_RET_DEVICE_DOWN) {
				ior->io_error = D_DEVICE_DOWN;
				ior->io_op |= IO_ERROR;
			} else {
				printf("%s%x\n", "?rz_disk Disk error, ret=x", tgt->done);
				ior->io_error = D_IO_ERROR;
				ior->io_op |= IO_ERROR;
			}
		}
		/*
		 * No errors.
		 * See if we requested more than the max
		 * (We use io_residual in a flip-side way here)
		 */
		else if (ior->io_count > (xferred = max_dma_data)) {
			ior->io_residual += xferred;
			ior->io_count -= xferred;
			ior->io_data += xferred;
			ior->io_recnum += xferred / tgt->block_size;
			goto start;
		}
		else if (xferred = ior->io_residual) {
			ior->io_data -= xferred;
			ior->io_count += xferred;
			ior->io_recnum -= xferred / tgt->block_size;
			ior->io_residual = 0;
		} /* that's it */

#ifdef	CHECKSUM
		if ((ior->io_op & IO_READ) && (ior->io_count < max_checksum_size)) {
			part = rzpartition(ior->io_unit);
			label=lookup_part(tgt->dev_info.disk.scsi_array, part);
	if (!label) printf("NOT FOUND!\n");
			secno = ior->io_recnum + label->start;
			scdisk_bcheck(secno, ior->io_data, ior->io_count);
		}
#endif	CHECKSUM

		/* dequeue next one */
		{
			io_req_t	next;

			simple_lock(&tgt->target_lock);
			next = ior->io_next;
			tgt->ior = next;
			simple_unlock(&tgt->target_lock);

			iodone(ior);
			if (next == 0)
				return;

			ior = next;
		}

#ifdef	CHECKSUM
		if (((ior->io_op & IO_READ) == 0) && (ior->io_count < max_checksum_size)) {
			part = rzpartition(ior->io_unit);
			label=lookup_part(tgt->dev_info.disk.scsi_array, part);
			secno = ior->io_recnum + label->start;
			scdisk_checksum(secno, ior->io_data, ior->io_count);
		}
#endif	CHECKSUM
	}
	ior->io_residual = 0;
start:
	scdisk_start_rw( tgt, ior);
}

void scdisk_start_rw( tgt, ior)
	target_info_t	*tgt;
	register io_req_t	ior;
{
	unsigned int	part, secno;
	register boolean_t	long_form;
	struct diskpart *label;

	part = rzpartition(ior->io_unit);
	label=lookup_part(tgt->dev_info.disk.scsi_array, part);
	if (!label)
		printf("NOT FOUND!\n");
	secno = ior->io_recnum + label->start;

	/* Use long form if either big block addresses or
	   the size is more than we can fit in one byte */
	long_form = (tgt->flags & TGT_BIG) ||
		    (ior->io_count > (256 * tgt->block_size));
	if (ior->io_op & IO_READ)
		(long_form ? scsi_long_read : scdisk_read)(tgt, secno, ior);
	else if ((ior->io_op & IO_INTERNAL) == 0)
		(long_form ? scsi_long_write : scdisk_write)(tgt, secno, ior);
}

#include <sys/ioctl.h>
#ifdef	ULTRIX_COMPAT
#include <mips/PMAX/rzdisk.h>
#endif	/*ULTRIX_COMPAT*/

io_return_t
scdisk_get_status(dev, tgt, flavor, status, status_count)
	int		dev;
	target_info_t	*tgt;
	dev_flavor_t	flavor;
	dev_status_t	status;
	natural_t	*status_count;
{
	struct disklabel *lp;
	struct diskpart *label;

	lp = &tgt->dev_info.disk.l;

	switch (flavor) {
#ifdef	MACH_KERNEL
	case DEV_GET_SIZE:
		
		label=lookup_part(tgt->dev_info.disk.scsi_array, rzpartition(dev));
		status[DEV_GET_SIZE_DEVICE_SIZE] = label->size * lp->d_secsize;
		status[DEV_GET_SIZE_RECORD_SIZE] = tgt->block_size;
		*status_count = DEV_GET_SIZE_COUNT;
		break;
#endif

	case DIOCGDINFO:
		*(struct disklabel *)status = *lp;
#ifdef	MACH_KERNEL
		*status_count = sizeof(struct disklabel)/sizeof(int);
#endif	MACH_KERNEL
		break;

	case DIOCGDINFO - (0x10<<16):
		*(struct disklabel *)status = *lp;
#ifdef	MACH_KERNEL
		*status_count = sizeof(struct disklabel)/sizeof(int) - 4;
#endif	MACH_KERNEL
		break;

#ifdef	MACH_KERNEL
#else	/*MACH_KERNEL*/
#if	ULTRIX_COMPAT
	case SCSI_MODE_SENSE:		/*_IOWR(p, 9, struct mode_sel_sns_params) */
		break;
	case DIOCGETPT:			/*_IOR(p, 1, struct pt) */
	case SCSI_GET_SENSE:		/*_IOR(p, 10, struct extended_sense) */
		return ul_disk_ioctl(tgt, flavor, status, status_count);
#endif	/*ULTRIX_COMPAT*/
#endif	/*!MACH_KERNEL*/

#if 0
	case DIOCRFORMAT:
		break;
#endif
	default:
#ifdef	i386
		return(scsi_i386_get_status(dev, tgt, flavor, status, status_count));
#else	i386
 		return(D_INVALID_OPERATION);
#endif	i386
	}
	return D_SUCCESS;
}

io_return_t
scdisk_set_status(dev, tgt, flavor, status, status_count)
	int		dev;
	target_info_t	*tgt;
	dev_flavor_t	flavor;
	dev_status_t	status;
	natural_t	status_count;
{
	io_return_t error = D_SUCCESS;
	struct disklabel *lp;

	lp = &tgt->dev_info.disk.l;


	switch (flavor) {
	case DIOCSRETRIES:
#ifdef	MACH_KERNEL
		if (status_count != sizeof(int))
			return D_INVALID_SIZE;
#endif	/* MACH_KERNEL */
		scsi_bbr_retries = *(int *)status;
		break;

	case DIOCWLABEL:
	case DIOCWLABEL - (0x10<<16):
		if (*(int*)status)
			tgt->flags |= TGT_WRITE_LABEL;
		else
			tgt->flags &= ~TGT_WRITE_LABEL;
		break;
	case DIOCSDINFO:
	case DIOCSDINFO - (0x10<<16):
	case DIOCWDINFO:
	case DIOCWDINFO - (0x10<<16):
#ifdef	MACH_KERNEL
		if (status_count != sizeof(struct disklabel) / sizeof(int))
			return D_INVALID_SIZE;
#endif	/* MACH_KERNEL */
		error = setdisklabel(lp, (struct disklabel*) status);
		if (error || (flavor == DIOCSDINFO) || (flavor == DIOCSDINFO - (0x10<<16)))
			return error;
		error = scdisk_writelabel(tgt);
		break;

#ifdef	MACH_KERNEL
#else	/*MACH_KERNEL*/
#if	ULTRIX_COMPAT
	case SCSI_FORMAT_UNIT:		/*_IOW(p, 4, struct format_params) */
	case SCSI_REASSIGN_BLOCK:	/*_IOW(p, 5, struct reassign_params) */
	case SCSI_READ_DEFECT_DATA:	/*_IOW(p, 6, struct read_defect_params) */
	case SCSI_VERIFY_DATA:		/*_IOW(p, 7, struct verify_params) */
	case SCSI_MODE_SELECT:		/*_IOW(p, 8, struct mode_sel_sns_params) */
	case SCSI_MODE_SENSE:		/*_IOW(p, 9, struct mode_sel_sns_params) */
	case SCSI_GET_INQUIRY_DATA:	/*_IOW(p, 11, struct inquiry_info) */
		return ul_disk_ioctl(tgt, flavor, status, status_count);
#endif	/*ULTRIX_COMPAT*/
#endif	/*!MACH_KERNEL*/

#if notyet
	case DIOCWFORMAT:
	case DIOCSBAD:	/* ?? how ? */
#endif
	default:
#ifdef	i386
		error = scsi_i386_set_status(dev, tgt, flavor, status, status_count);
#else	i386
 		error = D_INVALID_OPERATION;
#endif	i386
	}
	return error;
}

static int grab_it(tgt, ior)
	target_info_t	*tgt;
	io_req_t	ior;
{
	spl_t	s;

	s = splbio();
	simple_lock(&tgt->target_lock);
	if (!tgt->ior)
		tgt->ior = ior;
	simple_unlock(&tgt->target_lock);
	splx(s);

	if (tgt->ior != ior)
		return D_ALREADY_OPEN;
	return D_SUCCESS;
}

/* Write back a label to the disk */
io_return_t scdisk_writelabel(tgt)
	target_info_t	*tgt;
{

printf("scdisk_writelabel: NO LONGER IMPLEMENTED\n");
#if 0
/* Taken out at Bryan's suggestion until 'fixed' for slices */

	io_req_t	ior;
	char		*data = (char *)0;
	struct disklabel *label;
	io_return_t	error;
	int		dev_bsize = tgt->block_size;

	io_req_alloc(ior,0);
#ifdef	MACH_KERNEL
	data = (char *)kalloc(dev_bsize);
#else	/*MACH_KERNEL*/
	data = (char *)ior->io_data;
#endif	/*MACH_KERNEL*/
	ior->io_next = 0;
	ior->io_prev = 0;
	ior->io_data = data;
	ior->io_count = dev_bsize;
	ior->io_op = IO_READ;
	ior->io_error = 0;

	if (grab_it(tgt, ior) != D_SUCCESS) {
		error = D_ALREADY_OPEN;
		goto ret;
	}

	scdisk_read( tgt, tgt->dev_info.disk.labelsector, ior);
	iowait(ior);
	if (error = ior->io_error)
		goto ret;

	label = (struct disklabel *) &data[tgt->dev_info.disk.labeloffset];
	*label = tgt->dev_info.disk.l;

	ior->io_next = 0;
	ior->io_prev = 0;
	ior->io_data = data;
	ior->io_count = dev_bsize;
	ior->io_op = IO_WRITE;

	while (grab_it(tgt, ior) != D_SUCCESS)	;	/* ahem */

	scdisk_write( tgt, tgt->dev_info.disk.labelsector, ior);
	iowait(ior);

	error = ior->io_error;
ret:
#ifdef	MACH_KERNEL
	if (data) kfree((int)data, dev_bsize);
#endif	/*MACH_KERNEL*/
	io_req_free(ior);
	return error;

#endif  0  scdisk_writelabel
return -1;  /* FAILURE ? */
}

#ifdef	MACH_KERNEL
#else	/*MACH_KERNEL*/
#if	ULTRIX_COMPAT

io_return_t ul_disk_ioctl(tgt, flavor, status, status_count)
	target_info_t	*tgt;
	dev_flavor_t	flavor;
	dev_status_t	status;
	natural_t	status_count;
{
	io_return_t	ret;
	scsi_ret_t	err = SCSI_RET_ABORTED;/*xxx*/
	io_req_t	ior;

	if (!suser())
		return EACCES;

	ior = geteblk(sizeof(struct defect_descriptors));
	ior->io_next = 0;
	ior->io_count = 0;
	ior->io_op = IO_INTERNAL;
	ior->io_error = 0;
	ior->io_recnum = 0;
	ior->io_residual = 0;

	switch (flavor) {

	case DIOCGETPT: {			/*_IOR(p, 1, struct pt) */
		scsi_dec_label_t	*p;
		struct disklabel	*lp;
		int			i;

		lp = &tgt->dev_info.disk.l;
		p = (scsi_dec_label_t *)status;

		p->magic = DEC_PARTITION_MAGIC;
		p->in_use = 1;
		for (i = 0; i < 8; i++) {
			label=lookup_part(tgt->dev_info.disk.scsi_array, part);
			p->partitions[i].n_sectors = label->size;
			p->partitions[i].offset = label->start;
		}
		err = SCSI_RET_SUCCESS;
	    }
	    break;

	case SCSI_GET_SENSE: {		/*_IOR(p, 10, struct extended_sense) */
		scsi_sense_data_t	*s;

		s = (scsi_sense_data_t*)tgt->cmd_ptr;
		bcopy(s, status, sizeof(*s) + s->u.xtended.add_len - 1);
		err = SCSI_RET_SUCCESS;
		/* only once */
		bzero(tgt->cmd_ptr, sizeof(scsi_sense_data_t));
	    }
	    break;

	case SCSI_GET_INQUIRY_DATA: {	/*_IOR(p, 11, struct inquiry_info) */
		struct mode_sel_sns_params *ms;

		ms = (struct mode_sel_sns_params*)status;
		err = scsi_inquiry( tgt, SCSI_INQ_STD_DATA);
		if (copyout(tgt->cmd_ptr, ms->msp_addr, sizeof(struct inquiry_info))){
			ret = EFAULT;
			goto out;
		}
	    }
	    break;

	case SCSI_FORMAT_UNIT: {	/*_IOW(p, 4, struct format_params) */
		struct format_params *fp;
		struct defect_descriptors *df;
		unsigned char	mode;
		unsigned int	old_timeout;

		fp = (struct format_params *)status;
		df = (struct defect_descriptors*)ior->io_data;
		if (fp->fp_length != 0) {
			if (copyin(fp->fp_addr, df, sizeof(*df))) {
				ret = EFAULT;
				goto out;
			}
			ior->io_count = sizeof(*df);
		} else
			ior->io_count = 0;
		mode = fp->fp_format & SCSI_CMD_FMT_LIST_TYPE;
		switch (fp->fp_defects) {
		case VENDOR_DEFECTS:
			mode |= SCSI_CMD_FMT_FMTDATA|SCSI_CMD_FMT_CMPLIST;
			break;
		case KNOWN_DEFECTS:
			mode |= SCSI_CMD_FMT_FMTDATA;
			break;
		case NO_DEFECTS:
		default:
			break;
		}
		old_timeout = scsi_watchdog_period;
		scsi_watchdog_period = 60*60;	/* 1 hour should be enough, I hope */
		err = scsi_format_unit( tgt, mode, fp->fp_pattern,
					fp->fp_interleave, ior);
		scsi_watchdog_period = old_timeout;
		/* Make sure we re-read all info afresh */
		tgt->flags = TGT_ALIVE |
			     (tgt->flags & (TGT_REMOVABLE_MEDIA|TGT_FULLY_PROBED));
	    }
	    break;

	case SCSI_REASSIGN_BLOCK: {	/*_IOW(p, 5, struct reassign_params) */
		struct reassign_params	*r;
		int			ndef;

		r = (struct reassign_params*) status;
		ndef = r->rp_header.defect_len0 | (r->rp_header.defect_len1 >> 8);
		ndef >>= 2;
		tgt->ior = ior;
		(void) scsi_reassign_blocks( tgt, &r->rp_lbn3, ndef, ior);
		iowait(ior);
		err = tgt->done;
	    }
	    break;

	case SCSI_READ_DEFECT_DATA: {	/*_IOW(p, 6, struct read_defect_params) */
		struct read_defect_params *dp;

		dp = (struct read_defect_params *)status;
		ior->io_count = ior->io_alloc_size;
		if (dp->rdp_alclen > ior->io_count)
			dp->rdp_alclen = ior->io_count;
		else
			ior->io_count = dp->rdp_alclen;
		ior->io_op |= IO_READ;
		tgt->ior = ior;
		err = scsi_read_defect(tgt, dp->rdp_format|0x18, ior);
		if (copyout(ior->io_data, dp->rdp_addr, dp->rdp_alclen)) {
			ret = EFAULT;
			goto out;
		}
	    }
	    break;

	case SCSI_VERIFY_DATA: {	/*_IOW(p, 7, struct verify_params) */
		struct verify_params	*v;
		unsigned int	old_timeout;

		old_timeout = scsi_watchdog_period;
		scsi_watchdog_period = 5*60;	/* 5 mins enough, I hope */
		v = (struct verify_params *)status;
		ior->io_count = 0;
		err = scdisk_verify( tgt, v->vp_lbn, v->vp_length, ior);
		scsi_watchdog_period = old_timeout;
	    }
	    break;

	case SCSI_MODE_SELECT: {	/*_IOW(p, 8, struct mode_sel_sns_params) */
		struct mode_sel_sns_params *ms;

		ms = (struct mode_sel_sns_params*)status;
		if(copyin(ms->msp_addr, ior->io_data, ms->msp_length)) {
			ret = EFAULT;
			goto out;
		}
		err = scdisk_mode_select( tgt, DEV_BSIZE, ior, ior->io_data,
					  ms->msp_length, ms->msp_setps);
	    }
	    break;

	case SCSI_MODE_SENSE: {		/*_IOWR(p, 9, struct mode_sel_sns_params) */
		struct mode_sel_sns_params *ms;
		unsigned char		pagecode;

		ms = (struct mode_sel_sns_params*)status;
		pagecode = (ms->msp_pgcode & 0x3f) | (ms->msp_pgctrl << 6);
		err = scsi_mode_sense( tgt, pagecode, ms->msp_length, ior);
		if (copyout(tgt->cmd_ptr, ms->msp_addr, ms->msp_length)){
			ret = EFAULT;
			goto out;
		}
	    }
	    break;
	}

	ret = (err == SCSI_RET_SUCCESS) ? D_SUCCESS : D_IO_ERROR;
	if (ior->io_op & IO_ERROR)
		ret = D_IO_ERROR;
out:
	brelse(ior);
	return ret;
}
#endif	/*ULTRIX_COMPAT*/
#endif	/*!MACH_KERNEL*/

#ifdef	CHECKSUM

#define SUMSIZE 0x10000
#define SUMHASH(b)	(((b)>>1) & (SUMSIZE - 1))
struct {
	long blockno;
	long sum;
} scdisk_checksums[SUMSIZE];

void scdisk_checksum(bno, addr, size)
	long bno;
	register unsigned int *addr;
{
	register int i = size/sizeof(int);
	register unsigned int sum = -1;

	while (i-- > 0)
		sum ^= *addr++;
	scdisk_checksums[SUMHASH(bno)].blockno = bno;
	scdisk_checksums[SUMHASH(bno)].sum = sum;
}

void scdisk_bcheck(bno, addr, size)
	long bno;
	register unsigned int *addr;
{
	register int i = size/sizeof(int);
	register unsigned int sum = -1;
	unsigned int *start = addr;

	if (scdisk_checksums[SUMHASH(bno)].blockno != bno) {
if (scsi_debug) printf("No checksum for block x%x\n", bno);
		return;
	}

	while (i-- > 0)
		sum ^= *addr++;

	if (scdisk_checksums[SUMHASH(bno)].sum != sum) {
		printf("Bad checksum (x%x != x%x), bno x%x size x%x at x%x\n",
			sum,
			scdisk_checksums[bno & (SUMSIZE - 1)].sum,
			bno, size, start);
		gimmeabreak();
		scdisk_checksums[SUMHASH(bno)].sum = sum;
	}
}


#endif CHECKSUM

/*#define PERF */
#ifdef	PERF
int test_read_size = 512;
int test_read_skew = 12;
int test_read_skew_min = 0;
int test_read_nreads = 1000;
int test_read_bdev = 0;

#include <sys/time.h>

void test_read(max)
{
	int i, ssk, usecs;
	struct timeval start, stop;

	if (max == 0)
		max = test_read_skew + 1;
	ssk = test_read_skew;
	for (i = test_read_skew_min; i < max; i++){
		test_read_skew = i;

		start = time;
		read_test();
		stop = time;

		usecs = stop.tv_usec - start.tv_usec;
		if (usecs < 0) {
			stop.tv_sec -= 1;
			usecs += 1000000;
		}
		printf("Skew %3d size %d count %d time %3d sec %d us\n",
			i, test_read_size, test_read_nreads,
			stop.tv_sec - start.tv_sec, usecs);
	}
	test_read_skew = ssk;
}

void read_test()
{
	static int	buffer[(8192*2)/sizeof(int)];
	struct io_req	io;
	register int 	i, rec;

	bzero(&io, sizeof(io));
	io.io_unit = test_read_bdev;
	io.io_op = IO_READ;
	io.io_count = test_read_size;
	io.io_data = (char*) buffer;

	for (rec = 0, i = 0; i < test_read_nreads; i++) {
		io.io_op = IO_READ;
		io.io_recnum = rec;
		scdisk_strategy(&io);
		rec += test_read_skew;
		iowait(&io);
	}
}

void tur_test()
{
	struct io_req	io;
	register int 	i;
	char		*a, *b;
	struct timeval start, stop;

	bzero(&io, sizeof(io));
	io.io_unit = test_read_bdev;
	io.io_data = (char*)&io;/*unused but kernel space*/

	start = time;
	for (i = 0; i < test_read_nreads; i++) {
		io.io_op = IO_INTERNAL;
		rz_check(io.io_unit, &a, &b);
		scsi_test_unit_ready(b,&io);
	}
	stop = time;
	i = stop.tv_usec - start.tv_usec;
	if (i < 0) {
		stop.tv_sec -= 1;
		i += 1000000;
	}
	printf("%d test-unit-ready took %3d sec %d us\n",
			test_read_nreads,
			stop.tv_sec - start.tv_sec, i);
}

#endif	PERF

/*#define WDEBUG*/
#ifdef	WDEBUG

int buggo_write_size = 8192;
int buggo_dev = 2; /* rz0b */  /* changed by KTVM from 1 (still b) */
int	buggo_out_buffer[8192/2];
int	buggo_in_buffer[8192/2];
int buggotest(n, pattern, verbose)
{
	struct io_req	io;
	register int 	i, rec;

	if (n <= 0)
		n = 1;

	if(pattern)
		for (i = 0; i < buggo_write_size/4; i++)
			buggo_out_buffer[i] = i + pattern;

	for (i = 0; i < n; i++) {
		register int j;

		buggo_out_buffer[0] = i + pattern;
		buggo_out_buffer[(buggo_write_size/4)-1] = i + pattern;
		bzero(&io, sizeof(io));
		io.io_unit = buggo_dev;
		io.io_data = (char*)buggo_out_buffer;
		io.io_op = IO_WRITE;
		io.io_count = buggo_write_size;
		io.io_recnum = i % 1024;
		scdisk_strategy(&io);

		bzero(buggo_in_buffer, sizeof(buggo_in_buffer));
		iowait(&io);

		if (verbose)
			printf("Done write with %x", io.io_error);

		bzero(&io, sizeof(io));
		io.io_unit = buggo_dev;
		io.io_data = (char*)buggo_in_buffer;
		io.io_op = IO_READ;
		io.io_count = buggo_write_size;
		io.io_recnum = i % 1024;
		scdisk_strategy(&io);
		iowait(&io);

		if (verbose)
			printf("Done read with %x", io.io_error);

		for  (j = 0; j < buggo_write_size/4; j++)
			if (buggo_out_buffer[j] != buggo_in_buffer[j]){
				printf("Difference at %d-th word: %x %x\n",
					buggo_out_buffer[j], buggo_in_buffer[j]);
				return i;
			}
	}
	printf("Test ok\n");
	return n;
}
#endif	WDEBUG
#endif  /* NSCSI > 0 */