/*      @(#)sf.c 1.1 94/10/31 SMI      */

/*
 * Copyright (c) 1988 by Sun Microsystems, Inc.
 */
#include "sf.h"
#if NSF > 0

#define SFDEBUG 1		/* Allow compiling of debug code */
#define REL4			/* enable release 4.00 mods */

/*
 * SCSI driver for SCSI disks.
 */
#ifndef REL4
#include "sf.h"
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/dk.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/map.h"
#include "../h/vmmac.h"
#include "../h/ioctl.h"
#include "../h/uio.h"
#include "../h/kernel.h"
#include "../h/dkbad.h"
#include "../h/fcntl.h"
#include "../h/file.h"
#include "../h/proc.h"

#include "../machine/pte.h"
#include "../machine/psl.h"
#include "../machine/mmu.h"
#include "../machine/cpu.h"

#include "../sun/dklabel.h"
#include "../sun/dkio.h"

#include "../sundev/mbvar.h"
#include "../sundev/screg.h"
#include "../sundev/sireg.h"
#include "../sundev/scsi.h"
#include "../sundev/sfreg.h"

#else REL4
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/dk.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/user.h>
#include <sys/map.h>
#include <sys/vmmac.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/dkbad.h>
#include <sys/fcntl.h>
#include <sys/file.h>

#include <machine/pte.h>
#include <machine/psl.h>
#include <machine/mmu.h>
#include <machine/cpu.h>

#include <sun/dklabel.h>
#include <sun/dkio.h>

#include <sundev/mbvar.h>
#include <sundev/screg.h>
#include <sundev/sireg.h>
#include <sundev/scsi.h>
#include <sundev/sfreg.h>
#endif REL4

#if defined(REL4)  &&  defined(SF_FORMAT)
#include <vm/faultcode.h>
#include <vm/hat.h>
#include <vm/seg.h>
#include <vm/as.h> 

#endif defined(REL4)  &&  defined(SF_FORMAT)


#define SFDTYPE(flags)	((flags & 0xff00) >> 8)
/* #define MAX_RETRIES 		 4	/* retry limit */
#define MAX_RETRIES 		 1	/* retry limit */
/* #define MAX_RESTORES 		 4	/* rezero unit limit (after retries) */
#define MAX_RESTORES 		 1	/* rezero unit limit (after retries) */
#define MAX_FAILS 		20 	/* soft errors before reassign */
#define MAX_LABEL_RETRIES  	 1
#define MAX_LABEL_RESTORES 	 1
#define MAX_BUSY		10
#define EL_RETRY		 1	/* Error msg threshold, retries */
#define EL_REST			 0	/* Error msg threshold, restores */
#define EL_FAILS 		10 	/* Error msg threshold, soft errors */


#define LPART(dev)		(dev & (NLPART -1))
#define SFUNIT(dev)		((dev >> 3) & (NUNIT -1))
#define SFNUM(un)		(un - sfunits)


#ifdef SFDEBUG
short sf_debug = 0;		/*
				 * 0 = normal operation
				 * 1 = extended error info only
				 * 2 = debug and error info
				 * 3 = all status info
				 */
/* Handy debugging 0, 1, and 2 argument printfs */
#define SF_PRINT_CMD(un) \
	if (sf_debug > 1) sf_print_cmd(un)
#define DPRINTF(str) \
	if (sf_debug > 1) printf(str)
#define DPRINTF1(str, arg1) \
	if (sf_debug > 1) printf(str,arg1)
#define DPRINTF2(str, arg1, arg2) \
	if (sf_debug > 1) printf(str,arg1,arg2)

/* Handy extended error reporting 0, 1, and 2 argument printfs */
#define EPRINTF(str) \
	if (sf_debug) printf(str)
#define EPRINTF1(str, arg1) \
	if (sf_debug) printf(str,arg1)
#define EPRINTF2(str, arg1, arg2) \
	if (sf_debug) printf(str,arg1,arg2)
#define DEBUG_DELAY(cnt) \
	if (sf_debug)  DELAY(cnt)

#else SFDEBUG
#define SF_PRINT_CMD(un)
#define DPRINTF(str)
#define DPRINTF1(str, arg2)
#define DPRINTF2(str, arg1, arg2)
#define EPRINTF(str)
#define EPRINTF1(str, arg2)
#define EPRINTF2(str, arg1, arg2)
#define DEBUG_DELAY(cnt)
#endif SFDEBUG

extern char *strncpy();

extern struct scsi_unit sfunits[];
extern struct scsi_unit_subr scsi_unit_subr[];
extern struct scsi_floppy sfdisk[];
extern int nsfdisk;
extern int scsi_debug;
short sf_retry = 1;	/*
/* short sf_retry = EL_RETRY;	/*
				 * Error message threshold, retries.
				 * Make it global so manufacturing can
				 * override setting.
				 */

#ifdef	SFDEBUG
sf_print_buffer(y, count)
	register u_char *y;
	register int count;
{
	register int x;

	for (x = 0; x < count; x++)
		printf("%x  ", *y++);
	printf("\n");
}
#endif	SFDEBUG


/*
 * Return a pointer to this unit's unit structure.
 */
sfunitptr(md)
	register struct mb_device *md;
{
	return ((int)&sfunits[md->md_unit]);
}


static
sftimer(dev)
	register dev_t dev;
{
	register struct scsi_floppy *dsi;
	register struct scsi_unit *un;
	register struct scsi_ctlr *c;
	register u_int	unit;

	unit = SFUNIT(dev);
	un = &sfunits[unit];
	dsi = &sfdisk[unit];
	c = un->un_c;

/* 	DPRINTF("sftimer:\n"); */
	if (dsi->un_openf >= OPENING) {
#ifdef		SF_TIMEOUT
		if ((dsi->un_timeout != 0)  &&  (--dsi->un_timeout == 0)) {

			/* Process command timeout for normal I/O operations */
			printf("sf%d:  sftimer: I/O request timeout\n", unit);
			if ((*c->c_ss->scs_deque)(c, un)) {
				/* Can't Find CDB I/O request to kill, help! */
				printf("sf%d:  sftimer: can't abort request\n",
					unit);
				(*un->un_c->c_ss->scs_reset)(un->un_c, 0);
			}
		} else
			if (dsi->un_timeout != 0) {
/* 			DPRINTF2("sftimer:  running, open= %d, timeout= %d\n",
				 dsi->un_openf, dsi->un_timeout); */
			}
#endif		SF_TIMEOUT

	/* Process opening delay timeout */
	} else if ((dsi->un_timeout != 0)  &&  (--dsi->un_timeout == 0)) {
		DPRINTF("sftimer:  running...\n");
		wakeup((caddr_t)dev);
	}
	timeout(sftimer, (caddr_t)dev, 30*hz);
}


/*
 * Attach device (boot time).
 */
sfattach(md)
	register struct mb_device *md;
{
	register struct scsi_unit *un;
	register struct scsi_floppy *dsi;
	struct scsi_inquiry_data *sid;
	int i;

	dsi = &sfdisk[md->md_unit];
	un = &sfunits[md->md_unit];

	/*
	 * link in, fill in unit struct.
	 */
	un->un_md = md;
	un->un_mc = md->md_mc;
	un->un_unit = md->md_unit;
	un->un_target = TARGET(md->md_slave);
	un->un_lun = LUN(md->md_slave);
	un->un_ss = &scsi_unit_subr[TYPE(md->md_flags)];
	un->un_present = 0;

	dsi->un_openf = OPENING;
	dsi->un_flags = 0;
	dsi->un_timeout = 0;
	dsi->un_timer = 0;
	dsi->un_status = 0;
	dsi->un_retries = 0;
	dsi->un_restores = 0;
	dsi->un_sec_left = 0;
	dsi->un_total_errors = 0;
	dsi->un_err_resid = 0;
	dsi->un_err_blkno = 0;
	dsi->un_cyl_start = 0;
	dsi->un_cyl_end = 0;
	dsi->un_cylin_last = 0;
	dsi->un_lp1 = NULL;
	dsi->un_lp2 = NULL;
	dsi->un_ctype = CTYPE_UNKNOWN;
	dsi->sf_spt = 0;
	dsi->sf_mdb = 0;
	dsi->sf_nhead = 0;
	dsi->sf_nblk = 0;
	dsi->sf_rate = 0xfa;
	switch (SFDTYPE(md->md_flags)) {
	default:
	case 0:
		dsi->sf_mdb = 0x1e;
		break;
	case 1:
		dsi->sf_mdb = 0x16;
		break;
	}
	
	/*
	 * Set default disk geometry and partition table.
	 * This is necessary so we can later open the device
	 * if we don't find it at probe time.
	 */
	bzero((caddr_t)&(dsi->un_g), sizeof (struct dk_geom));
	bzero((caddr_t)dsi->un_map, sizeof (struct dk_allmap));
	dsi->un_g.dkg_ncyl  = 78;
	dsi->un_g.dkg_acyl  = 0;
	dsi->un_g.dkg_pcyl  = 80;
	dsi->un_g.dkg_nhead = 2;
	dsi->un_g.dkg_nsect = 9;
	dsi->un_cyl_size = 9;

	/*
	 * Allocate space for request sense/inquiry buffer in
	 * Multibus memory.  Align it to a longword boundary.
	 */
	sid = (struct scsi_inquiry_data *)rmalloc(iopbmap,
		(long)(sizeof (struct scsi_inquiry_data) + 4));
	if (sid == NULL) {
		printf("sf%d:  sfattach: no space for inquiry data\n",
		       SFNUM(un));
		return;
	}

	while ((u_int)sid & 0x03)
		((u_int)sid)++;
	dsi->un_sense = (int)sid;

#ifdef notdef
	EPRINTF2("sf%d:  sfattach: buffer= 0x%x, ", SFNUM(un), (int)sid);
	EPRINTF2("dsi= 0x%x, un= 0x%x\n", dsi, un);
	DPRINTF2("sfattach: looking for lun %d on target %d\n",
		 LUN(md->md_slave), TARGET(md->md_slave));

#endif
	/*
	 * Test for unit ready.  The first test checks
	 * for a non-existant device.  The other tests
	 * wait for the drive to get ready.
	 */
	if (simple(un, SC_TEST_UNIT_READY, 0, 0, 0) > 1) {
		DPRINTF("sfattach:  unit offline\n");
		return;
	}
	for (i = 0; i < MAX_BUSY; i++) {
		if (simple(un, SC_TEST_UNIT_READY, 0, 0, 0) == 0) {
			goto SFATTACH_UNIT;

		} else if (un->un_scb.chk) {
			goto SFATTACH_UNIT;

		} else if (un->un_scb.busy  &&  !un->un_scb.is) {
			EPRINTF("sfattach:  unit busy\n");
			DELAY(4000000);		/* Wait 4 Sec. */

		} else if (un->un_scb.is) {
			EPRINTF("sfattach:  reservation conflict\n");
			break;
		}
	}
	DPRINTF("sfattach:  unit offline\n");
	return;

SFATTACH_UNIT:
	if (simple(un, SC_TEST_UNIT_READY, 0, 0, 0) != 0) {
		DPRINTF("sfattach:  unit failed\n");
		return;
	}

	bzero((caddr_t)sid, (u_int)sizeof (struct scsi_inquiry_data));
	if (simple(un, SC_INQUIRY, (char *) sid - DVMA, 0,
			(int)sizeof (struct scsi_inquiry_data)) == 0) {
		/* Only CCS controllers support Inquiry command */
#ifdef		SFDEBUG
/* 		if (sf_debug > 2)
			sf_print_buffer((u_char *)sid, 32); */
#endif		SFDEBUG
		if (bcmp(sid->vid, "NCR HC F", 8) == 0) {
 			EPRINTF("sfattach:  NCR floppy controller found\n");
 			dsi->un_ctype = CTYPE_NCRFLOP;
 			dsi->un_flags = 0;	/* dis/reconnect turn on */
		} else {
			EPRINTF("sfattach:  CCS found\n");
			dsi->un_ctype = CTYPE_CCS;
			dsi->un_flags = 0;
		} 
        } else {
                EPRINTF("sfattach:  unknow conntroller type\n");
		dsi->un_ctype = CTYPE_UNKNOWN;
		return;
	}

}


/*
 * Read the label from the disk.
 * Return true if read was ok, false otherwise.
 */
static
getlabel(un, l, dev)
	register struct scsi_unit *un;
	register struct fk_label *l;
	dev_t dev;
{
	register int s;
	register struct scsi_floppy *sfp;
	int err;
	struct ms_med_parms {
		struct ccs_modesel_head_fl header;
		struct ccs_modesel_medium page5;
	} *ms_med_parms, *oms_med_parms;

 	
	sfp = &sfdisk[un->un_unit];
	/*
	 * SET DISK MEDIUM PARAMETERS (PAGE 5)
	 */
	ms_med_parms = (struct ms_med_parms *)rmalloc(iopbmap,
		(long)(sizeof (struct ms_med_parms) + 4));
	oms_med_parms = ms_med_parms;
	while ((u_int)ms_med_parms & 0x03)
		((u_int)ms_med_parms)++;
	bzero((char *)ms_med_parms, sizeof (struct ms_med_parms));
 	/* First get them */
	if (sfcmd(dev, SC_MODE_SENSE, 0x5, 
		     sizeof (struct ms_med_parms), 
		     (caddr_t)ms_med_parms, 0)) {
		EPRINTF("Can not get characteristics\n");
		return (ENXIO);
	}

               /* set page5 stuff */
	sfp->sf_rate = 0xfa;
	sfp->sf_spt = 9;
	ms_med_parms->header.medium = sfp->sf_mdb;
	ms_med_parms->page5.xfer_rate = sfp->sf_rate;
	ms_med_parms->page5.data_bytes = 512;
	ms_med_parms->page5.nhead = 2;
	ms_med_parms->page5.sec_trk = 9;
 	ms_med_parms->page5.ncyl = 0x50;
 	ms_med_parms->page5.ssn = 1;
	s = splr(pritospl(un->un_mc->mc_intpri));
	if (sfcmd(dev, SC_MODE_SELECT, 0x5, 
		     sizeof (struct ms_med_parms), 
		     (caddr_t)ms_med_parms, 0)) {
		EPRINTF("Can not set characteristics\n");
		rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
		  (u_long)oms_med_parms);
		(void) splx(s);
		return (ENXIO);
	}
	err = sfcmd(dev, SC_READ_LABEL, 1, 1, (caddr_t)l, 0);
	if (err == 0) {
		rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
				  (u_long)oms_med_parms);
		(void) splx(s);
		return (1);
	}
/* the stupid floppy could be high density so switch and try again */
	if (sfp->sf_mdb == 0x1e) {
		sfp->sf_rate = 0x1f4;
		sfp->sf_spt = 18;
	} else {
		sfp->sf_mdb = 0x1a;
		sfp->sf_rate = 0x1f4;
		sfp->sf_spt = 15;
	}
	ms_med_parms->page5.xfer_rate = sfp->sf_rate;
	ms_med_parms->page5.sec_trk = sfp->sf_spt;
	if (sfcmd(dev, SC_MODE_SELECT, 0x5, 
		     sizeof (struct ms_med_parms), 
		     (caddr_t)ms_med_parms, 0)) {
		EPRINTF("Can not set characteristics\n");
		sfp->sf_rate = 0xfa;
		rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
		  (u_long)oms_med_parms);
		(void) splx(s);
		return (ENXIO);
	}
	err = sfcmd(dev, SC_READ_LABEL, 1, 1, (caddr_t)l, 0);
	if (err == 0) {
		rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
				  (u_long)oms_med_parms);
		(void) splx(s);
		return (1);
	}
	(void) sfcmd(dev, SC_REZERO_UNIT, 0, 0, (caddr_t)0, 0);
	sfp->sf_rate = 0xfa;
	sfp->sf_spt = 9;
	if (sfp->sf_mdb == 0x1a)
		sfp->sf_mdb = 16;
	rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
		  (u_long)oms_med_parms);
	(void) splx(s);
	return (0);
}

static
uselabel(un, l, dev)
	register struct scsi_unit *un;
	register struct fk_label *l;
	dev_t dev;
	{       
	register struct scsi_floppy *sfp;
	struct ms_med_parms {
		struct ccs_modesel_head_fl header;
		struct ccs_modesel_medium page5;
	} *ms_med_parms, *oms_med_parms;
	

	un->un_present = 1;			/* "it's here..." */
	sfp = &sfdisk[un->un_unit];
/* set default geometry */
	sfp->sf_spt = 9;
	sfp->sf_nhead = 2;
	sfp->sf_nblk = 720 * 2;
	sfp->sf_xrate = 0xfa;
	sfp->sf_bsec = 512;
	sfp->sf_step = 1;
	sfp->un_g.dkg_ncyl = sfp->sf_nblk;
	sfp->un_g.dkg_acyl = 0;
	sfp->un_g.dkg_bcyl = 0;
	sfp->un_g.dkg_nhead = sfp->sf_nhead;
	sfp->un_g.dkg_bhead = 0;
	sfp->un_g.dkg_nsect = sfp->sf_spt;
	sfp->un_g.dkg_gap1 = 0;
	sfp->un_g.dkg_gap2 = 0;
	sfp->un_g.dkg_intrlv =0;
	sfp->un_g.dkg_pcyl = 0;
        /*
         * Old labels don't have pcyl in them, so we make a guess at it.
         */
	if (sfp->un_g.dkg_pcyl == 0)
                sfp->un_g.dkg_pcyl = sfp->un_g.dkg_ncyl + sfp->un_g.dkg_acyl;
	sfp->un_cyl_size = sfp->un_g.dkg_nhead * sfp->un_g.dkg_nsect;

	/*
	 * Fill in partition table.
	 */
        /* default to three partitions for now */
	sfp->un_map[0].dkl_cylno = 0;
	sfp->un_map[0].dkl_nblk = 8;
	sfp->un_map[2].dkl_cylno = 0;
	sfp->un_map[2].dkl_nblk = sfp->sf_nblk;
	sfp->un_map[6].dkl_cylno = 1;
	sfp->un_map[6].dkl_nblk = sfp->sf_nblk - 8;
	sfp = &sfdisk[un->un_unit];
	/*
	 * Check magic number of the label
	 */
	if ((l->fkl_magich != FKL_MAGIC) || (l->fkl_magicl != FKL_MAGIC) ) {
    		return (0);
	}

	switch (l->fkl_type) {
	case DSHSPT:
	    if (sfp->sf_rate == 0xfa) {
		    sfp->sf_spt = 9;
		    sfp->sf_nblk = 1440;
		    sfp->sf_xrate = 0xfa;
	    } else {
		    sfp->sf_spt = 18;
		    sfp->sf_nblk = 2880;
		    sfp->sf_xrate = 0x1f4;
	    }
	    sfp->sf_nhead = 2;
	    sfp->sf_bsec = 512;
	    sfp->sf_step = 1;
	    break;

	case SS8SPT:
	    sfp->sf_spt = 8;
	    sfp->sf_nhead = 1;
	    sfp->sf_nblk = 640;
	    sfp->sf_xrate = 0xfa;
	    sfp->sf_bsec = 512;
	    sfp->sf_step = 1;
	    break;

	case DS8SPT:
	    sfp->sf_spt = 8;
	    sfp->sf_nhead = 2;
	    sfp->sf_nblk = 640 * 2;
	    sfp->sf_xrate = 0xfa;
	    sfp->sf_bsec = 512;
	    sfp->sf_step = 1;
	    break;

	case SS9SPT:
	    sfp->sf_spt = 9;
	    sfp->sf_nhead = 1;
	    sfp->sf_nblk = 720;
	    sfp->sf_xrate = 0xfa;
	    sfp->sf_bsec = 512;
	    sfp->sf_step = 1;
	    break;

	case DS9SPT:
	    sfp->sf_spt = 9;
	    sfp->sf_nhead = 2;
	    sfp->sf_nblk = 720 * 2;
	    sfp->sf_xrate = 0xfa;
	    sfp->sf_bsec = 512;
	    sfp->sf_step = 1;
	    break;

	default:
	    sfp->sf_spt = 9;
	    sfp->sf_nhead = 2;
	    sfp->sf_nblk = 720 * 2;
	    sfp->sf_xrate = 0xfa;
	    sfp->sf_bsec = 512;
	    sfp->sf_step = 1;
	    EPRINTF2("sf%d: unknown label %x\n", un->un_unit, sfp->sf_mdb);
	    break;

	}
        /*
	 * Fill in disk geometry from label.
	 */
	un->un_present = 1;			/* "it's here..." */
	sfp->un_g.dkg_ncyl = sfp->sf_nblk;
	sfp->un_g.dkg_acyl = 0;
	sfp->un_g.dkg_bcyl = 0;
	sfp->un_g.dkg_nhead = sfp->sf_nhead;
	sfp->un_g.dkg_bhead = 0;
	sfp->un_g.dkg_nsect = sfp->sf_spt;
	sfp->un_g.dkg_gap1 = 0;
	sfp->un_g.dkg_gap2 = 0;
	sfp->un_g.dkg_intrlv =0;
	sfp->un_g.dkg_pcyl = 0;
        /*
         * Old labels don't have pcyl in them, so we make a guess at it.
         */
	if (sfp->un_g.dkg_pcyl == 0)
                sfp->un_g.dkg_pcyl = sfp->un_g.dkg_ncyl + sfp->un_g.dkg_acyl;
	sfp->un_cyl_size = sfp->un_g.dkg_nhead * sfp->un_g.dkg_nsect;

	/*
	 * Fill in partition table.
	 */
        /* default to three partitions for now */
	sfp->un_map[0].dkl_cylno = 0;
	sfp->un_map[0].dkl_nblk = 8;
	sfp->un_map[2].dkl_cylno = 0;
	sfp->un_map[2].dkl_nblk = sfp->sf_nblk;
	sfp->un_map[6].dkl_cylno = 1;
	sfp->un_map[6].dkl_nblk = sfp->sf_nblk - 8;
	/*
	 * SET DISK MEDIUM PARAMETERS (PAGE 5)
	 */
	ms_med_parms = (struct ms_med_parms *)rmalloc(iopbmap,
		(long)(sizeof (struct ms_med_parms) + 4));
	oms_med_parms = ms_med_parms;
	while ((u_int)ms_med_parms & 0x03)
		((u_int)ms_med_parms)++;
	bzero((char *)ms_med_parms, sizeof (struct ms_med_parms));
/* First get them */
	if (sfcmd(dev, SC_MODE_SENSE, 0x5, 
		     sizeof (struct ms_med_parms), 
		     (caddr_t)ms_med_parms, 0)) {
		EPRINTF("Can not get characteristics\n");
		return (ENXIO);
	}



               /* set page5 stuff */
	ms_med_parms->header.medium = sfp->sf_mdb;
	ms_med_parms->page5.xfer_rate = sfp->sf_rate;
	ms_med_parms->page5.nhead = sfp->sf_nhead;
	ms_med_parms->page5.sec_trk = sfp->sf_spt;
	ms_med_parms->page5.data_bytes = 512;
	ms_med_parms->page5.ncyl = 0x50;

	if (sfcmd(dev, SC_MODE_SELECT, 0x5, 
		     sizeof (struct ms_med_parms), 
		     (caddr_t)ms_med_parms, 0)) {
		EPRINTF("Can not set characteristics\n");
		rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
		  (u_long)oms_med_parms);
		return (ENXIO);
	}
	rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
		  (u_long)oms_med_parms);
	return (0);
}

/*
 * Run a command in polled mode.
 * Return true if successful, false otherwise.
 */
static
simple(un, cmd, dma_addr, secno, nsect)
	register struct scsi_unit *un;
	register int cmd, secno, nsect, dma_addr;
	{       
	register struct scsi_cdb *cdb;
	register struct scsi_ctlr *c;
	register int err;

	/*
	 * Grab and clear the command block.
	 */
	c = un->un_c;
	cdb = &un->un_cdb;
	bzero((caddr_t)cdb, CDB_GROUP0);

	/*
	 * Plug in command block values.
	 */
	cdb->cmd = cmd;
	if (SCSI_RECV_DATA_CMD(cmd))
		un->un_flags |= SC_UNF_RECV_DATA;
	else
		un->un_flags &= ~SC_UNF_RECV_DATA;

	c->c_un = un;
	cdb->lun = un->un_lun;
	FORMG0ADDR(cdb, secno);
	FORMG0COUNT(cdb, nsect);
	un->un_dma_addr = dma_addr;
	if (cmd == SC_INQUIRY)
		un->un_dma_count = nsect;
	else
		un->un_dma_count = nsect << SECDIV;

	/*
	 * Fire up the pup.
	 */
	if (err = (*c->c_ss->scs_cmd)(c, un, 0)) {
		if (err > 1) {
			return (2);	/* Hard failure */
		} else {
			return (1);	/* Recoverable failure */
		}
	}
	return (0);			/* No failure */
}


/*
 * This routine opens a disk.  Note that we can handle disks
 * that make an appearance after boot time.
 */
/*ARGSUSED*/
sfopen(dev, flag)
	dev_t dev;
	int flag;
{
	register struct scsi_unit *un;
	register struct fk_label *l;
	register struct dk_map *lp;
	register int unit;
	register struct scsi_floppy *dsi;
	register struct scsi_inquiry_data *sid;
	struct fk_label *old_l;

	unit = SFUNIT(dev);
	if (unit >= nsfdisk) {
		EPRINTF("sfopen:  illegal unit\n");
		return (ENXIO);
	}

	un = &sfunits[unit];
	dsi = &sfdisk[unit];
	if (un->un_mc == 0) {
		EPRINTF("sfopen:  disk not atached\n");
		return (ENXIO);
	}

	/*
	 * If command timeouts not activated yet, switch it on.
	 */
#ifdef	SF_TIMEOUT
	if (dsi->un_timer == 0) {
		EPRINTF("sfopen:  starting timer\n");
		dsi->un_timer++;
		timeout(sftimer, (caddr_t)dev, 30*hz);
	}
#endif	SF_TIMEOUT

	/*
	 * Check for special opening mode.
	 */
		EPRINTF("sfopen:  opening device\n");
		lp = &dsi->un_map[LPART(dev)];
		lp->dkl_nblk = 1536;

		dsi->un_openf = OPENING;
		dsi->un_flags = SC_UNF_NO_DISCON;	/* no disconnects */
		if (sfcmd(dev, SC_TEST_UNIT_READY, 0, 0, (caddr_t)0, 0)  &&
		    dsi->un_openf == CLOSED) {
			EPRINTF("sfopen:  not ready\n");
			dsi->un_timer = 0;
			untimeout(sftimer, (caddr_t)dev);
			return (ENXIO);
		}

		dsi->un_openf = OPENING;
		(void) sfcmd(dev, SC_TEST_UNIT_READY, 0, 0, (caddr_t)0, 0);
		dsi->un_openf = OPENING;
		if (sfcmd(dev, SC_TEST_UNIT_READY, 0, 0, (caddr_t)0, 0)) {
			EPRINTF("sfopen:  not ready\n");
			dsi->un_timer = 0;
			untimeout(sftimer, (caddr_t)dev);
			return (ENXIO);
		}
		dsi->un_openf = OPENING;
		sid = (struct scsi_inquiry_data *)dsi->un_sense;
		bzero((caddr_t)sid, (u_int)sizeof (struct scsi_inquiry_data));
		if (sfcmd(dev, SC_INQUIRY, 0, 
			sizeof (struct scsi_inquiry_data), (caddr_t)sid, 0)) {
			EPRINTF("sfopen:  unknown controller type\n");
			dsi->un_ctype = CTYPE_UNKNOWN;
			return (ENXIO);
		} else {
#ifdef			SFDEBUG
/* 			if (sf_debug > 2)
				sf_print_buffer((u_char *)sid, 32); */
#endif			SFDEBUG
			dsi->un_flags = 0;
			if (bcmp(sid->vid, "NCR HC F", 8) == 0) {
/* 				EPRINTF("sfopen: NCR floppy controller found\n"); */
				dsi->un_ctype = CTYPE_NCRFLOP;
			} else {
				EPRINTF("sfopen:  CCS found\n");
				dsi->un_ctype = CTYPE_CCS;
			}
		}


		/* Allocate space for label on longword boundary */
		old_l =	l =
			(struct fk_label *)rmalloc(iopbmap, (long)(SECSIZE +4));
		if (l == NULL) {
			printf("sf%d:  no space for disk label\n", SFNUM(un));
			return (ENXIO);
		}
		while ((u_int)l & 0x03)
			((u_int)l)++;

		DPRINTF("sfopen:  reading label\n");
		dsi->un_openf = OPEN;
		dsi->un_restores = 1/* MAX_RESTORES */;
		if (getlabel(un, l, dev)) {
			if (uselabel(un, l, dev)) {
			rmfree(iopbmap, (long)(SECSIZE + 4), (u_long)old_l);
			if (flag & O_NDELAY) {
				EPRINTF("sfopen:  suppressing label error\n");
				return (0);
			}
			return (0);
		}
		rmfree(iopbmap, (long)(SECSIZE + 4), (u_long)old_l);
		return (0);
	}
	return (0);
}

/*
 * Dummy close routine
 */

/*ARGSUSED*/
sfclose(dev,flag)
	dev_t dev;
	int flag;
{
	return (0);
}

/*
 * This routine returns the size of a logical partition.  It is called
 * from the device switch at normal priority.
 */
sfsize(dev)
	register dev_t dev;
{
	register struct scsi_unit *un;
	register struct dk_map *lp;
	register struct scsi_floppy *dsi;

	if (SFUNIT(dev) >= nsfdisk) {
		EPRINTF("sfsize:  illegal unit\n");
		return (-1);
	}
	un = &sfunits[SFUNIT(dev)];
	/* DPRINTF("sfsize:\n"); */

	if (un->un_present) {
	/* 	DPRINTF("sfsize:  getting info\n"); */
		dsi = &sfdisk[SFUNIT(dev)];
		lp = &dsi->un_map[LPART(dev)];
		return (lp->dkl_nblk);
	} else {
/* 		EPRINTF("sfsize:  unit not present\n"); */
		return (-1);
	}
}


/*
 * This routine is the focal point of internal commands to the controller.
 * NOTE: this routine assumes that all operations done before the disk's
 * geometry is defined.
 */
sfcmd(dev, cmd, block, count, addr, options)
	dev_t dev;
	u_int cmd, block, count;
	caddr_t addr;
	int options;
{
	register struct scsi_floppy *dsi;
	register struct scsi_unit *un;
	register struct buf *bp;
	int s;
	long b_flags;

	un = &sfunits[SFUNIT(dev)];
	dsi = &sfdisk[SFUNIT(dev)];
	bp = &un->un_sbuf;

	/* DPRINTF("sfcmd:\n"); */
	/* s = splr(pritospl(un->un_mc->mc_intpri)); */
	s = splr(pritospl(3));
	while (bp->b_flags & B_BUSY) {
		bp->b_flags |= B_WANTED;
		/* DPRINTF1("sfcmd:  sleeping...,  bp= 0x%x\n", bp); */
		(void) sleep((caddr_t) bp, PRIBIO);
	}

	bp->b_flags = B_BUSY | B_READ;
	(void) splx(s);
	/* DPRINTF1("sfcmd:  waking...,  bp= 0x%x\n", bp); */
/* 	DPRINTF1("sfcmd: waking up with addr=0x%x\n", addr); */

	un->un_scmd = cmd;
	bp->b_dev = dev;
	bp->b_bcount = count;
	bp->b_blkno = block;
	bp->b_un.b_addr = addr;
	dsi->un_options = options;

#ifdef  SF_FORMAT
	if (options & (SD_DVMA_RD | SD_DVMA_WR)) {
/*   		DPRINTF2("sfcmd:  addr= 0x%x size= 0x%x\n", addr, count); */
		bp->b_flags |= B_PHYS;
		bp->b_proc = u.u_procp;
		u.u_procp->p_flag |= SPHYSIO;
		/*
		 * Fault lock the address range of the buffer.
		 */
		if (as_fault(u.u_procp->p_as, bp->b_un.b_addr,
		   	 (u_int)count, F_SOFTLOCK,
			 (options & SD_DVMA_WR) ? S_WRITE : S_READ))  {

			EPRINTF("sfcmd:  as_fault error1\n");
			b_flags = bp->b_flags = B_ERROR;
			goto SFCMD_EXIT;
		}
	}
#endif  SF_FORMAT
	/*
	 * Execute the I/O request.
	 */
	sfstrategy(bp);
	(void) iowait(bp);

#ifdef  SF_FORMAT
	/*
	 * Release memory and DVMA resources.
	 */
	if (options & (SD_DVMA_RD | SD_DVMA_WR)) {
		if (as_fault(u.u_procp->p_as, bp->b_un.b_addr, 
			 (u_int)bp->b_bcount, F_SOFTUNLOCK,
			 (options & SD_DVMA_WR) ? S_WRITE : S_READ)) {

			EPRINTF("sfcmd:  as_fault error2\n");
			b_flags = bp->b_flags = B_ERROR;
			goto SFCMD_EXIT;
		}
		s = splr(pritospl(3));
		/* s = splr(pritospl(un->un_mc->mc_intpri));*/
		u.u_procp->p_flag &= ~SPHYSIO;
		bp->b_flags &= ~(B_BUSY | B_PHYS);
		(void) splx(s);
	}

SFCMD_EXIT:
#endif  SF_FORMAT

	bp->b_flags &= ~B_BUSY;
	b_flags = bp->b_flags;
	if (bp->b_flags & B_WANTED) {
		/* DPRINTF1("sfcmd:  waking...,  bp= 0x%x\n", bp); */
		wakeup((caddr_t)bp);
	}
	return (b_flags & B_ERROR);
}


/*
 * This routine is the high level interface to the disk.  It performs
 * reads and writes on the disk using the buf as the method of communication.
 * It is called from the device switch for block operations and via physio()
 * for raw operations.  It is called at normal priority.
 */
sfstrategy(bp)
	register struct buf *bp;
{
	register struct scsi_unit *un;
	register struct mb_device *md;
	register struct dk_map *lp;
	register u_int bn, cyl;
	register struct diskhd *dh;
	register struct scsi_floppy *dsi;
	register int s;
	int unit;

	/* DPRINTF("sfstrategy:\n"); */
	unit = dkunit(bp);
	if (unit >= nsfdisk) {
		EPRINTF("sfstrategy: invalid unit\n");
		bp->b_flags |= B_ERROR;
		iodone(bp);
		return;
	}
	un = &sfunits[unit];
	md = un->un_md;
	dsi = &sfdisk[unit];
	lp = &dsi->un_map[LPART(bp->b_dev)];/* which partition */
	bn = bp->b_blkno;

	if (bp != &un->un_sbuf)  {
		if ((un->un_present == 0)  ||  (lp->dkl_nblk == 0)) {
			EPRINTF("sfstrategy:  unit not present\n");
			EPRINTF2("sfstrategy:  bn= 0x%x  nblk= 0x%x\n",
				bn, lp->dkl_nblk);
			bp->b_flags |= B_ERROR;
			iodone(bp);
			return;
		}
		/* Check for EOM */
		if (un->un_present  &&  (bn >= lp->dkl_nblk)) {
			EPRINTF("sfstrategy:  invalid block addr\n");
			bp->b_resid = bp->b_bcount;
			iodone(bp);
			return;
		}
	}

	if ((bn >= dsi->un_cyl_start)  &&  (bn < dsi->un_cyl_end)  &&
	    ( (int)lp == dsi->un_lp1)) {
				/* DPRINTF("WIN  "); */
		bp->b_cylin = dsi->un_cylin_last;

	} else {
 				/* DPRINTF("LOSE\n"); */
		dsi->un_lp1 = (int)lp;
		cyl = bn / dsi->un_cyl_size;

		dsi->un_cyl_start = cyl * dsi->un_cyl_size;
		dsi->un_cyl_end   = dsi->un_cyl_start + dsi->un_cyl_size;

		cyl += lp->dkl_cylno + dsi->un_g.dkg_bcyl;
		bp->b_cylin = dsi->un_cylin_last = cyl;
	}
	s = splr(pritospl(un->un_mc->mc_intpri));
	dh = &md->md_utab;
	disksort(dh, bp);

	/* call unit start routine to queue up device, if it
	 * currently isn't queued.  Does not actually start command
	 */
	(*un->un_c->c_ss->scs_ustart)(un);

	/* call start routine to run the next SCSI command */
        /* Now we start the command */
	(*un->un_c->c_ss->scs_start)(un);
	(void) splx(s);
}


/*
 * Set up a transfer for the controller
 */
sfstart(bp, un)
	register struct buf *bp;
	register struct scsi_unit *un;
{
	register struct dk_map *lp;
	register struct scsi_floppy *dsi;
	register int nblk;

/* 	DPRINTF("sfstart:\n"); */
		
	dsi = &sfdisk[dkunit(bp)];
	lp = &dsi->un_map[LPART(bp->b_dev)];

	/* Process internal I/O requests */
	if (bp == &un->un_sbuf) {
/* 		DPRINTF("sfstart:  internal I/O\n"); */
		un->un_cmd = un->un_scmd;
		un->un_count = bp->b_bcount;
		un->un_blkno = bp->b_blkno;
		un->un_flags = 0;
		if (dsi->un_options & (SD_DVMA_RD | SD_DVMA_WR))
			un->un_flags = SC_UNF_SPECIAL_DVMA;
		return (1);
	}

	/* Process file system I/O requests */
	if (bp->b_flags & B_READ) {
				/* DPRINTF("sfstart:  read\n"); */
		un->un_cmd = SC_READ;
	} else {
				/* DPRINTF("sfstart:  write\n"); */
		un->un_cmd = SC_WRITE;
	}

	/* Compute absolute block location */
	if ((int)lp == dsi->un_lp2) {
		un->un_blkno = bp->b_blkno + dsi->un_last_cyl_size;
	} else {
		dsi->un_lp2 = (int)lp;
		dsi->un_last_cyl_size = lp->dkl_cylno * dsi->un_cyl_size;
		un->un_blkno = bp->b_blkno + dsi->un_last_cyl_size;
	}

	/* Finish processing read/write request */
	nblk = (bp->b_bcount + (SECSIZE - 1)) >> SECDIV;
	un->un_count = MIN(nblk, (lp->dkl_nblk - bp->b_blkno));
	un->un_flags = SC_UNF_DVMA;
	return (1);
}


/*
 * Make a cdb for disk I/O.
 */
sfmkcdb(un)
	struct scsi_unit *un;
{
	register struct scsi_cdb *cdb;
	register struct scsi_floppy *dsi;

/* 	DPRINTF("sfmkcdb:\n"); */

	dsi = &sfdisk[un->un_unit];
	cdb = &un->un_cdb;
	bzero((caddr_t)cdb, CDB_GROUP0);
	cdb->cmd = un->un_cmd;
	cdb->lun = un->un_lun;

	switch (un->un_cmd) {
	case SC_READ:
/* 		DPRINTF("sfmkcdb:  read\n"); */
		dsi->un_timeout = 4;
		un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
		FORMG0ADDR(cdb, un->un_blkno);
		FORMG0COUNT(cdb, un->un_count);
		un->un_dma_addr = un->un_baddr;
		un->un_dma_count = un->un_count << SECDIV;
		break;

	case SC_WRITE:
/* 		DPRINTF("sfmkcdb:  write\n"); */
		dsi->un_timeout = 4;
		un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
				dsi->un_flags;
		FORMG0ADDR(cdb, un->un_blkno);
		FORMG0COUNT(cdb, un->un_count);
		un->un_dma_addr = un->un_baddr;
		un->un_dma_count = un->un_count << SECDIV;
		break;

	case SC_WRITE_VERIFY:
/* 		DPRINTF("sfmkcdb:  write verify\n"); */
		dsi->un_timeout = 4;
		un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
				dsi->un_flags;
		FORMG1ADDR(cdb, un->un_blkno);
		FORMG1COUNT(cdb, un->un_count);
		un->un_dma_addr = un->un_baddr;
		un->un_dma_count = un->un_count << SECDIV;
		break;

	case SC_REQUEST_SENSE:
		DPRINTF("sfmkcdb:  request sense\n");
		dsi->un_timeout = 4;
		un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
		FORMG0COUNT(cdb,  sizeof (struct scsi_sense));
		un->un_dma_addr = (int)dsi->un_sense - (int)DVMA;
		un->un_dma_count = sizeof (struct scsi_sense);
		bzero((caddr_t)(dsi->un_sense), sizeof (struct scsi_sense));
		return;

	case SC_SPECIAL_READ:
/* 		DPRINTF1("sfmkcdb:  special read blkno= 0x%x\n", un->un_blkno); */
		dsi->un_timeout = 4;
		un->un_cmd = cdb->cmd = SC_READ;
		un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
		FORMG0ADDR(cdb, un->un_blkno);
		FORMG0COUNT(cdb, un->un_count >> SECDIV);
		un->un_dma_addr = un->un_baddr;
		un->un_dma_count = un->un_count;
		break;

	case SC_SPECIAL_WRITE:
/* 		DPRINTF1("sfmkcdb:  special write blkno= 0x%x\n", un->un_blkno); */
		dsi->un_timeout = 4;
		un->un_cmd = cdb->cmd = SC_WRITE;
		un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
				dsi->un_flags;
		FORMG0ADDR(cdb, un->un_blkno);
		FORMG0COUNT(cdb, un->un_count >> SECDIV);
		un->un_dma_addr = un->un_baddr;
		un->un_dma_count = un->un_count;
		break;

	case SC_READ_LABEL:
		DPRINTF("sfmkcdb:  read label\n");
		dsi->un_timeout = 4;
		un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
		un->un_cmd = cdb->cmd = SC_READ;
		FORMG0ADDR(cdb, un->un_blkno);
		FORMG0COUNT(cdb,  1);
		un->un_dma_addr = (int)un->un_sbuf.b_un.b_addr - (int)DVMA;
		un->un_dma_count = SECSIZE;
		break;

	case SC_REZERO_UNIT:
		EPRINTF("sfmkcdb:  rezero unit\n");
		dsi->un_timeout = 4;
		un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
				dsi->un_flags;
		un->un_dma_addr = 0;
		un->un_dma_count = 0;
		return;

	case SC_TEST_UNIT_READY:
		/* DPRINTF("sfmkcdb:  test unit ready\n"); */
		dsi->un_timeout = 2;
		un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
				dsi->un_flags;
		un->un_dma_addr = 0;
		un->un_dma_count = 0;
		break;

	case SC_INQUIRY:
		DPRINTF("sfmkcdb:  inquiry\n");
		dsi->un_timeout = 2;
		un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
		FORMG0COUNT(cdb, sizeof (struct scsi_inquiry_data));
		un->un_dma_addr = (int)dsi->un_sense - (int)DVMA;
		un->un_dma_count = sizeof (struct scsi_inquiry_data);
		bzero((caddr_t)(dsi->un_sense), sizeof (struct scsi_inquiry_data));
		break;

	case SC_FORMAT:
		EPRINTF("sfmkcdb:  format\n");
		DPRINTF1("sfmkcdb:  count= 0x%d\n", un->un_count);
		dsi->un_timeout = 60;
		un->un_dma_addr = un->un_baddr;
		un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
				dsi->un_flags;
/* 		cdb->fmt_parm_bits =  FPB_BFI; XXXXX ???*/
		cdb->fmt_parm_bits =  0;
		un->un_dma_count = un->un_count;
		cdb->fmt_interleave = 1;
		break;

	case SC_MODE_SELECT:
		EPRINTF("sfmkcdb:  mode select\n");
		dsi->un_timeout = 2;
		cdb->tag |= 0x10;
		un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
				dsi->un_flags;
		FORMG0COUNT(cdb, un->un_count);
		un->un_dma_count = un->un_count;
		un->un_dma_addr = (int)un->un_sbuf.b_un.b_addr - (int)DVMA;
/* 		un->un_dma_addr = un->un_baddr; */
		break;

	case SC_MODE_SENSE:
/* 		EPRINTF("sfmkcdb:  mode sense\n"); */
		dsi->un_timeout = 2;
		un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
		FORMG0COUNT(cdb, un->un_count);
		cdb->g0_addr1  = un->un_blkno;
		un->un_dma_count = un->un_count;
		un->un_dma_addr = (int)un->un_sbuf.b_un.b_addr - (int)DVMA;
/* 		un->un_dma_addr = un->un_baddr; */
		break;

	case SC_READ_DEFECT_LIST:
		EPRINTF("sfmkcdb:  read defect list- NOT SUPPORTED\n");
		dsi->un_timeout = 4;
		un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
				dsi->un_flags;
		break;

	case SC_REASSIGN_BLOCK:
		EPRINTF1("sfmkcdb:  reassign block 0x%x NOT SUPPORTED\n", 
			    un->un_blkno);
		dsi->un_timeout = 4;
		un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
				dsi->un_flags;
		break;
	default:
		EPRINTF("sfmkcdb:  unknown command\n");
		dsi->un_timeout = 4;
		un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
				dsi->un_flags;
		break;
	}
	dsi->un_last_cmd = un->un_cmd;
	return;
}


/*
 * This routine handles controller interrupts.
 * It is always called at disk interrupt priority.
 */
typedef enum sfintr_error_resolution {	
	real_error, 		/* Hard error */
	psuedo_error, 		/* What looked like an error is actually OK */
	more_processing 	/* Recoverable error */
} sfintr_error_resolution;
sfintr_error_resolution sferror();

sfintr(c, resid, error)
	register struct scsi_ctlr *c;
	u_int resid;
	int error;
{
	register struct scsi_unit *un;
	register struct scsi_floppy *dsi;
	register struct buf *bp;
	register struct mb_device *md;
	int status = 0;
	int error_code = 0;
	u_char severe = DK_NOERROR;

	/* DPRINTF("sfintr:\n"); */
	un = c->c_un;
	md = un->un_md;
	bp = md->md_utab.b_forw;
	dsi = &sfdisk[SFUNIT(bp->b_dev)];
	dsi->un_timeout = 0;		/* Disable time-outs */

/* XXXX What it this? */
	if (md->md_dk >= 0) {
		dk_busy &= ~(1 << md->md_dk);
	}

	/*
	 * Check for error or special operation states and process.
	 * Otherwise, it was a normal I/O command which was successful.
	 */
	if (dsi->un_openf < OPEN  ||  error  ||  resid ) {

		/*
		 * Special processing for SCSI bus failures.
		 */
		if (error == SE_FATAL) {
			if (dsi->un_openf == OPEN) {
				printf("sf%d:  scsi bus failure\n", SFNUM(un));
			}
			dsi->un_retries = dsi->un_restores = 0;
			dsi->un_err_severe = DK_FATAL;
			un->un_present = 0;
			dsi->un_openf = CLOSED;
			bp->b_flags |= B_ERROR;
			goto SFINTR_WRAPUP;
		}

		/*
		 * Opening disk, check if command failed.  If it failed
		 * close device.  Otherwise, it's open.
		 */
		if (dsi->un_openf == OPENING) {
			if (error == SE_NO_ERROR) {
				dsi->un_openf = OPEN;
				goto SFINTR_SUCCESS;
			} else if (error == SE_RETRYABLE) {
				DPRINTF("sfintr:  open failed\n");
				dsi->un_openf = OPEN_FAILED;
				bp->b_flags |= B_ERROR;
				goto SFINTR_WRAPUP;
			} else {
				EPRINTF("sfintr:  hardware failure\n");
				dsi->un_openf = CLOSED;
				bp->b_flags |= B_ERROR;
				goto SFINTR_WRAPUP;
			}
		}

		/*
		 * Rezero for failed command done, retry failed command
		 */
		if ((dsi->un_openf == RETRYING)  &&
		    (un->un_cdb.cmd == SC_REZERO_UNIT)) {

			if (error)
				printf("sf%d:  rezero failed\n", SFNUM(un));

			DPRINTF("sfintr:  rezero done\n");
			un->un_flags &= ~SC_UNF_GET_SENSE;
			un->un_cdb = un->un_saved_cmd.saved_cdb;
			un->un_dma_addr = un->un_saved_cmd.saved_dma_addr;
			un->un_dma_count = un->un_saved_cmd.saved_dma_count;
			un->un_cmd = dsi->un_last_cmd;
			sfmkcdb(un);
			(*c->c_ss->scs_cmd)(c, un, 1);
			return;
		}


		/*
		 * Command failed, need to run request sense command.
		 */
		if ((dsi->un_openf == OPEN)  &&  error) {
			sfintr_sense(dsi, un, resid);
			return;
		}

		/*
		 * Request sense command done, restore failed command.
		 */
		if (dsi->un_openf == SENSING) {
			DPRINTF("sfintr:  restoring sense\n");
			sfintr_ran_sense(un, dsi, &resid);
		}

		/*
		 * Reassign done, restore original state
		 */
		if (dsi->un_openf == MAPPING) {
			DPRINTF("sfintr:  restoring state\n");
			sfintr_ran_reassign(un, dsi, &resid);
		}

		EPRINTF2("sfintr:  retries= %d  restores= %d  ",
			 dsi->un_retries, dsi->un_restores);
		EPRINTF1("total= %d\n", dsi->un_total_errors);
		if ((dsi->un_openf == RETRYING)  &&  (error == 0)) {

			EPRINTF("sfintr:  ok\n\n");
			dsi->un_openf = OPEN;
			dsi->un_retries = dsi->un_restores = 0;
			dsi->un_err_severe = DK_RECOVERED;
			goto SFINTR_SUCCESS;
		}

		/*
		 * Process all other errors here
		 */
#ifdef notdef
		EPRINTF2("sfintr:  processing error,  error= %x  chk= %x",
			error, un->un_scb.chk);
		EPRINTF1("  busy= %x", un->un_scb.busy);
#endif
		EPRINTF2("  resid= %d (%d)\n", resid, un->un_dma_count);
		switch (sferror(c, un, dsi, bp, resid, error)) {
		case real_error:
			/* This error is FATAL ! */
/* 			DPRINTF("sfintr:  real error\n"); */
			SF_PRINT_CMD(un);
			dsi->un_retries = dsi->un_restores = 0;
			bp->b_flags |= B_ERROR;
			goto SFINTR_WRAPUP;

		case psuedo_error:
			/* A psuedo-error:  soft error reported by ctlr */
			DPRINTF("sfintr:  psuedo error\n");
			status = dsi->un_status;
			error_code = dsi->un_err_code;
			severe = dsi->un_err_severe;
			dsi->un_retries = dsi->un_restores = 0;
			goto SFINTR_SUCCESS;

		case more_processing:
			/* real error requiring error recovery */
/*			DPRINTF("stintr:  more processing\n"); */
			return;
		}
	}


	/*
	 * Handle successful Transfer.  Also, take care of 
	 * seek error problem by doing single sector I/O.
	 */
SFINTR_SUCCESS:
	dsi->un_status = status;
	dsi->un_err_code = error_code;
	dsi->un_err_severe = severe;

	if (dsi->un_sec_left) {
		EPRINTF("sfintr:  single sector writes\n");
		dsi->un_sec_left--;
		un->un_baddr += SECSIZE;
		un->un_blkno++;
		sfmkcdb(un);
		if ((*c->c_ss->scs_cmd)(c, un, 1) != 0)
			printf("sf%d:  single sector I/O failed\n", SFNUM(un));
	}


	/*
	 * Handle I/O request completion (both sucessful and failed).
	 */
SFINTR_WRAPUP:
	if (bp == &un->un_sbuf  &&
	    ((un->un_flags & SC_UNF_DVMA) == 0)) {
		bp->b_resid = 0;
		if (un->un_flags & SC_UNF_SPECIAL_DVMA)
			mbudone(un->un_md);
		else
			(*c->c_ss->scs_done)(un->un_md);

	} else {
		bp->b_resid = bp->b_bcount - (un->un_count << SECDIV);
		mbudone(un->un_md);
		un->un_flags &= ~SC_UNF_DVMA;
	}
}


/*
 * Disk error decoder/handler.
 */
static sfintr_error_resolution
sferror(c, un, dsi, bp, resid, error)
	register struct scsi_ctlr *c;
	register struct scsi_unit *un;
	register struct scsi_floppy *dsi;
	struct buf *bp;
	u_int resid;
	int error;
{
	register struct scsi_ext_sense *ssf;

	ssf = (struct scsi_ext_sense *)dsi->un_sense;
	/* DPRINTF("sferror:\n"); */

	/*
	 * Special processing for driver command timeout errors.
	 */
	if (error == SE_TIMEOUT) {
		EPRINTF("sferror:  command timeout error\n");
		dsi->un_status = SC_TIMEOUT;
		goto SFERROR_RETRY;
	}

	/*
	 * Check for various check condition errors.
	 */
	dsi->un_total_errors++;
	if (un->un_scb.chk) {

		switch (ssf->key) {
		case SC_RECOVERABLE_ERROR:
			dsi->un_err_severe = DK_CORRECTED;
			if (sf_retry == 0) {
				sferrmsg(un, bp, "recoverable");
			}
			if (dsi->un_options & SD_NORETRY) {
				dsi->un_err_severe = DK_CORRECTED;
				return (real_error);
			}
			return (psuedo_error);

		case SC_MEDIUM_ERROR:
			EPRINTF("sferror:  media error\n");
			goto SFERROR_RETRY;

		case SC_HARDWARE_ERROR:
			EPRINTF("sferror:  hardware error\n");
			goto SFERROR_RETRY;

		case SC_UNIT_ATTENTION:
			EPRINTF("sferror:  unit attention error\n");
			goto SFERROR_RETRY;

		case SC_NOT_READY:
			EPRINTF("sferror:  not ready\n");
			dsi->un_err_severe = DK_FATAL;
			return (real_error);

		case SC_ILLEGAL_REQUEST:
			EPRINTF("sferror:  illegal request\n");
			dsi->un_err_severe = DK_FATAL;
			return (real_error);

		case SC_VOLUME_OVERFLOW:
			EPRINTF("sferror:  volume overflow\n");
			dsi->un_err_severe = DK_FATAL;
			return (real_error);

		case SC_WRITE_PROTECT:
			EPRINTF("sferror:  write protected\n");
			dsi->un_err_severe = DK_FATAL;
			return (real_error);

		case SC_BLANK_CHECK:
			EPRINTF("sferror:  blank check\n");
			dsi->un_err_severe = DK_FATAL;
			return (real_error);

		default:
			/*
			 * Undecoded sense key.  Try retries and hope
			 * that will fix the problem.  Otherwise, we're
			 * dead.
			 */
			EPRINTF("sferror:  undecoded sense key error\n");
			SF_PRINT_CMD(un);
			dsi->un_err_severe = DK_FATAL;
			goto SFERROR_RETRY;
		}

	/*
	 * Process busy error.  Try retries and hope that
	 * it'll be ready soon.
	 */
	} else if (un->un_scb.busy  &&  !un->un_scb.is) {
		EPRINTF1("sf%d:  sferror: busy error\n", SFNUM(un));
		SF_PRINT_CMD(un);
		goto SFERROR_RETRY;

	/*
	 * Process reservation error.  Abort operation.
	 */
	} else if (un->un_scb.busy  &&  un->un_scb.is) {
		EPRINTF1("sf%d:  sferror: reservation conflict error\n",
			SFNUM(un));
		SF_PRINT_CMD(un);
		dsi->un_err_severe = DK_FATAL;
		return (real_error);

	/*
	 * Have left over residue data from last command.
	 * Do retries and hope this fixes it...
	 */
	} else  if (resid != 0) {
		EPRINTF1("sferror:  residue error, residue= %d\n", resid);
		SF_PRINT_CMD(un);
		goto SFERROR_RETRY;

	/*
	 * Have an unknown error.  Don't know what went wrong.
	 * Do retries and hope this fixes it...
	 */
	} else {
		EPRINTF("sferror:  unknown error\n");
		SF_PRINT_CMD(un);
		dsi->un_err_severe = DK_FATAL;
		goto SFERROR_RETRY;
	}

	/*
	 * Process command retries and rezeros here.
	 * Note, for on-line formatting, normal error
	 * recovery is inhibited.
	 */
SFERROR_RETRY:
	if (dsi->un_options & SD_NORETRY) {
		EPRINTF("sferror:  error recovery disabled\n");
		sferrmsg(un, bp, "failed, no retries");
		dsi->un_err_severe = DK_FATAL;
		return (real_error);
	}

	/*
	 * Command failed, retry it.
	 */
	if (dsi->un_retries++ < MAX_RETRIES) {
		if (((dsi->un_retries + dsi->un_restores) > sf_retry) ||
	    	    (dsi->un_restores != 0)) {
			sferrmsg(un, bp, "retry");
		}

		dsi->un_openf = RETRYING;
		sfmkcdb(un);
		if ((*c->c_ss->scs_cmd)(c, un, 1) != 0) {
			printf("sf%d:  sferror: retry failed\n", SFNUM(un));
			return (real_error);
		}
		return (more_processing);

	/*
	 * Retries exhausted, try restore
	 */
	} else if (++dsi->un_restores < MAX_RESTORES) {
		if (dsi->un_restores != EL_REST)
			sferrmsg(un, bp, "restore");

		/*
		 * Save away old command state.
		 */
		un->un_saved_cmd.saved_cdb = un->un_cdb;
		un->un_saved_cmd.saved_dma_addr = un->un_dma_addr;
		un->un_saved_cmd.saved_dma_count = un->un_dma_count;
		dsi->un_openf = RETRYING;
		dsi->un_retries = 0;
		un->un_cmd = SC_REZERO_UNIT;
		sfmkcdb(un);
		(void) (*c->c_ss->scs_cmd)(c, un, 1);
		return (more_processing);

	/*
	 * Restores and retries exhausted, die!
	 */
	} else {
		/* complete failure */
		sferrmsg(un, bp, "failed");
		dsi->un_openf = OPEN;
		dsi->un_retries = 0;
		dsi->un_restores = 0;
		dsi->un_err_severe = DK_FATAL;
		return (real_error);
	}
}


/*
 * Command failed, need to run a request sense command to determine why.
 */
static
sfintr_sense(dsi, un, resid)
	register struct scsi_floppy *dsi;
	register struct scsi_unit *un;
	u_int resid;
{

	/*
	 * Save away old command state.
	 */
	un->un_saved_cmd.saved_scb = un->un_scb;
	un->un_saved_cmd.saved_cdb = un->un_cdb;
	un->un_saved_cmd.saved_resid = resid;
	un->un_saved_cmd.saved_dma_addr = un->un_dma_addr;
	un->un_saved_cmd.saved_dma_count = un->un_dma_count;
	/*
	 * Note that s?start will call sfmkcdb, which
	 * will notice that the flag is set and not do
	 * the copy of the cdb, doing a request sense
	 * rather than the normal command.
	 */
/* 	DPRINTF("sfintr:  getting sense\n"); */
	un->un_flags |= SC_UNF_GET_SENSE;
	dsi->un_openf = SENSING;
	un->un_cmd = SC_REQUEST_SENSE;
	(*un->un_c->c_ss->scs_go)(un->un_md);
}


/*
 * Cleanup after running request sense command to see why the real
 * command failed.
 */
static
sfintr_ran_sense(un, dsi, resid_ptr)
	register struct scsi_unit *un;
	register struct scsi_floppy *dsi;
	u_int *resid_ptr;
{
	register struct scsi_ext_sense *ssf;

	/*
	 * Check if request sense command failed.  This should
	 * never happen!
	 */
	un->un_flags &= ~SC_UNF_GET_SENSE;
	dsi->un_openf = OPEN;
	if (un->un_scb.chk) {
		printf("sf%d:  request sense failed\n",
		       SFNUM(un));
	}

	un->un_scb = un->un_saved_cmd.saved_scb;
	un->un_cdb = un->un_saved_cmd.saved_cdb;
	*resid_ptr = un->un_saved_cmd.saved_resid;
	un->un_dma_addr = un->un_saved_cmd.saved_dma_addr;
	un->un_dma_count = un->un_saved_cmd.saved_dma_count;
	un->un_flags &= ~SC_UNF_GET_SENSE;
	un->un_cmd = dsi->un_last_cmd;
	dsi->un_openf = OPEN;

	/*
	 * Log error information.
	 */
	ssf = (struct scsi_ext_sense *)dsi->un_sense;
	dsi->un_err_resid = *resid_ptr;
	dsi->un_status = ssf->key;
	dsi->un_err_code = ssf->error_code;

	dsi->un_err_blkno = (ssf->info_1 << 24) | (ssf->info_2 << 16) |
			    (ssf->info_3 << 8)  | ssf->info_4;
	if (dsi->un_err_blkno == 0  ||  !(ssf->adr_val)) {
		/* No supplied block number, use original value */
		EPRINTF("sfintr_ran_sense:  synthesizing block number\n");
		dsi->un_err_blkno = un->un_blkno;
	}

#ifdef	SFDEBUG
	/* dump sense info on screen */
	if (sf_debug > 1) {
		sf_error_message(un, dsi);
		printf("\n");
	}
#endif	SFDEBUG
}


/*
 * Cleanup after running reassign block command.
 */
static
sfintr_ran_reassign(un, dsi, resid_ptr)
	register struct scsi_unit *un;
	register struct scsi_floppy *dsi;
	register u_int *resid_ptr;
{
	/*
	 * Check if reassign command failed.
	 */
	un->un_flags &= ~SC_UNF_GET_SENSE;
	dsi->un_openf = OPEN;
	if (un->un_scb.chk)
		printf("sf%d:  reassign block failed\n", SFNUM(un));

	un->un_scb = un->un_saved_cmd.saved_scb;
	un->un_cdb = un->un_saved_cmd.saved_cdb;
	*resid_ptr = un->un_saved_cmd.saved_resid;
	un->un_dma_addr = un->un_saved_cmd.saved_dma_addr;
	un->un_dma_count = un->un_saved_cmd.saved_dma_count;
	un->un_flags &= ~SC_UNF_GET_SENSE;
	un->un_cmd = dsi->un_last_cmd;
	dsi->un_openf = OPEN;
}


/*
 * This routine performs raw read operations.  It is called from the
 * device switch at normal priority.  It uses a per-unit buffer for the
 * operation.
 */
sfread(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	register struct scsi_unit *un;
	register int unit;

/* 	DPRINTF("sfread:\n"); */
	unit = SFUNIT(dev);
	if (unit >= nsfdisk) {
		EPRINTF("sfread:  invalid unit\n");
		return (ENXIO);
	}
	/*
	 * The following tests assume a block size which is power of 2.
	 * This allows a bit mask operation to be used instead of a
	 * divide operation thus saving considerable time.
	 */
	if (uio->uio_offset & (DEV_BSIZE -1)) {
		EPRINTF1("sfread:  block address not modulo %d\n",
			DEV_BSIZE);
		return (EINVAL);
	}
	if (uio->uio_iov->iov_len & (DEV_BSIZE -1)) {
		tracedump();
		EPRINTF2("sfread:  block length not modulo %d %d\n",
			DEV_BSIZE, uio->uio_iov->iov_len);
		return (EINVAL);
	}
	un = &sfunits[unit];
	return (physio(sfstrategy, &un->un_rbuf, dev, B_READ, minphys, uio));
}


/*
 * This routine performs raw write operations.  It is called from the
 * device switch at normal priority.  It uses a per-unit buffer for the
 * operation.
 */
sfwrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	register struct scsi_unit *un;
	register int unit;

	/* DPRINTF("sfwrite:\n"); */
	unit = SFUNIT(dev);
	if (unit >= nsfdisk) {
		EPRINTF("sfwrite:  invalid unit\n");
		return (ENXIO);
	}
	/*
	 * The following tests assume a block size which is power of 2.
	 * This allows a bit mask operation to be used instead of a
	 * divide operation thus saving considerable time.
	 */
	if (uio->uio_offset & (DEV_BSIZE -1)) {
		EPRINTF1("sfwrite:  block address not modulo %d\n",
			DEV_BSIZE);
		return (EINVAL);
	}
	if (uio->uio_iov->iov_len & (DEV_BSIZE -1)) {
		EPRINTF1("sfwrite:  block length not modulo %d\n",
			DEV_BSIZE);
		return (EINVAL);
	}
	un = &sfunits[unit];
	return (physio(sfstrategy, &un->un_rbuf, dev, B_WRITE, minphys, uio));
}


/*
 * This routine implements the ioctl calls.  It is called
 * from the device switch at normal priority.
 */
/*ARGSUSED*/
sfioctl(dev, cmd, data, flag)
	dev_t dev;
	register int cmd;
	register caddr_t data;
	int flag;
{
	register struct scsi_unit *un;
	register struct scsi_floppy *dsi;
	register struct dk_map *lp;
	register struct dk_info *info;
#ifdef SF_FORMAT
	register struct dk_conf *conf;
#endif SF_FORMAT
	register struct dk_diag *diag;
	register struct fdk_char *fchar;
	register int unit;
	register int i;
	int s;
	struct ms_med_parms {
		struct ccs_modesel_head_fl header;
		struct ccs_modesel_medium page5;
	} *ms_med_parms, *oms_med_parms;

/*  	DPRINTF("sfioctl:\n"); */
	unit = SFUNIT(dev);
	if (unit >= nsfdisk) {
		EPRINTF("sfioctl:  invalid unit\n");
		return (ENXIO);
	}
	un = &sfunits[unit];
	dsi = &sfdisk[unit];
	lp = &dsi->un_map[LPART(dev)];
	switch (cmd) {

	/*
	 * Return info concerning the controller.
	 */
	case DKIOCINFO:
		DPRINTF("sfioctl:  get info\n");
		info = (struct dk_info *)data;
		info->dki_ctlr = getdevaddr(un->un_mc->mc_addr);
		info->dki_unit = un->un_md->md_slave;

		switch (dsi->un_ctype) {
		case CTYPE_NCRFLOP:
			info->dki_ctype = DKC_NCRFLOPPY;
			break;
		default:
			info->dki_ctype = DKC_SCSI_CCS;
			break;
		}
		info->dki_flags = DKI_FMTVOL;
		return (0);

	/*
	 * Return the geometry of the specified unit.
	 */
	case DKIOCGGEOM:
		DPRINTF("sfioctl:  get geometry\n");
		*(struct dk_geom *)data = dsi->un_g;
		return (0);

	/*
	 * Set the geometry of the specified unit.
	 */
	case DKIOCSGEOM:
		EPRINTF("sfioctl:  set geometry\n");
		dsi->un_g = *(struct dk_geom *)data;
		return (0);

	/*
	 * Return the map for the specified logical partition.
	 * This has been made obsolete by the get all partitions
	 * command.
	 */
	case DKIOCGPART:
 		DPRINTF("sfioctl:  get partition\n");
	 	*(struct dk_map *)data = *lp;
		return (0);

	/*
	 * Set the map for the specified logical partition.
	 * This has been made obsolete by the set all partitions
	 * command.  We raise the priority just to make sure
	 * an interrupt doesn't come in while the map is
	 * half updated.
	 */
	case DKIOCSPART:
		EPRINTF("sfioctl:  set partitions\n");
		*lp = *(struct dk_map *)data;
		return (0);

	/*
	 * Return configuration info
	 */
	case DKIOCGCONF:
#ifdef  	SF_FORMAT
		DPRINTF("sfioctl:  get configuration info\n");
		conf = (struct dk_conf *)data;
		switch (dsi->un_ctype) {
		case CTYPE_NCRFLOP:
			conf->dkc_ctype = DKC_NCRFLOPPY;
			break;
		default:
			conf->dkc_ctype = DKC_SCSI_CCS;
			break;
		}
		(void) strncpy(conf->dkc_dname, "sf", DK_DEVLEN);
		conf->dkc_flags = DKI_FMTVOL;
		conf->dkc_cnum = un->un_mc->mc_ctlr;
		conf->dkc_addr = getdevaddr(un->un_mc->mc_addr);
		conf->dkc_space = un->un_mc->mc_space;
		conf->dkc_prio = un->un_mc->mc_intpri;
		if (un->un_mc->mc_intr)
			conf->dkc_vec = un->un_mc->mc_intr->v_vec;
		else
			conf->dkc_vec = 0;
		(void) strncpy(conf->dkc_cname, un->un_c->c_name, DK_DEVLEN);
		conf->dkc_unit = un->un_md->md_unit;
		conf->dkc_slave = un->un_md->md_slave;
#endif  	SF_FORMAT
		return (0);

	/*
	 * Return the map for all logical partitions.
	 */
	case DKIOCGAPART:
		DPRINTF("sfioctl:  get all logical partitions\n");
		for (i = 0; i < NLPART; i++)
			((struct dk_map *)data)[i] = dsi->un_map[i];
		return (0);

	/*
	 * Set the map for all logical partitions.  We raise
	 * the priority just to make sure an interrupt doesn't
	 * come in while the map is half updated.
	 */
	case DKIOCSAPART:
		EPRINTF("sfioctl:  set all logical partitions\n");
		s = splr(pritospl(un->un_mc->mc_intpri));
		for (i = 0; i < NLPART; i++)
			dsi->un_map[i] = ((struct dk_map *)data)[i];
		(void) splx(s);
		return (0);

	/*
	 * Get error status from last command.
	 */
	case DKIOCGDIAG:
		EPRINTF("sfioctl:  get error status\n");
		diag = (struct dk_diag *) data;
		diag->dkd_errcmd  = dsi->un_last_cmd;
		diag->dkd_errsect = dsi->un_err_blkno;
		diag->dkd_errno = dsi->un_status;
		/* diag->dkd_errno   = dsi->un_err_code; */
#ifdef		SF_FORMAT
		diag->dkd_severe = dsi->un_err_severe;
#endif		SF_FORMAT

		dsi->un_last_cmd   = 0;	/* Reset */
		dsi->un_err_blkno  = 0;
		dsi->un_err_code   = 0;
		dsi->un_err_severe = 0;
		return (0);

	/*
	 * Run a generic command.
	 */
	case FDKIOCSCMD:
	case DKIOCSCMD:
#ifdef  SF_FORMAT
/*  		DPRINTF("sfioctl:  run special command\n"); */
		return (sfioctl_cmd(dev, un, dsi, data));
#endif  SF_FORMAT

	case FDKIOGCHAR:
		DPRINTF("sfioctl: get characteristics\n");
		fchar = (struct fdk_char *)data;
/* request page 5 stuff */
		ms_med_parms = (struct ms_med_parms *)rmalloc(iopbmap,
				(long)(sizeof (struct ms_med_parms) + 4));
		oms_med_parms = ms_med_parms;
		while ((u_int)ms_med_parms & 0x03)
			((u_int)ms_med_parms)++;
		bzero((char *)ms_med_parms, sizeof (struct ms_med_parms));
		s = splr(pritospl(un->un_mc->mc_intpri));
		if (sfcmd(dev, SC_MODE_SENSE, 0x5, 
			     sizeof (struct ms_med_parms), 
			     (caddr_t)ms_med_parms, 0)) {
			EPRINTF("Can not get characteristics\n");
			(void) splx(s);
			return (ENXIO);
		}
		(void) splx(s);

		fchar->medium = ms_med_parms->header.medium;
		fchar->transfer_rate = ms_med_parms->page5.xfer_rate;
		fchar->ncyl = ms_med_parms->page5.ncyl;
		fchar->nhead = ms_med_parms->page5.nhead;
		fchar->sec_size = ms_med_parms->page5.data_bytes;
		fchar->secptrack = ms_med_parms->page5.sec_trk ;
		rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
			  (u_long)oms_med_parms);
		return (0);

	case FDKIOSCHAR:
		DPRINTF("sfioctl: set characteristics\n");
		fchar = (struct fdk_char *)data;
		ms_med_parms = (struct ms_med_parms *)rmalloc(iopbmap,
		(long)(sizeof (struct ms_med_parms) + 4));
		oms_med_parms = ms_med_parms;
		while ((u_int)ms_med_parms & 0x03)
			((u_int)ms_med_parms)++;
		bzero((char *)ms_med_parms, sizeof (struct ms_med_parms));
		s = splr(pritospl(un->un_mc->mc_intpri));
		if (sfcmd(dev, SC_MODE_SENSE, 0x5, 
			     sizeof (struct ms_med_parms), 
			     (caddr_t)ms_med_parms, 0)) {
			EPRINTF("Can not get characteristics\n");
			rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
				  (u_long)oms_med_parms);
		
			(void) splx(s);
			return(ENXIO);
		}
				/* stuff set by user */
		ms_med_parms->header.medium = fchar->medium;
		ms_med_parms->page5.xfer_rate = fchar->transfer_rate;
		ms_med_parms->page5.nhead = fchar->nhead;
		ms_med_parms->page5.sec_trk = fchar->secptrack;
		ms_med_parms->page5.data_bytes = fchar->sec_size;
		ms_med_parms->page5.ncyl = fchar->ncyl;
		if (sfcmd(dev, SC_MODE_SELECT, 0x5, 
			     sizeof (struct ms_med_parms), 
			     (caddr_t)ms_med_parms, 0)) {
			EPRINTF("Can not set characteristics\n");
			rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
			  (u_long)oms_med_parms);
			(void) splx(s);
			return (ENXIO);
		}
		(void) splx(s);
		rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
			  (u_long)oms_med_parms);
		return (0);

	/*
	 * Handle unknown ioctls here.
	 */
	default:
		EPRINTF("sfioctl:  unknown ioctl\n");
		return (ENOTTY);
	}
}


/*
 * Run a command for sfioctl.
 */
#ifdef  SF_FORMAT
/*ARGSUSED*/
static
sfioctl_cmd(dev, un, dsi, data)
	dev_t dev;
	register struct scsi_unit *un;
	struct scsi_floppy *dsi;
	caddr_t data;
{
	register struct dk_cmd *com;
	register struct fdk_state *flstate;
	int err, options;
	int s;

/* 	DPRINTF("sfioctl_cmd:\n"); */

	com = (struct dk_cmd *)&((struct fdk_cmd *)data)->dcmd;
	flstate = (struct fdk_state *)&((struct fdk_cmd *)data)->fstate;
#ifdef  SFDEBUG
#ifdef notdef
	if (sf_debug > 1) {
		printf("sfioctl_cmd:  cmd= %x  blk= %x  cnt= %x  ",
				com->dkc_cmd, com->dkc_blkno, com->dkc_secnt);
		printf("buf addr= %x  buflen= 0x%x\n",
				com->dkc_bufaddr, com->dkc_buflen);
	}
#endif
#endif  SFDEBUG
	/*
	 * Set options.
	 */
	options = 0;
	if (com->dkc_flags & DK_SILENT) {
		options |= SD_SILENT;
#ifdef		SFDEBUG
		if (sf_debug > 0)
			options &= ~SD_SILENT;
#endif		SFDEBUG
	}
	if (com->dkc_flags & DK_ISOLATE)
		options |= SD_NORETRY;

	/*
	 * Process the special ioctl command.
	 */
	switch (com->dkc_cmd) {
	case FKREAD:
/*  		DPRINTF("sfioctl_cmd:  read\n"); */
		s = splr(pritospl(un->un_mc->mc_intpri));
		err = sf_check_char(flstate, un, dev);
 		/* make sure we have not changed */
		options |= SD_DVMA_RD;
		err = sfcmd(dev, SC_SPECIAL_READ, (u_int)com->dkc_blkno,
			    (u_int)com->dkc_buflen,
			    (caddr_t)com->dkc_bufaddr, options);
		(void) splx(s);
		break;

	case FKWRITE:
/* 		DPRINTF("sfioctl_cmd:  write\n"); */
		s = splr(pritospl(un->un_mc->mc_intpri));
		err = sf_check_char(flstate, un, dev);
 		/* make sure we have not changed */
		options |= SD_DVMA_WR;
		err = sfcmd(dev, SC_SPECIAL_WRITE, (u_int)com->dkc_blkno,
			    (u_int)com->dkc_buflen,
			    (caddr_t)com->dkc_bufaddr, options);
		(void) splx(s);
		break;

	case FKSEEK:
		DPRINTF("sfioctl_cmd:  seek\n");
		s = splr(pritospl(un->un_mc->mc_intpri));
		err = sfcmd(dev, SC_SEEK, (u_int)com->dkc_blkno,
			    (u_int)com->dkc_buflen,
			    (caddr_t)com->dkc_bufaddr, options);
		(void) splx(s);
		break;

	case FKREZERO:
		DPRINTF("sfioctl_cmd:  rezero\n");
		s = splr(pritospl(un->un_mc->mc_intpri));
		err = sfcmd(dev, SC_REZERO_UNIT, (u_int)com->dkc_blkno,
			    (u_int)com->dkc_buflen,
			    (caddr_t)com->dkc_bufaddr, options);
		(void) splx(s);
		break;

	case SC_MODE_SELECT:
		EPRINTF("sfioctl_cmd:  mode select\n");
		options |= SD_DVMA_WR;
		s = splr(pritospl(un->un_mc->mc_intpri));
		err = sfcmd(dev, SC_MODE_SELECT, (u_int)com->dkc_blkno,
			    (u_int)com->dkc_buflen, (caddr_t)com->dkc_bufaddr,
			    options);
		(void) splx(s);
#ifdef		SFDEBUG
		if (sf_debug > 2) {
			sf_print_buffer((u_char *)com->dkc_bufaddr,
					(int)com->dkc_buflen);
			printf("\n");
		}
#endif		SFDEBUG
		break;

	case SC_MODE_SENSE:
		EPRINTF("sfioctl_cmd:  mode sense\n");
		options |= SD_DVMA_RD;
		s = splr(pritospl(un->un_mc->mc_intpri));
		err = sfcmd(dev, SC_MODE_SENSE, (u_int)com->dkc_blkno,
			    (u_int)com->dkc_buflen, (caddr_t)com->dkc_bufaddr,
			    options);
		(void) splx(s);
#ifdef		SFDEBUG
		if (sf_debug > 2) {
			sf_print_buffer((u_char *)com->dkc_bufaddr,
					(int)com->dkc_buflen);
			printf("\n");
		}
#endif		SFDEBUG
		break;

        case FKFORMAT_TRACK:
 		EPRINTF("sfioctl_cmd:  format track\n");
		s = splr(pritospl(un->un_mc->mc_intpri));
		err = sf_check_char(flstate, un, dev);
		err = sfcmd(dev, SC_REZERO_UNIT, (u_int)com->dkc_blkno,
			    (u_int)com->dkc_buflen,
			    (caddr_t)com->dkc_bufaddr, options);
		err = sfcmd(dev, SC_FORMAT_TRACK, (u_int)com->dkc_blkno,
			    (u_int)com->dkc_buflen, (caddr_t)com->dkc_bufaddr,
			    options);
		(void) splx(s);
		break;

	case FKFORMAT_UNIT:
/* 		EPRINTF("sfioctl_cmd:  format\n"); */
		s = splr(pritospl(un->un_mc->mc_intpri));
		err = sf_check_char(flstate, un, dev);
		err = sfcmd(dev, SC_FORMAT, (u_int)com->dkc_blkno,
			    (u_int)com->dkc_buflen, (caddr_t)com->dkc_bufaddr,
			    options);
		(void) splx(s);
		break;

	default:
		EPRINTF1("sfioctl_cmd:  unknown command %x\n", com->dkc_cmd);
		return (EINVAL);
		/* break; */
	}
	if (err) {
/* 		EPRINTF("sfioctl_cmd:  ioctl cmd failed\n"); */
		return (EIO);
	}
	return (0);
}

/* see if any of the characteristics of the floppy have changed */
sf_check_char(fchar, un, dev)
struct fdk_state *fchar;
register struct scsi_unit *un;
dev_t dev;
{       
	register struct scsi_floppy *sfp;
	struct ms_med_parms {
		struct ccs_modesel_head_fl header;
		struct ccs_modesel_medium page5;
	} *ms_med_parms, *oms_med_parms;
	int change = 0;
	
	sfp = &sfdisk[un->un_unit];
	if (fchar->fkc_bsec || fchar->fkc_strack || 
	       fchar->fkc_step || fchar->fkc_rate) {
		ms_med_parms = (struct ms_med_parms *)rmalloc(iopbmap,
		(long)(sizeof (struct ms_med_parms) + 4));
		oms_med_parms = ms_med_parms;
		while ((u_int)ms_med_parms & 0x03)
			((u_int)ms_med_parms)++;
		bzero((char *)ms_med_parms, sizeof (struct ms_med_parms));
				/* stuff set by user */
		if (fchar->fkc_rate != sfp->sf_xrate) {
			sfp->sf_xrate = fchar->fkc_rate;
			change = 1;
		}
		if (fchar->fkc_strack != sfp->sf_spt) {
			sfp->sf_spt = fchar->fkc_strack;
			change = 1;
		}
		if (fchar->fkc_bsec != sfp->sf_bsec) {
			sfp->sf_bsec = fchar->fkc_bsec;
			change = 1;
		}
		if (fchar->fkc_step != sfp->sf_step) {
			sfp->sf_step = fchar->fkc_step;
			change = 1;
		}
		if (change) {
			if (sfcmd(dev, SC_MODE_SENSE, 0x5, 
			     sizeof (struct ms_med_parms), 
			     (caddr_t)ms_med_parms, 0)) {
			EPRINTF("Can not get characteristics\n");
			rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
				  (u_long)oms_med_parms);
			return(ENXIO);
		}
		ms_med_parms->page5.xfer_rate = sfp->sf_xrate;
		ms_med_parms->page5.sec_trk = sfp->sf_spt;
		ms_med_parms->page5.data_bytes = sfp->sf_bsec;
		ms_med_parms->page5.step = sfp->sf_step;
#ifdef notdef
		if (sfcmd(dev, SC_MODE_SELECT, 0x5, 
			     sizeof (struct ms_med_parms), 
			     (caddr_t)ms_med_parms, 0)) {
			EPRINTF("Can not set characteristics\n");
			rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
			  (u_long)oms_med_parms);
			return (ENXIO);
		}
#endif
		}
		rmfree(iopbmap, (long)(sizeof (struct ms_med_parms) + 4), 
			  (u_long)oms_med_parms);
		return (0);
	}
	return (0);
}
#endif  SF_FORMAT


static char *sf_ncrflop_error_str[] = {
	"",				/* 0x00 */
	"no index",			/* 0x01 */
	"seek incomplete",		/* 0x02 */
	"write fault",			/* 0x03 */
	"drive not ready",		/* 0x04 */
	"drive not selected",		/* 0x05 */
	"no track 0",			/* 0x06 */
	"multiple drives selected",	/* 0x07 */
	"lun failure",			/* 0x08 */
	"servo error",			/* 0x09 */
	"",				/* 0x0a */
	"",				/* 0x0b */
	"",				/* 0x0c */
	"",				/* 0x0d */
	"",				/* 0x0e */
	"",				/* 0x0f */
	"ID error",			/* 0x10 */
	"hard data error",		/* 0x11 */
	"no addr mark",			/* 0x12 */
	"no data field addr mark",	/* 0x13 */
	"block not found",		/* 0x14 */
	"seek error",			/* 0x15 */
	"dma timeout",			/* 0x16 */
	"recoverable error",		/* 0x17 */
	"soft data error",		/* 0x18 */
	"bad block",			/* 0x19 */
	"parameter overrun",		/* 0x1a */
	"synchronous xfer error",	/* 0x1b */
	"no primary defect list",	/* 0x1c */
	"compare error",		/* 0x1d */
	"recoverable error",		/* 0x1e */
	"",				/* 0x1f */
	"invalid command",		/* 0x20 */
	"invalid block",		/* 0x21 */
	"illegal command",		/* 0x22 */
	"",				/* 0x23 */
	"invalid cdb",			/* 0x24 */
	"invalid lun",			/* 0x25 */
	"invalid param list",		/* 0x26 */
	"write protected",		/* 0x27 */
	"media changed",		/* 0x28 */
	"reset",			/* 0x29 */
	"mode select changed",		/* 0x2a */
	0
};
#define MAX_NCRFLOP_ERROR_STR \
	(sizeof(sf_ncrflop_error_str)/sizeof(sf_ncrflop_error_str[0]))


static char *sf_key_error_str[] = {
	"no sense",			/* 0x00 */
	"soft error",			/* 0x01 */
	"not ready",			/* 0x02 */
	"medium error",			/* 0x03 */
	"hardware error",		/* 0x04 */
	"illegal request",		/* 0x05 */
	"unit attention",		/* 0x06 */
	"write protected",		/* 0x07 */
	"blank check",			/* 0x08 */
	"vendor unique",		/* 0x09 */
	"copy aborted",			/* 0x0a */
	"aborted command",		/* 0x0b */
	"equal error",			/* 0x0c */
	"volume overflow",		/* 0x0d */
	"miscompare error",		/* 0x0e */
	"reserved",			/* 0x0f */
	0
};
#define MAX_KEY_ERROR_STR \
	(sizeof(sf_key_error_str)/sizeof(sf_key_error_str[0]))


static char *sf_cmds[] = {
	"test unit ready",		/* 0x00 */
	"rezero",			/* 0x01 */
	"<bad cmd>",			/* 0x02 */
	"request sense",		/* 0x03 */
	"format",			/* 0x04 */
	"<bad cmd>",			/* 0x05 */
	"<bad cmd>",			/* 0x06 */
	"reassign",			/* 0x07 */
	"read",				/* 0x08 */
	"<bad cmd>",			/* 0x09 */
	"write",			/* 0x0a */
	"seek",				/* 0x0b */
	"<bad cmd>",			/* 0x0c */
	"<bad cmd>",			/* 0x0d */
	"<bad cmd>",			/* 0x0e */
	"<bad cmd>",			/* 0x0f */
	"<bad cmd>",			/* 0x10 */
	"<bad cmd>",			/* 0x11 */
	"inquiry",			/* 0x12 */
	"<bad cmd>",			/* 0x13 */
	"<bad cmd>",			/* 0x14 */
	"mode select",			/* 0x15 */
	"reserve",			/* 0x16 */
	"release",			/* 0x17 */
	"copy",				/* 0x18 */
	"<bad cmd>",			/* 0x19 */
	"mode sense",			/* 0x1a */
	"start/stop",			/* 0x1b */
	"<bad cmd>",			/* 0x1c */
};
#define MAX_SF_CMDS \
	(sizeof(sf_cmds)/sizeof(sf_cmds[0]))


/*
 * Return the text string associated with the sense key value.
 */
static char *
sf_print_key(key_code)
	register u_char key_code;
{
	static char *unknown_key = "unknown key";
	if ((key_code > MAX_KEY_ERROR_STR -1)  ||
	     sf_key_error_str[key_code] == NULL) {

		return (unknown_key);
	}
	return (sf_key_error_str[key_code]);
}


/*
 * Return the text string associated with the secondary
 * error code, if availiable.
 */
static char *
sf_print_error(dsi, error_code)
	register struct scsi_floppy *dsi;
	register u_char error_code;
{
	static char *unknown_error = "unknown error";
	switch (dsi->un_ctype) {
	case CTYPE_NCRFLOP:
	case CTYPE_CCS:
		if ((MAX_NCRFLOP_ERROR_STR > error_code)  &&
		    sf_ncrflop_error_str[error_code] != NULL)
			return (sf_ncrflop_error_str[error_code]);
		break;

	}
	return (unknown_error);
}


/*
 * Print the sense key and secondary error codes
 * and dump out the sense bytes.
 */
#ifdef	SFDEBUG
static
sf_error_message(un, dsi)
	register struct scsi_unit *un;
	register struct scsi_floppy *dsi;
{
	register struct scsi_ext_sense *ssf;
	register u_char   *cp;
	register int i;
	register u_char error_code;

	/* If error messages are being suppressed, exit. */
	if (dsi->un_options & SD_SILENT)
		return;

	ssf = (struct scsi_ext_sense *)cp = (
		struct scsi_ext_sense *)dsi->un_sense;
	error_code = ssf->error_code;
	printf("sf%d error:  sense key(0x%x): %s",
		un - sfunits, ssf->key, sf_print_key(ssf->key));
	if (error_code != 0) {
		printf(",  error code(0x%x): %s",
		       error_code, sf_print_error(dsi, error_code));
	}

	printf("\n           sense = ");
	for (i=0; i < sizeof (struct scsi_ext_sense); i++)
		printf("%x  ", *cp++);
	printf("\n");
}


static
sf_print_cmd(un)
	register struct scsi_unit *un;
{
	register int x;
	register u_char *y = (u_char *)&(un->un_cdb);

	printf("sf%d:  failed cmd =  ", SFUNIT(un->un_unit));
	for (x = 0; x < CDB_GROUP0; x++)
		printf("%x  ", *y++);
	printf("\n");
}
#endif	SFDEBUG


static
sferrmsg(un, bp, action)
	struct scsi_unit *un;
	struct buf *bp;
	char *action;
{
	register struct scsi_ext_sense *ssf;
	register struct scsi_floppy *dsi;
	char *cmdname;
	u_char error_code;
	int blkno_flag = 1;		/* if false, blkno is meaningless */

	dsi = &sfdisk[dkunit(bp)];
	ssf = (struct scsi_ext_sense *)dsi->un_sense;

	/* If error messages are being suppressed, exit. */
	if ((dsi->un_options & SD_SILENT) || (sf_debug == 0))
		return;

	/* Decode command name */
	if (un->un_cmd < MAX_SF_CMDS) {
		cmdname = sf_cmds[un->un_cmd];
	} else {
		cmdname = "unknown cmd";
	}

	/* If not a check condition error, block number is invalid */
	if (! un->un_scb.chk)
		blkno_flag = 0;


	printf("sf%d%c:  %s %s",
		SFNUM(un), LPART(bp->b_dev) + 'a', cmdname, action);
	if (blkno_flag)
		printf(",  block %d", dsi->un_err_blkno);

	error_code = ssf->error_code;
	printf("\n       sense key(0x%x): %s",
	       ssf->key, sf_print_key(ssf->key));
	if (error_code != 0) {
		printf(",  error code(0x%x): %s\n",
		       error_code, sf_print_error(dsi, error_code));
	} else {
		printf("\n");
	}

}
#endif	NSF > 0
