*BSD News Article 7277


Return to BSD News archive

Newsgroups: comp.unix.bsd
Path: sserve!manuel.anu.edu.au!munnari.oz.au!uunet!timbuk.cray.com!hemlock.cray.com!overby
From: overby@cray.com (Glen Overby)
Subject: [Source] Seagate ST01/ST02 SCSI driver
Message-ID: <1992Nov1.154143.1034@hemlock.cray.com>
Summary: source for sys.i386bsd/i386/isa/st01-[1s].[cs]
Lines: 1880
Nntp-Posting-Host: cypress32
Organization: Yuppie suburbs
Date: 1 Nov 92 15:41:43 CST

/*
   This is a re-release of my Seagate (and maybe Future Domain) driver for 0.1
   The README describes it all.

   I've deposited a copy on agate in incoming/ST01.shar
 */

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# If this archive is complete, you will see the following message at the end:
#		"End of shell archive."
#
# Contents:
#   README st01-1.c st01-s.s conf.c.diff
#
# Wrapped by overby@cray.com on Sun Nov  1 15:36:44 1992
#
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(3794 characters\)
sed "s/^X//" >README <<'END_OF_README'
XHistory:
X5/28/92		First release of my ST-01 driver: works on 386BSD 0.0
X10/31/92	Second release for 0.1 and assmebly-level copy
X
XHardware:
X
XThis driver is written to support a Seagate ST-01/ST-02 controler.  This
Xis supposedly a re-labeled Future Domain (unsure what model number).  If
Xyou are successful using it with a Future Domain (or if you're not), 
Xplease tell me.
X
XThe Seagate ST-01 is a simiple non-DMA SCSI host adapter which resides
Xentirely in memory (no I/O space is taken).  
X
XIt uses interrupts (IRQ3 or IRQ5) to notify the CPU of a reconnect from target,
Xand will insert wait-states durring data in/out phases to synchronize the host
XCPU with the SCSI device.
X
XYou may configure the board to reside at any of it's possible addresses and
Xinterrupt numbers (just be sure they match your configuration file).  If you
Xplan to use the assembly-level copy routines, you MUST enable the 0WS 
X(wait-state) jumper.
X
XIf you do NOT have an ISA disk drive, you should disable the IDA disk register,
Xotherwise the wd driver will think that there is a WD controler available.
X
X				Jumpers
X
XST-01B 			ST-02
XJumper/Connector				Description
X
X	W1		JP1			BIOS address selection
X	W2		JP2			0WS Enable
X	W3		JP3			IRQ Enable
X			JP6			Register Enable/Disable
X	W5		J7			Channel Activity LED connector
X	J2		J2			50-pin SCSI connector
X	J3		J6			Drive power connector
X
XSource: Seagate ST01/ST02 Product Manual, Rev. G  Pub No. 36027-002
X
XThe Driver
X
XThe code to talk to the ST-01 controler itself is derived from a
Xdriver for Microport System V/386 Copyright 1988 by by Tatu Yl|nen
Xwith BSD-specific driver code liberally stolen from sys/vax/uda/uba.c
Xand sys/i386/isa/wd.c.
X
XINSTALLATION
X
XCopy st01-1.c to src/sys/i386/isa
XCopy st01-s.s to src/sys/i386/isa
X
Xadd the following to your configuration file:
X
Xcontroler	sg0  at isa? bio irq 5 iomem 0xC8000 iosiz 0x2000 vector sgintr
Xdisk		dk8  at sg0 drive 0
X
XThis assumes your controler is at the first ST01 address and uses IRQ5.
X
XNOTE: Interrupts MUST be enabled on the card!  The factory setting
Xdoes not enable interrupts.
X
XIf you do NOT enable interrupts, you must go into the code and disable the
Xdriver's use of SCSI disconnects (which certainly can't hurt performance
Xif you have one device).
X
Xadd to src/sys/i386/conf/files.i386:
X
Xi386/isa/st01-1.c		optional sg device-driver
Xi386/isa/st01-s.s		optional sg device-driver
X
Xadd the driver to src/sys/i386/i386/conf.c (conf.c.diff)
X
XCheck the driver source (st01-1.c) for the proper definition of "ASM":
Xdefined if you want to use the assembly-level data copy, undefined otherwise.
XIf you have problems, undefine it.
X
Xconfigure your system with 'config'.
X
XManually edit "swap386bsd.c" to add a swap space entry:
X
Xstruct	swdevt swdevt[] = {
X	{ makedev(5, 1),	0,	0 },	/* sg0b */
X	{ makedev(0, 1),	0,	0 },	/* wd0b */
X	{ 0, 0, 0 }
X};
X
Xthis is requred because 'config' doesn't recognise 'sg' on the 'swap' 
Xparameter.
X
Xfinally, compile your new system!
X
XLIMITATIONS
X
XI have only tested the code with one SCSI target, a Seagate ST-296N
X80MB hard drive, allthough there is nothing in the code intended to
Xprevent it from working with a full bus.  This probably won't work for 
Xcartridge tape drives.
X
XPROBLEMS
X
XThe config code for 386BSD does not do anything with 'disk'
Xdefinitions.  This should be changed to be like what a VAX does and
Xdefine a structure for each disk.  Then the device driver will get a
Xisa_device structure for each configured SCSI target.
X
XFUTURE PLANS
X
XI think the code that handles the low-level SCSI bus handling is
Xinefficient and rather grody (too many gotos for my liking).  I have
Xbegun sketching out a completely new low-level interface.  I'll make it
Xpart of Julian's generic SCSI system.
X
XGlen Overby
Xoverby@plains.nodak.edu / overby@{cobber,nic}.cord.edu / overby@cray.com
X
END_OF_README
if test 3794 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f st01-1.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"st01-1.c\"
else
echo shar: Extracting \"st01-1.c\" \(41764 characters\)
sed "s/^X//" >st01-1.c <<'END_OF_st01-1.c'
X/*-
X * Seagate ST-01/02 driver.
X * $Id: st01.c,v 1.4 92/05/19 22:19:30 overby Exp Locker: overby $
X *
X * Derived from:
X *	sys/i386/isa/wd.c @(#)wd.c	7.2 (Berkeley) 5/9/91
X *	sys/vax/uba/uda.c @(#)uda.c	7.32 (Berkeley) 2/13/91
X *					(Chris Torek's driver)
X *	sys/vax/vax/mscpvar.h  @(#)mscpvar.h	7.3 (Berkeley) 6/28/90
X *	Header: scsi.c,v 1.3 90/01/23 11:33:00 brian Locked 
X */
X
X#include "sg.h"
X#if NSG > 0
X
X#define DEBUG3
X#define ASM
X
X#include "param.h"
X#include "dkbad.h"
X#include "systm.h"
X#include "conf.h"
X#include "file.h"
X#include "stat.h"
X#include "ioctl.h"
X#include "disklabel.h"
X#include "buf.h"
X#include "uio.h"
X#include "i386/isa/isa_device.h"
X#include "i386/isa/icu.h"
X#include "syslog.h"
X#include "vm/vm.h"
X
X/*	sys/vax/vax/mscpvar.h  @(#)mscpvar.h	7.3 (Berkeley) 6/28/90 */
X/*
X * The following macro appends a buffer to a drive queue or a drive to
X * a controller queue, given the name of the forward link.  Use as
X * `APPEND(dp, &um->um_tab, b_forw)' or `APPEND(bp, dp, av_forw)',
X * where `bp' is a transfer request, `dp' is a drive queue, and `um_tab'
X * is a controller queue.  (That is, the forward link for controller
X * queues is `b_forw'; for drive queues, it is `av_forw'.)
X */
X#define	APPEND(bp, queue, link) { \
X	(bp)->link = NULL; \
X	if ((queue)->b_actf == NULL) \
X		(queue)->b_actf = (bp); \
X	else \
X		(queue)->b_actl->link = (bp); \
X	(queue)->b_actl = (bp); \
X}
X
X/* wd.c */
X#define	MAXTRANSFER	32	/* max size of transfer in page clusters */
X
X
Xint	sgprobe(), sgattach(), sgintr();
Xstruct	isa_driver sgdriver = {
X	sgprobe, sgattach, "sg",
X};
X
X/*
X * Target info, per Target
X */
Xstruct target_info {
X	int 	ti_rw;
X	int	ti_flags;
X	int	ti_state;	/* open/closed state */
X
X	daddr_t	ti_dsize;	/* size in sectors */
X	daddr_t	ti_ssize;	/* sector size */
X	int	ti_type;	/* drive type */
X	u_long	ti_mediaid;	/* media id */
X	struct	ti_geom {	/* geometry information */
X#ifdef notyet
X		u_short	rg_nsectors;	/* sectors/track */
X		u_short	rg_ngroups;	/* track groups */
X		u_short	rg_ngpc;	/* groups/cylinder */
X		u_short	rg_ntracks;	/* ngroups*ngpc */
X		u_short	rg_ncyl;	/* ti_dsize/ntracks/nsectors */
X		u_short	rg_rctsize;	/* size of rct */
X		u_short	rg_rbns;	/* replacement blocks per track */
X		u_short	rg_nrct;	/* number of rct copies */
X#endif
X	} ti_geom;
X	int	ti_wlabel;	/* label sector is currently writable */
X
X	u_long	ti_openpart;	/* partitions open */
X	u_long	ti_bopenpart;	/* block partitions open */
X	u_long	ti_copenpart;	/* character partitions open */
X
X	struct	disklabel ti_label;
X	struct	dos_partition ti_part[NDOSPART];
X	struct	dkbad	ti_bad;	/* bad sector table */
X
X} t_info[NSG * 8];
X
X/*
X * Software state, per target
X */
X#define	CLOSED		0
X#define	WANTOPEN	1
X#define	RDLABEL		2
X#define	OPEN		3
X#define	OPENRAW		4
X
X/*
X * flags...
X */
X#define	UNIT_ONLINE	0x01000
X
X#define	SGFL_DOSPART	0x00001	 /* has DOS partition table */
X#define	SGFL_QUIET	0x00002	 /* report errors back, but don't complain */
X#define	SGFL_SINGLE	0x00004	 /* sector at a time mode */
X#define	SGFL_ERROR	0x00008	 /* processing a disk error */
X#define	SGFL_BSDLABEL	0x00010	 /* has a BSD disk label */
X#define	SGFL_BADSECT	0x00020	 /* has a bad144 badsector table */
X#define	SGFL_WRITEPROT	0x00040	 /* manual unit write protect */
X
X/*
X * Active SCSI commands (one for each target)
X */
Xtypedef struct target_status {
X	char *command, *data, *status;	/* send/recieve buffers */
X	int length;
X	struct {			/* saved send/recieve buffers */
X		char *command, *data, *status;
X		int length;
X	} save;
X	char command_buf[12], status_buf[2]; /* the buffers themselves */
X	volatile char *cmdport, *dataport;	/* HACK! controller addresses */
X	int 	connected,		/* TRUE if we're connected right now */
X		busy,			/* TRUE if a command is in progress */
X		retries,		/* how bad are things? */
X		nomsgs,			/* don't say we can do disconnects */
X		xfertimeout,	xferstatus,	xfertime,	xferslow,
X		blocksize,
X		target_id;		/* SCSI target ID we're talking with */
X} ACTIVE;
X
XACTIVE active[NSG * 8];		/* one for each target on each controler */
X
Xstruct	isa_device *sgcinfo[NSG];	/* Seagate Controler info */
Xstruct	isa_device *sgtinfo[NSG * 8];	/* SCSI Target info */
X
Xstruct buf sgqueue[NSG * 8];		/* per-drive transfer queue */
X
X/* wd.c
X * Device to unit number and partition and back
X *	bits	use
X *	0-3	partition
X *	4-5	unit
X *	6	dos partitions
X *	7	ignore partition table
X */
X#define sgnoreloc(dev)	(minor(dev) & 0x80)	/* ignore partition table */
X#define sgdospart(dev)	(minor(dev) & 0x40)	/* use dos partitions */
X#define scsiunit(dev)	((minor(dev) & 0x38) >> 3)
X#define scsipart(dev)	(minor(dev) & 0x7)
X#define makesgdev(maj, unit, part)	(makedev(maj,((unit<<3)+part)))
X#define SGRAW	3		/* 'd' partition isn't a partition! */
X
X/*
X#define	TARGETSHIFT	3
X#define	TARGETMASK	7
X#define	PARTMASK	7
X
X#define	scsiunit(dev)	((minor(dev) >> TARGETSHIFT) & TARGETMASK)
X#define	scsipart(dev)	(minor(dev) & PARTMASK)
X#define	scsiminor(u, p)	(((u) << TARGETSHIFT) | (p))
X*/
X
X#define SCSIRAMOFS	0x00001800
X#define SCSICMDOFS	0x00001a00l
X#define SCSIDATOFS	0x00001c00l
X
X
X/* Internal command returns [st01.c] */
X#define COK             0 /* command completed successfully */
X#define CNOCONNECT      1 /* no connection could be made to the drive */
X#define CBUSBUSY        2 /* the bus is busy and cannot be cleared */
X#define CTIMEOUT        3 /* timeout waiting for the drive */
X#define CERROR          4 /* an error was returned by the target */
X#define CBUSY           5 /* the drive is busy - wait and retry later */
X#define CDISCONNECT     6 /* target disconnected; this is not an error */
X
X/* ST-01 Status Register */
X#define STARBCOMPL      0x80        /* arbitration complete bit */
X#define STPARERR        0x40        /* parity error bit */
X#define STSEL           0x20        /* scsi select */
X#define STREQ           0x10        /* scsi req */
X#define STCD            0x08        /* scsi c/d */
X#define STIO            0x04        /* scsi i/o */
X#define STMSG           0x02        /* scsi msg */
X#define STBSY           0x01        /* scsi busy */
X
Xstatic int scsi_arbitrate(), scsi_machine();
X
Xint int_happened =0;
Xint resel_fail =0;
Xint resel_loop =0;
X
X/*
X * Probe routine
X */
Xsgprobe(dvp)
X	struct isa_device *dvp;
X{	char *st01c;
X	st01c = dvp->id_maddr;		/* only uses memory, no I/O */
X#ifdef DEBUG1
X	printf("st01 id_maddr=0x%lx\n", st01c);
X#endif
X#ifdef lint
X	st01intr(0);
X#endif
X
X	*(st01c+SCSIRAMOFS) = 0xa5;	/* write to RAM */
X	*(st01c+SCSIRAMOFS+1) = 0x5a;	/* write to RAM */
X
X	/*printf("st01 rom  0x%x 0x%x\n", *st01c, *(st01c+1));*/
X#ifdef DEBUG1
X	printf("st01 ram  0x%x (@0x%lx)\n",
X		*(st01c+SCSIRAMOFS), (st01c+SCSIRAMOFS));
X#endif
X	/*if( *st01c != (char) 0xa5 && *st01c == (char) 0x55 && *(st01c+SCSIRAMOFS) == (char) 0xa5)*/
X	if( *(st01c+SCSIRAMOFS) == (char) 0xa5 && *(st01c+SCSIRAMOFS+1) == (char) 0x5a ) {
X#ifdef DEBUG1
X		printf("st01 OK!\n");
X#endif
X		return(1);
X	}
X	return (0);
X}
X
X/*
X * attach each target if possible.
X */
Xsgattach(dvp)
X	struct isa_device *dvp;
X{
X	int target; /* = dvp->id_unit; */
X
X#ifdef DEBUG1
X	printf("sgattach: %d\n", dvp->id_unit);
X#endif
X
X	sgcinfo[dvp->id_unit] = dvp;
X
X	for(target = 0; target <1; target++) {
X		actsetup(dvp, &active[target], target);
X		sgslave(&active[target]);
X	}
X}
X
X/* set up stuff in *active for this controler/target */
Xactsetup(dvp, active, target)
X	struct isa_device *dvp;
X	ACTIVE *active;
X	int target;
X{
X	active->cmdport  = dvp->id_maddr + SCSICMDOFS;
X	active->dataport = dvp->id_maddr + SCSIDATOFS;
X	active->target_id = 1<<target;
X	active->connected = active->busy = active->retries = 0;
X#ifdef DEBUG1
X	printf("C = 0x%x D = 0x%x T = 0x%x \n",active->cmdport, 
X		active->dataport, active->target_id);
X#endif
X}
X
X/* Look for a slave device; print out everything about it. */
Xsgslave(t)
X	ACTIVE *t;
X{	int a;
X	char *sense_err();
X
X/* initdrive(unit) */
X	if( (a = testready( t )) != COK) {
X		if( a == CERROR || a == CBUSY ) { /* Bad News */
X			t->target_id = 0;	/* mark it vaporware */
X			return(a);
X		}
X	}
X	if(t->status_buf[0] & 0x2) {
X		sense_err(t);
X	}
X}
X
X/*
X * Open a drive.
X */
X/*ARGSUSED*/
Xsgopen(dev, flag, fmt)
X	dev_t dev;
X	int flag, fmt;
X{
X	register int unit;
X	register struct disklabel *lp;
X	register struct partition *pp;
X	register struct target_info *ti;
X	int s, i, part, mask, error = 0;
X	daddr_t start, end;
X
X	printf("sgopen: dev=0x%x flag=0x%x fmt=0x%x\n", dev, flag, fmt);
X	/*
X	 * Make sure this is a reasonable open request.
X	 */
X	unit = scsiunit(dev);
X	if (unit >= NSG /*|| (ti = &t_info[unit])->ti_flags) /* & AVAILABLE == 0)*/)
X	{	printf("sgopen: Unit %d higher than max (%d)\n", unit, NSG);
X		return (ENXIO);
X	}
X
X	ti = &t_info[unit];
X	s = splhigh();
X	/*
X	 * Wait for the state to settle
X	 */
X	while(ti->ti_state != OPEN && ti->ti_state != OPENRAW &&
X	     ti->ti_state != CLOSED)
X		if (error = tsleep((caddr_t)ti->ti_state, (PZERO + 1) | PCATCH,
X		    devopn, 0)) {
X			splx(s);
X			return(error);
X		}
X	/*
X	 * If not on line, or we are not sure of the label, reinitialise
X	 * the drive.
X	 */
X	if (ti->ti_flags & UNIT_ONLINE == 0 ||
X	    (ti->ti_state != OPEN && ti->ti_state != OPENRAW)) /* OPEN? */
X		error = targ_init(ti, dev, flag);
X
X	splx(s);
X	if (error) {
X		printf("sgopen: targ_init error\n");
X		return (error);
X	}
X
X	ti->ti_flags |= UNIT_ONLINE;
X	part = scsipart(dev);
X	lp = &ti->ti_label;
X	if (part >= lp->d_npartitions) {
X		printf("sgopen: part %d > max %d\n", part, lp->d_npartitions);
X		return (ENXIO);
X	}
X
X	/*
X	 * Warn if a partition is opened that overlaps another
X	 * already open, unless either is the `raw' partition
X	 * (whole disk).
X	 */
X#define	RAWPART		2	/* 'c' partition */	/* XXX */
X	mask = 1 << part;
X	if ((ti->ti_openpart & mask) == 0 && part != RAWPART) {
X		pp = &lp->d_partitions[part];
X		start = pp->p_offset;
X		end = pp->p_offset + pp->p_size;
X		for (pp = lp->d_partitions, i = 0;
X		     i < lp->d_npartitions; pp++, i++) {
X			if (pp->p_offset + pp->p_size <= start ||
X			    pp->p_offset >= end || i == SGRAW /*RAWPART*/)
X				continue;
X			if (ti->ti_openpart & (1 << i))
X				log(LOG_WARNING,
X				    "sg%d%c: overlaps open partition (%c)\n",
X				    unit, part + 'a', i + 'a');
X		}
X	}
X	switch (fmt) {
X	case S_IFCHR:
X		ti->ti_copenpart |= mask;
X		break;
X	case S_IFBLK:
X		ti->ti_bopenpart |= mask;
X		break;
X	}
X	ti->ti_openpart |= mask;
X	return (0);
X}
X
X/* ARGSUSED */
Xsgclose(dev, flags, fmt)
X	dev_t dev;
X	int flags, fmt;
X{
X	register int unit;
X	register struct target_info *ti;
X	int s, mask = (1 << scsipart(dev));
X
X	unit = scsiunit(dev);
X	ti = &t_info[unit];
X
X	switch (fmt) {
X	case S_IFCHR:
X		ti->ti_copenpart &= ~mask;
X		break;
X	case S_IFBLK:
X		ti->ti_bopenpart &= ~mask;
X		break;
X	}
X	ti->ti_openpart = ti->ti_copenpart | ti->ti_bopenpart;
X
X	/*
X	 * Should wait for I/O to complete on this partition even if
X	 * others are open, but wait for work on blkflush().
X	 */
X	if (ti->ti_openpart == 0) {
X		s = splhigh();
X		while (sgqueue[unit].b_actf)
X			sleep((caddr_t)&sgqueue[unit], PZERO - 1);
X		splx(s);
X		ti->ti_state = CLOSED;
X		ti->ti_wlabel = 0;
X	}
X	return (0);
X}
X
X/*
X * Initialise a drive.  If it is not already, bring it on line,
X * and set a timeout on it in case it fails to respond.
X * When on line, read in the pack label.
X */
Xtarg_init(ti, dev, flags)
X	register struct target_info *ti;
X	int dev, flags;
X{
X	register struct disklabel *lp;
X	register int unit;
X	char *msg, *readdisklabel();
X	int s, i, sgstrategy();
X	extern int cold;
X	char *inquiry(), *readcapacity(), *db;
X
X	if (flags & O_NDELAY)
X		return (0);
X
X	unit = scsiunit(dev);
X
X	ti->ti_state = RDLABEL;
X
X	/*
X	 * Read SCSI-level device info 
X	 */
X	if((db=inquiry( &active[unit] )) != NULL) {
X		*(db+31) = '\0';	*(db+45) = '\0';
X		printf("open: %d(0x%x) %s HW %d FW %d ROM %d SER %s\n",
X		       unit, dev,
X		       db+8, *(db+32)&0xff, *(db+33)&0xff, *(db+34)&0xff, db+36);
X	}
X
X	if((db=readcapacity( &active[unit] )) != NULL) {
X		ti->ti_dsize = ((db[0]&0xff) << 24) | ((db[1]&0xff) << 16) |
X				((db[2]&0xff) << 8) | (db[3]&0xff);
X		ti->ti_ssize = ((db[4]&0xff) << 24) | ((db[5]&0xff) << 16) |
X				((db[6]&0xff) << 8) | (db[7]&0xff);
X		printf("open: Capacity: %ld sector size=%ld\n", ti->ti_dsize, ti->ti_ssize);
X	}
X
X	/*
X	 * Set up default sizes until we have the label, or longer
X	 * if there is none.  Set secpercyl, as readdisklabel wants
X	 * to compute b_cylin (although we do not need it), and set
X	 * nsectors in case diskerr is called.
X	 */
X	lp = &ti->ti_label;
X	lp->d_secsize = DEV_BSIZE;
X	lp->d_secperunit = ti->ti_dsize;
X	lp->d_secpercyl = 1;
X	lp->d_npartitions = 1;
X	lp->d_secsize = 512;
X	lp->d_secperunit = ti->ti_dsize;
X	lp->d_nsectors = 17; /*ti->ti_geom.rg_nsectors;*/
X	lp->d_partitions[0].p_size = lp->d_secperunit;
X	lp->d_partitions[0].p_offset = 0;
X
X	/*
X	 * Read pack label.
X	 */
X	if ((msg = readdisklabel(
X				 makesgdev(major(dev), scsiunit(dev), SGRAW),
X				 sgstrategy, 
X				 lp, ti->ti_part, &ti->ti_bad, 0)) != NULL) {
X
X		if (cold)
X			printf(": %s", msg);
X		else
X			log(LOG_ERR, "sd%d: %s", unit, msg);
X#ifdef COMPAT_42
X		if (udamaptype(unit, lp))
X			ra->ra_state = OPEN;
X		else
X			ra->ra_state = OPENRAW;
X#else
X		ti->ti_state = OPENRAW;
X		printf("MakeFakeLabel!\n");
X		sg_makefakelabel(ti, lp);
X#endif
X	} else {
X		ti->ti_flags |= SGFL_BSDLABEL;
X		ti->ti_flags &= ~SGFL_WRITEPROT;
X		if (ti->ti_label.d_flags & D_BADSECT)
X			ti->ti_flags |= SGFL_BADSECT;
X		ti->ti_state = OPEN;
X      }
X
X	wakeup((caddr_t)ti->ti_state);
X	return (0);
X}
X
Xsg_makefakelabel(ti, lp)
X	register struct target_info *ti;
X	register struct disklabel *lp;
X{
X	lp->d_secsize = ti->ti_ssize;
X	lp->d_nsectors = 17;
X	lp->d_ntracks = 10;
X	lp->d_ncylinders = 1024;
X	lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
X	lp->d_npartitions = 8;
X	lp->d_partitions[0].p_offset = 0;
X	lp->d_partitions[0].p_size = ti->ti_dsize;
X}
X
Xsgsize(dev_t dev)
X{
X	int unit = scsiunit(dev), part = scsipart(dev), val;
X	struct target_info *ti;
X
X	if (unit >= NSG)
X		return(-1);
X
X	ti = &t_info[unit];
Xprintf("sgsize: open? %d dev 0x%x unit %d\n", ti->ti_flags & SGFL_WRITEPROT,
X	dev, unit);
X
X	if (ti->ti_state == CLOSED)
X		val = sgopen (makesgdev(major(dev), unit, SGRAW), FREAD, S_IFBLK, 0);
X/*	if (val != 0 || ti->ti_flags & SGFL_WRITEPROT)
X		return (-1);*/
X
Xprintf("sgsize rc = %d\n", (int)ti->ti_label.d_partitions[part].p_size);
X	return((int)ti->ti_label.d_partitions[part].p_size);
X}
X
X/*
X * Queue a transfer request, and if possible, hand it to the controller.
X *
X * This routine is broken into two so that the internal version
X * udastrat1() can be called by the (nonexistent, as yet) bad block
X * revectoring routine.
X */
Xsgstrategy(bp)
X	register struct buf *bp;
X{
X	register int unit;
X	register struct target_info *ti;
X	struct partition *pp;
X	int p;
X	daddr_t sz, maxsz;
X
X#ifdef DEBUG2
X	printf("sgstrat: dev= 0x%x blk=%d\n", bp->b_dev, bp->b_blkno);
X#endif
X
X	/*
X	 * Make sure this is a reasonable drive to use.
X	 */
X	if ((unit = scsiunit(bp->b_dev)) >= NSG || sgcinfo[unit] == NULL ||
X	    (ti = &t_info[unit])->ti_state == CLOSED) {
X		printf("sgstrat: %d is 'unreasonable'\n", unit);
X		bp->b_error = ENXIO;
X		goto bad;
X	}
X
X	/* "soft" write protect check */
X	if ((ti->ti_flags & SGFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
X		bp->b_error = EROFS;
X		bp->b_flags |= B_ERROR;
X		goto bad;
X	}
X
X	/* have partitions and want to use them? */
X	if ((ti->ti_flags & SGFL_BSDLABEL) != 0 && scsipart(bp->b_dev) != SGRAW) {
X
X		/*
X		 * do bounds checking, adjust transfer. if error, process.
X		 * if end of partition, just return
X		 */
X		if (bounds_check_with_label(bp, &ti->ti_label, ti->ti_wlabel) <= 0)
X			goto bad;
X		/* otherwise, process transfer request */
X
X		p = scsipart(bp->b_dev);
X		if ((ti->ti_openpart & (1 << p)) == 0) {
X			printf("sgstrat: partition %d not open\n", p);
X			bp->b_flags |= B_ERROR;
X			bp->b_error = ENODEV;
X			goto bad;
X		}
X	}
X
X	sgstrat1(bp);
X	return;
Xbad:
X	biodone(bp);
X}
X
X/*
X * Work routine for udastrategy.
X */
Xsgstrat1(bp)
X	register struct buf *bp;
X{
X	register int unit = scsiunit(bp->b_dev);
X	register struct buf *dp;
X	struct target_info *ti;
X	int s = splhigh();
X
X	/*
X	 * Append the buffer to the drive queue, and if it is not
X	 * already there, the drive to the controller queue.  (However,
X	 * if the drive queue is marked to be requeued, we must be
X	 * awaiting an on line or get unit status command; in this
X	 * case, leave it off the controller queue.)
X	 */
X	dp = &sgqueue[unit];
X	APPEND(bp, dp, av_forw);
X
X	/*disksort(dp, bp);*/	/* more efficient... someday */
X
X/*
X	if (dp->b_active == 0 && (ui->ui_flags & UNIT_REQUEUE) == 0) {
X		APPEND(dp, &um->um_tab, b_forw);
X		dp->b_active++;
X	}
X */	
X
X	/*
X	 * Start activity on the controller.  Note that unlike other
X	 * Unibus drivers, we must always do this, not just when the
X	 * controller is not active.
X	 */
X	sgstart(dp,s);
X	splx(s);
X}
X
Xsgstart(hp)
X	struct buf *hp;
X{
X	int unit, count, rc;
X	register struct buf *bp;
X	long l;
X	register struct target_info *ti;
X
X	ACTIVE *a;
X	u_long blknum;
X
X   do {
X	if(!(bp = hp->av_forw)) return;
X
X	unit = scsiunit(bp->b_dev);
X	ti = &t_info[unit];
X
X	if((a = &active[unit])->busy) { 
X#ifdef DEBUG2
X		printf("sgstart: Already Busy\n");
X#endif
X		return;
X	}
X	a->busy = 1;
X
X	blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / 512; /*xx->dk_dd.d_secsize;*/
X	count = bp->b_bcount;
X
X#ifdef DEBUG2
Xprintf("sgstart: b_blkno= %lu", bp->b_blkno);
Xprintf(" LBA=%lu TL=%d PO=%lu\n", blknum, count, ti->ti_label.d_partitions[scsipart(bp->b_dev)].p_offset);
X/*printf("sgstart: b_flags=0x%x\n", bp->b_flags);*/
X#endif
X
X	/*  add on partition offsets */
X	blknum += ti->ti_label.d_partitions[scsipart(bp->b_dev)].p_offset;
X
X	if((u_long) bp->b_blkno * DEV_BSIZE % 512 /*xx->dk_dd.d_secsize*/ ||
X	   bp->b_bcount >= MAXTRANSFER * CLBYTES ) {
X			bp->b_flags |= B_ERROR;
X			bp->b_error = EINVAL;
X
X			hp->av_forw = bp->av_forw;
X
X			biodone(bp);
X			a->busy = a->connected = 0;
X			return;
X	}
X
X	a->command_buf[0] = (bp->b_flags & B_READ) ? 0x08 : 0x0A; /* SCSIREAD : SCSIWRITE; */
X	a->command_buf[1] = (blknum >> 16) & 0x1f;
X	a->command_buf[2] = (blknum >> 8) & 0xff;
X	a->command_buf[3] = blknum & 0xff;
X	a->command_buf[4] = count / 512;
X	a->command_buf[5] = 0;
X
X	a->command = a->command_buf;	a->status = a->status_buf;
X	a->data = (char *)bp->b_un.b_daddr;
X	a->length = count;
X	a->nomsgs = 0;
X	a->xferslow = 0;
X
X	rc = scsi_arbitrate(a);
X#ifdef DEBUG1
X	printf("sgstart: arbitrate = %d\n", rc);
X#endif
X   } while(talk_scsi(hp, a) == 0);
X}
X
X
Xtalk_scsi(hp, a)
X	struct buf *hp;
X	register ACTIVE *a;
X{	register struct buf *bp;
X	int rc;
X
X	if(!(bp = hp->av_forw)) return;
X
X	rc = scsi_machine(a);
X#ifdef DEBUG1
X	printf("talk: SCSI_machine = %d <%d:%d>\n", rc, resel_fail, resel_loop);
X	resel_fail = 0;
X#endif
X
X	switch(rc) {
X	case CDISCONNECT:
X		a->connected = 0;
X#ifdef DEBUG1
X		printf("talk: Disconnect!\n");
X#endif
X		return(1);
X	case CTIMEOUT:
X	case CERROR:
X		bp->b_flags |= B_ERROR;		bp->b_error = EIO;
X		break;
X	case COK:
X#ifdef DEBUG1
X		printf("talk: I/O Complete... \n");
X#endif
X		bp->b_flags |= B_DONE;		bp->b_resid = 0;
X		break;
X	default:
X		printf("talk: unexpected scsi_machine return 0x%x\n", rc);
X		return(1);
X	}
X
X	a->busy = a->connected = 0;
X
X	hp->av_forw = bp->av_forw;
X	biodone(bp);
X	return(0);
X}
X
Xsgintr(unit)
X{	ACTIVE *a;
X	int t, t1, rc;
X	struct buf *hp, *bp;
X
X	int_happened++;
X
X	a = &active[0]; /* just for a starting place */
X	if(!(*a->cmdport & STSEL)) {
X		printf("sgintr: ignored (No SEL)\n");
X		return;
X	}
X	if((t=reconnect(a))) {
X		for(rc=1,t1=t;t1>>1;rc++) ;
X#ifdef DEBUG1
X		printf("sgintr: 0x%x (%d) reconnected <%d>\n",t,rc,resel_fail);
X		resel_fail = 0;
X#endif
X		if(!(a = &active[rc-1])->busy) {
X			printf("sgintr: uh-oh! 0x%x (%d) it's not busy\n", t, rc);
X		}
X		a->connected = 1;
X
X		if(talk_scsi((hp=&sgqueue[rc-1]), a) == 0) {
X#ifdef DEBUG1
X			printf("sgintr: try another\n");
X#endif
X			sgstart(hp);
X		}
X	} else {
X		resel_fail++;
X		/*printf("sgintr: reconnect failure\n");*/
X	}
X}
X
Xsgioctl(dev, cmd, addr, flag)
X	dev_t dev;
X	caddr_t addr;
X{
X	register struct target_info *ti;
X	int unit, rc = 0;
X
X	ti = &t_info[(unit=scsiunit(dev))];
X
X	switch(cmd) {
X	case DIOCGDINFO:
X		*(struct disklabel *)addr = ti->ti_label;
X		break;
X
X        case DIOCGPART:
X                ((struct partinfo *)addr)->disklab = &ti->ti_label;
X                ((struct partinfo *)addr)->part =
X                    &ti->ti_label.d_partitions[scsipart(dev)];
X                break;
X
X        case DIOCSDINFO:
X		printf("sgioctl: DIOCSDINFO\n");
X                if ((flag & FWRITE) == 0)
X			rc = EBADF;
X                else
X                        rc = setdisklabel(&ti->ti_label,
X					(struct disklabel *)addr,
X					  0, /*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/
X					  ti->ti_part);
X		if(rc == 0)
X			ti->ti_flags |= SGFL_BSDLABEL;
X
X                /*if (error == 0 && dk->dk_state == OPENRAW &&
X                    vdreset_drive(vddinfo[unit]))
X                        dk->dk_state = OPEN;*/
X		/*wdsetctlr(dev, du);*/
X                break;
X
X        case DIOCWLABEL:
X		printf("sgioctl: DIOCWLABEL\n");
X                if ((flag & FWRITE) == 0)
X                        rc = EBADF;
X                else
X			ti->ti_wlabel = *(int *)addr;
X                break;
X
X	case DIOCWDINFO:
X		printf("sgioctl: DIOCWDINFO\n");
X		if ((flag & FWRITE) == 0)
X			rc = EBADF;
X		else if ((rc = setdisklabel(&ti->ti_label,
X				(struct disklabel *)addr,
X				0, /*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/
X				ti->ti_part)) == 0) {
X                        int wlab;
X
X			ti->ti_flags |= SGFL_BSDLABEL;
X                        /*if (rc == 0 && dk->dk_state == OPENRAW &&
X                            vdreset_drive(vddinfo[unit]))
X                                dk->dk_state = OPEN; */
X			/*wdsetctlr(dev, du);*/
X
X                        /* simulate opening partition 0 so write succeeds */
X                        /* dk->dk_openpart |= (1 << 0);            /* XXX */
X			wlab = ti->ti_wlabel;
X			ti->ti_wlabel = 1;
X			rc = writedisklabel(dev, sgstrategy, 
X					    &ti->ti_label,
X					    ti->ti_part);
X			/*scsipart(dev));*/
X                        /*dk->dk_openpart = dk->dk_copenpart | dk->dk_bopenpart;*/
X			ti->ti_wlabel = wlab;
X                }
X                break;
X
X	default:
X		rc = ENOTTY;
X	}
X	return(rc);
X}
X
Xtestready(a)
X	ACTIVE *a;
X{	int rc;
X
X	a->command_buf[0] = 0; /*SCSITESTREADY;*/
X	a->command_buf[1] = 0;	a->command_buf[2] = 0;
X	a->command_buf[3] = 0;	a->command_buf[4] = 0;
X	a->command_buf[5] = 0;
X
X	a->command = a->command_buf;	a->status = a->status_buf;
X	a->data = 0;			a->length = 0;
X	a->nomsgs = 0;
X
X	rc = scsi_arbitrate(a);
X#ifdef DEBUG1
X	printf("Arbitrate = %d\n", rc);
X#endif
X	if(rc == COK)
X		rc = scsi_machine(a);
X#ifdef DEBUG1
X	printf("testready: scsi_machine = %d status = 0x%x\n", rc, a->status_buf[0]);
X#endif
X	return(rc);
X}
X
Xchar *
Xsense_err(a)
X	ACTIVE *a;
X{	int rc;
X
X	static char sensedata[128];
X
X	a->command_buf[0] = 0x03; /*SCSI_INQUIRY;*/
X	a->command_buf[1] = 0;	a->command_buf[2] = 0;
X	a->command_buf[3] = 0;	a->command_buf[4] = 27;
X	a->command_buf[5] = 0;
X
X	a->command = a->command_buf;	a->status = a->status_buf;
X	a->data = sensedata;		a->length = 27;
X	a->nomsgs = 0;
X	a->xferslow = 1;		a->blocksize = 512;
X
X	rc = scsi_arbitrate(a);
X#ifdef DEBUG1
X	printf("sense: arbitrate = %d\n", rc);
X#endif
X	if(rc == COK)
X		rc = scsi_machine(a);
X#ifdef DEBUG1
X	printf("sense: scsi_machine = %d status = 0x%x\n", rc, a->status_buf[0]);
X#else
X	printf("sense error:\n");
X	dumpmem(sensedata, 27);
X#endif
X	return((rc == COK) ? sensedata : (char *)0);
X}
X
Xchar *
Xinquiry(a)
X	ACTIVE *a;
X{	int rc;
X
X	static char inqdata[128];
X
X	a->command_buf[0] = 0x12; /*SCSI_INQUIRY;*/
X	a->command_buf[1] = 0;	a->command_buf[2] = 0;
X	a->command_buf[3] = 0;	a->command_buf[4] = 66;
X	a->command_buf[5] = 0;
X
X	a->command = a->command_buf;	a->status = a->status_buf;
X	a->data = inqdata;		a->length = 66;
X	a->nomsgs = 0;
X	a->xferslow = 1;		a->blocksize = 512;
X
X	rc = scsi_arbitrate(a);
X#ifdef DEBUG1
X	printf("inquiry: arbitrate = %d\n", rc);
X#endif
X	if(rc == COK)
X		rc = scsi_machine(a);
X#ifdef DEBUG1
X	printf("inquiry: scsi_machine = %d\n", rc);
X	printf("inquiry: Status Ret = 0x%x \n", a->status_buf[0]);
X	/*dumpmem(inqdata, 66);*/
X#endif
X	return((rc == COK) ? inqdata : (char *)0);
X}
X
Xchar *
Xreadcapacity(a)
X	ACTIVE *a;
X{	int rc;
X
X	static char capdata[128];
X	unsigned int capacity, blocksize;
X
X	a->command_buf[0] = 0x25; /*SCSI_READ-CAPACITY;*/
X	a->command_buf[1] = 0;	a->command_buf[2] = 0;
X	a->command_buf[3] = 0;	a->command_buf[4] = 0;
X	a->command_buf[5] = 0;  a->command_buf[6] = 0;
X	a->command_buf[7] = 0;  a->command_buf[8] = 0;
X	a->command_buf[9] = 0;
X
X	a->command = a->command_buf;	a->status = a->status_buf;
X	a->data = capdata;		a->length = 8;
X	a->nomsgs = 0;
X
X	a->xferslow = 1;		a->blocksize = 512;
X
X	rc = scsi_arbitrate(a);
X#ifdef DEBUG1
X	printf("ReadCapacity: arbitrate = %d\n", rc);
X#endif
X	if(rc == COK)
X		rc = scsi_machine(a);
X#ifdef DEBUG1
X	printf("ReadCapacity: scsi_machine = %d status = 0x%x\n", rc, a->status_buf[0]);
X/*	dumpmem(capdata, 8);
X	capacity = ((capdata[0]&0xff) << 24) | (capdata[1] << 16) | (capdata[2] << 8) | (capdata[3] & 0xff);
X	blocksize = (capdata[4] << 24) | (capdata[5] << 16) | (capdata[6] << 8) | capdata[7];
X	printf("ReadCapacity: %u blocksize %u\n", capacity, blocksize); */
X#endif
X	return((rc == COK) ? capdata : (char *)0);
X}
X
X
Xdumpmem(mem, len)
X	char *mem;
X	int len;
X{
X
X	char *line;
X	int off;
X
X	for(off=0; off<len; off++,mem++) {
X		if(off%16 == 0)	printf("\n0x%04x ", off);
X		if((*mem&0x7f) > ' ' && (*mem&0x7f) <= '~')
X			printf(" %c ", *mem&0x7f);
X		else	printf("%02x ", *mem & 0xff);
X	}
X	printf("\n");
X}
X/*#define DEBUG*/
X
X/* Header: scsi.c,v 1.3 90/01/23 11:33:00 brian Locked */
X/*
X
XSCSI disk driver for unix system V (Microport system V/386)
XThis driver uses the ST-01 controller.  This supports multiple initiators
Xand multiple targets.
X
XCopyright (c) 9.6.1988 Tatu Yl|nen
X              All rights reserved.
X
XFixes and mods by Brian E. Litzinger 12/1/1989
X*/
X
X/*#define COPYRIGHT "scsi disk driver V1.0  Copyright (c) 9.6.1988 Tatu Yl|nen\n                 V1.1 changes by Brian E. Litzinger 12/1/1989"*/
X
X/*#define ASM   /* use certain routines coded in assembly */
X
X#define RWTIMEOUT       5 /* timeout for read/write waiting for reconnect */
X
X#define splnointrs() splbio() /* disable any interrupts to this driver (clock!)*/
X
X/*#define NULL 0*/
X
X#define MYTARGETID      0x80        /* my address as bit mask */
X
X/* ST-01 Control Register */
X#define CMDENABLE       0x80        /* scsi enable */
X#define CMDENINTR       0x40        /* enable scsi interrupts */
X#define CMDPARENB       0x20        /* enable scsi parity generation */
X#define CMDSTARB        0x10        /* start arbitration bit */
X#define CMDATTN         0x08        /* scsi attention */
X#define CMDBSY          0x04        /* scsi busy */
X#define CMDSEL          0x02        /* scsi select */
X#define CMDRST          0x01        /* scsi reset */
X
X#define CMDBASE         (CMDPARENB|CMDENINTR) /* cmd when doing nothing */
X
X/* ST-01 Status Register */
X
X/* SCSI CCS commands */
X#define SCSIREAD        0x28        /* read command code (10-byte) */
X#define SCSIWRITE       0x2a        /* write command code (10-byte) */
X#define SCSIINQUIRY     0x12        /* inquiry command (6-byte) */
X#define SCSIREADCAPACITY 0x25       /* read drive capacity and block size */
X#define SCSIMODESELECT  0x15        /* select format parameters */
X#define SCSIFORMATUNIT  0x04        /* hard format the scsi drive */
X#define SCSIREQSENSE    0x03        /* request sense command */
X#define SCSITESTREADY   0x00        /* test unit ready command */
X
X#define MSGMYIDENTIFY   0xc0        /* our identify message to send to target */
X
X/* SCSI Reply Message */
X#define MSGCOMPLETE     0x00        /* command complete */
X#define MSGSAVEDATAPTR  0x02        /* save data pointer */
X#define MSGRESTOREPTR   0x03        /* restore pointer */
X#define MSGDISCONNECT   0x04        /* disconnect message */
X#define MSGIDETECTERR   0x05        /* initiator detected error */
X#define MSGABORT        0x06        /* scsi abort message */
X#define MSGMSGREJECT    0x07        /* message reject */
X#define MSGNOP          0x08        /* no operation message */
X#define MSGIDENTIFY     0x80        /* identify message from target */
X
X#define SCSIDATAPORTSZ	1024        /* size of data port on st01 */
X
Xstatic char timeouting=0;
Xstatic char intrserviced=0;
X#ifdef DEBUG
Xstatic int scsidebug=8;
X#endif
X
X/* This generates a hard reset on the scsi bus by asserting the reset line */
X
Xstatic scsi_resetbus(a)
X	ACTIVE *a;
X{
X  long l;
X  int u;
X
X  printf("st01: sending hard reset to scsi bus\n");
X  *a->cmdport = CMDBASE|CMDENABLE|CMDRST;
X  for (l=0;l<100000L;l++); /* keep rst asserted for a while */
X  *a->cmdport = CMDBASE;
X  for (l=0;l<5000000L;l++); /* give some time to recover before returning */
X  /*for (u=0;u<SCSIMAXDRIVES;u++)
X    d[u].connected=0; /* do this just in case */
X}
X
X/* This arbitrates for the scsi bus and selects the desired target.  This
X   returns a C* result code.  This will also set the connected flag
X   if appropriate.  If there are possibly recoverable errors, this will
X   retry.  The calling procedure should not retry if this returns
X   failure. */
X
Xstatic int scsi_arbitrate(a)
X	ACTIVE *a;
X{
X  long l;
X  int arbcnt, bsycnt; /* retry counts */
X
X  register int o, n;
X
X  arbcnt = bsycnt = 0;
X retryarb:
X  *a->cmdport = CMDBASE;
X  *a->dataport= MYTARGETID;
X  *a->cmdport = (CMDBASE&~CMDENINTR)|CMDSTARB;
X  /* wait for arbitration complete */
X  for (l=0;l<3000000L;l++) {
X    if ((n=*a->cmdport) & STARBCOMPL)
X      goto gotarb;
X    if(o!=n) { /*printf("0x%02x\n",n);*/ o=n; }
X  }
X  /* arbitration timeout - someone is keeping the bus reserved. */
X  *a->cmdport = CMDBASE;
X  if (arbcnt >= 2) /* retry twice, then give up */
X    {
X      printf("st01: arbitration timeout - someone is sitting on the bus?\n");
X      return CBUSBUSY;
X    }
X  scsi_resetbus(); /* reset the bus and hope the condition clears */
X  arbcnt++;
X  goto retryarb;
X gotarb:
X  arbcnt=0;
X  *a->dataport = MYTARGETID | a->target_id;
X  *a->cmdport = (a->nomsgs) ?
X		CMDBASE|CMDENABLE|CMDSEL :
X		CMDBASE|CMDENABLE|CMDSEL|CMDATTN;
X  for (l=0;l<2000000L;l++)
X    if (*a->cmdport & STBSY)
X      goto gotbusy;
X  /* timeout waiting for busy */
X  *a->cmdport = CMDBASE;
X  if (bsycnt >= 2) {
X#ifdef DEBUG
X    if (scsidebug>0)
X      printf("st01: arbitrate returning CNOCONNECT\n");
X#endif
X    return CNOCONNECT; /* probably no drive present */
X    }
X  bsycnt++;
X  for (l=0;l<2000l;l++); /* give some time for the drive */
X  printf("st01: busy timeout on drive %d\n",a->target_id);
X  goto retryarb;
X gotbusy:
X  a->connected = 1;
X  if (!a->nomsgs)
X    {
X      *a->cmdport = CMDBASE|CMDENABLE|CMDATTN;
X      for (l=0;l<500000;l++)
X        if ((*a->cmdport & (STMSG|STCD|STIO|STREQ)) == (STMSG|STCD|0|STREQ))
X          goto gotmsgreq;
X      /* timeout waiting for msg out */
X      printf("st01: timeout identify msg out - drive %d does not support messages?\n",
X             a->target_id);
X      a->nomsgs = 1; /* don't try messages again */
X      *a->cmdport = CMDBASE|CMDENABLE;
X      return COK;
X     gotmsgreq:
X      *a->dataport = MSGMYIDENTIFY;	/* this enables disconnect */
X      /* fall to successful completion */
X    }
X#ifdef DEBUG
X  if (scsidebug>5)
X    if (!(*a->cmdport & STBSY)) {
X	printf("st01: after successful arbitrate !STBSY\n");
X	for (l=0;l<10000000l;l++);
X	arbcnt++;
X	goto retryarb;
X    }
X#endif
X  *a->cmdport = CMDBASE|CMDENABLE;
X  return COK;
X}
X
X#ifndef ASM
X/* This copies data to the scsi data port as fast as possible.  This could
X   even be coded in assembly language for efficiency. */
X
Xstatic sendtoscsi(buf,len,port)
Xregister char *buf;
Xregister int len;
Xvolatile register char *port;
X{
X	/*printf("sendtoscsi: 0x%x %d\n", buf, len);*/
X	while (len--)
X		*port = (*buf++);
X}
X
X/* This reads data from the scsi data port as fast as possible. */
Xstatic getfromscsi(buf,len,port)
Xregister char *buf;
Xregister int len;
Xvolatile register char *port;
X{	/*printf("getfromscsi: 0x%x %d 0x%x\n", buf, len, port);/**/
X	while (len--)
X		*buf++ = *port;
X}
X
X#endif /* ASM */
X
X/* This implements the scsi data out phase.  There are several operating
X   modes for this.  1) normal as fast as possible io 2) slow io where we
X   check req individually for each character 3) moving data directly from
X   user space. If en error is encountered (such as a protection fault when
X   moving data from user space), this will return 0.  Moving data from
X   user space is only implemented in "fast" mode. */
X
Xstatic int
Xdataout(a)
X	ACTIVE *a;
X{
X  register int le;
X  register long l;
X
X#ifdef DEBUG
X  /*if (scsidebug==-3)*/
X    printf("dataout: xfering 0x%x\n", a->length);
X#endif
X  for (;a->length > 0;a->length -= le, a->data += le)
X    {
X      for (l=0;l<100000;l++)
X        if (*a->cmdport & STREQ)
X          goto gotreq;
X      /* timeout */
X      break;
X     gotreq:
X      if ((*a->cmdport & (STMSG|STCD|STIO)) != (0|0|0))
X        break;
X      if (a->xferslow)
X        {
X          le=1;
X          *a->dataport = *a->data;
X          continue;
X        }
X      le= a->length;
X      if (le > SCSIDATAPORTSZ)
X        le=SCSIDATAPORTSZ;
X      if (le > a->blocksize)
X        le= a->blocksize;
X		/* elide transfer from user space crap */
X        sendtoscsi(a->data, le, a->dataport);
X    }
X  while ((*a->cmdport & (STMSG|STCD|STIO|STREQ)) == (0|0|0|STREQ))
X    { printf(".");
X      *a->dataport = 0;
X      for (l=0;l<100000;l++)
X        if (*a->cmdport & STREQ)
X          break;
X    }
X  return 1;
X}
X
X/* this implements the scsi data in phase.  This copies data from
X   scsi bus to system memory.  There are three modes of operation:
X   1) "slow" transfer to kernel memory 2) "fast" transfer to kernel
X   memory 3) "fast" transfer to user memory */
X
Xstatic int
Xdatain(a)
X	ACTIVE *a;
X{
X  register int le;
X  long l;
X
X#ifdef DEBUG
X  /*if (scsidebug==-3)  */
X    printf("datain: xfering %x\n",a->length);
X#endif
X  for (;a->length > 0; a->length -= le, a->data += le) {
X      for (l=0;l<1000000;l++)
X        if (*a->cmdport & STREQ)
X          goto gotreq;
X#ifdef DEBUG
X      /*if (scsidebug==-3)*/
X	printf("datain: timed out waiting for STREQ!\n");
X#endif
X      /* timeout */
X      break;
X     gotreq:
X      if ((*a->cmdport & (STMSG|STCD|STIO)) != (0|0|STIO))
X        break;
X      if (a->xferslow) {
X          le=1;		*a->data = (*a->dataport);
X          continue;
X      }
X
X      le = a->length;
X      if (le > SCSIDATAPORTSZ)
X        le = SCSIDATAPORTSZ;
X      if (le > a->blocksize)
X        le =  a->blocksize;
X	/* elide transfer to user space crap */
X      getfromscsi(a->data, le, a->dataport);
X#ifdef DEBUG
X      /*if (scsidebug==-3) */
X	printf(" xferd 0x%x", le);
X#endif
X  }
X#ifdef DEBUG
X  /*if (scsidebug==-3)*/
X      printf("\n");
X#endif
X  while ((*a->cmdport & (STMSG|STCD|STIO|STREQ)) == (0|0|STIO|STREQ)) {
X      printf(".");
X      le = (*a->dataport);
X      for (l=0; l < 100000;l++)
X        if (*a->cmdport & STREQ)
X          break;
X  }
X  return 1;
X}
X
X/* This is called when we are connected to the target on the scsi bus.
X   This will do any exchange of data with the target.  The dialog is
X   controlled by the target.  This will remain connected until the
X   target sends a disconnect message, the command is complete, or a timeout
X   if encountered. There should be no interrupts while this is executing,
X   as the unit should be connected all the time.  This returns a C* completion
X   status. Normally, this should return quite fast.  This will never sleep
X   and will also be called at interrupt time.  With dumb drives not supporting
X   disconnect (are there any?) this would block the system for the duration
X   of this call.  This will only mark the drive not busy if the command
X   completed successfully.  If an error is returned, the drive has not
X   been marked not busy. */
X
Xstatic int
Xscsi_machine(a)
XACTIVE *a;
X{
X  int msg;
X  long l;
X  int i;
X  for (l=0;l<100000 || a->xfertimeout == 0;l++) {
X      if (!(*a->cmdport & STBSY)) {
X#ifdef DEBUG
X	if (scsidebug>5)
X          printf("st01: doxfernosleep: !STBSY unit %d\n",a->target_id);
X#endif
X	a->connected=0;
X	return CERROR; /* we are no longer connected??? */
X      }
X      if (!(*a->cmdport & STREQ))
X        continue; /* loop until target requesting something */
X#ifdef DEBUG1
X      if ((scsidebug==-1) | (scsidebug>5))
X	printf("st01: scsmachine: new state=%x ",*a->cmdport);
X#endif
X      switch ((*a->cmdport & (STMSG|STCD|STIO)) & 0xff)
X        {
X          case 0|0|0: /* data out */
X#ifdef DEBUG1
X	    printf("DATA OUTPUT phase\n");
X#endif
X            if (!dataout(a)) {
X#ifdef DEBUG
X		if (scsidebug>0)
X		  printf("st01: dataout returned error; unit=%d\n",a->target_id);
X#endif
X                return CERROR;
X              }
X            break;
X          case 0|0|STIO: /* data in */
X#ifdef DEBUG1
X	    printf("DATA INPUT phase\n");
X#endif
X            if (!datain(a))
X              {
X#ifdef DEBUG
X		if (scsidebug>0)
X		  printf("st01: datain returned error; unit=%d\n",a->target_id);
X#endif
X                return CERROR;
X              }
X            break;
X          case 0|STCD|0: /* command out */
X#ifdef DEBUG1
X	    printf("command out 0x%x\n", *a->command);
X#endif
X            *a->dataport=(*a->command++);
X            break;
X          case 0|STCD|STIO: /* status in */
X            *a->status=(*a->dataport);
X#ifdef DEBUG1
X	    printf("status in 0x%x\n", *a->status);
X#endif
X            break;
X          case STMSG|STCD|0: /* msg out */
X            /* we should never get here.  We don't want to send a message.
X               Lets just drop attention and hope the drive understands. */
X#ifdef DEBUG
X	      if ((scsidebug==-2) || (scsidebug==-1) || (scsidebug>0))
X	        printf("st01: unexpected msg out state; status=%x, ignored\n",*a->cmdport);
X#endif
X	      *a->dataport = MSGNOP; /* send a no-operation message */
X	      *a->cmdport = CMDBASE|CMDENABLE;
X	      break;
X          case STMSG|STCD|STIO: /* msg in */
X            msg = (*a->dataport) & 0xff;
X#ifdef DEBUG1
X	    printf("msg in 0x%x\n", msg);
X#endif
X            switch (msg)
X              {
X                case MSGCOMPLETE:
X                  a->connected = 0;
X                  *a->cmdport = CMDBASE;
X#ifdef DEBUG
X		  if (scsidebug>3)
X		    printf("command MSGCOMPLETE received\n");
X#endif
X                  if (a->xferstatus == 0) /* completed succesfully */
X                    {
X                     /* marknotbusy(unit,COK);*/
X#ifdef DEBUG
X		      if (scsidebug>3)
X			  printf("st01: MSGCOMPLETE successful\n");
X#endif
X                      return COK;
X                    }
X#ifdef DEBUG
X		  if (scsidebug>0)
X		    printf("st01: MSGCOMPLETE failed\n");
X#endif
X                  return CERROR;
X                case MSGSAVEDATAPTR:
X                  a->save.data = a->data;
X                  a->save.length = a->length;
X#ifdef DEBUG
X		  if (scsidebug>3)
X		    printf("st01: MSGSAVEDATAPTR received\n");
X#endif
X                  break;
X                case MSGRESTOREPTR:
X		  a->command = a->save.command;
X		  a->data = a->save.data;
X		  a->status = a->save.status;
X		  a->length = a->save.length;
X#ifdef DEBUG
X		  if (scsidebug>3)
X		    printf("st01: MSGRESTOREPPTR received\n");
X#endif
X                  break;
X                case MSGDISCONNECT:
X                  a->connected=0;
X                  a->xfertime=1;
X                  *a->cmdport = CMDBASE;
X#ifdef DEBUG
X		  if (scsidebug>3)
X		    printf("st01: MSGDISCONNECT received\n");
X#endif
X                  return CDISCONNECT;
X                case MSGMSGREJECT:
X#ifdef DEBUG1
X		  printf("st01: MSGMSGREJECT\n");
X#endif
X                  break; /* the target rejected some message... Who cares. */
X                case MSGNOP:
X#ifdef DEBUG1
X		  printf("st01: MSGNOP\n");
X#endif
X                  break; /* this should not be sent by the target, but... */
X                case MSGIDENTIFY:
X#ifdef DEBUG1
X		  printf("st01: MSGIDENTIFY\n");
X#endif
X                  break; /* we don't care about targets identify messages */
X                default:
X                  if (msg & 0x80) {
X#ifdef DEBUG1
X		  	printf("st01: default Identify\n");
X#endif
X                    break; /* assume it is an identify message */
X		    }
X                  printf("st01: unknown message received from drive %d: %x\n",
X                         a->target_id, msg);
X                  break;
X              }
X            break;
X          default:
X            /* unexpected stack state.  Now I don't know what to do.  Lets
X               hope the drive changes to another state. */
X	      printf("st01: unexpected bus state: status=%x\n",*a->cmdport);
X            break;
X        }
X    }
X#ifdef DEBUG
X  if (scsidebug>0)
X    printf("st01: doxfernosleep timed out\n");
X#endif
X  return CTIMEOUT;
X}
X
X/* This performs the actual reconnect.  The return value is the target that
X * requested the reconnect.  If it is 0 (false), the reconnect was not for us.
X */
Xreconnect(a)
X	ACTIVE *a;
X{	long l;
X	unsigned char ch;
X
X#ifdef KLUDGE
X      for(l=10;l && ((*a->cmdport) & (STSEL|STIO|STBSY)) != (STSEL|STIO|0);l--)
X	;
X	resel_loop = (int) l;
X#endif
X
X
X      if (!((ch=*a->cmdport) & (STSEL))) {
X#ifdef DEBUG1
X	printf("st01: wrong state 0x%x\n",ch);
X#endif
X	return(0);
X      }
X
X      for(l=5;l && ((*a->cmdport) & (STSEL|STIO|STBSY)) != (STSEL|STIO|0);l--)
X	;
X	resel_loop = (int) l;
X
X      ch=(*a->dataport);
X      if (!(ch & MYTARGETID)) {
X#ifdef DEBUG3
X /*       if (scsidebug>0)*/
X          printf("st01: polled releselection was not for me: %x\n",ch);
X#endif
X          return(0);
X      }
X      ch&=~MYTARGETID;
X      if (ch != a->target_id) {
X#ifdef DEBUG3
X	/*  if (scsidebug>0)*/
X	    printf("st01: reselecting (polled) unit other than expected: %x\n", ch);
X#endif
X          return(0);
X      }
X      *a->cmdport=(CMDBASE&~CMDENINTR)|CMDBSY|CMDENABLE;
X      for (l=0;l<50000;l++)
X        if (!(*a->cmdport & STSEL))
X          break;
X      for (l=0;l<50000;l++)
X	if (!(*a->cmdport & STBSY))
X	  break;
X      *a->cmdport=CMDBASE|CMDENABLE;
X      /*a->connected=1;*/
X#ifdef DEBUG
X      if (scsidebug>3)
X	printf("st01: polledwaitreconnect returned (true)\n");
X#endif
X      return ch;
X}
X
X#endif
END_OF_st01-1.c
if test 41764 -ne `wc -c <st01-1.c`; then
    echo shar: \"st01-1.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f st01-s.s -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"st01-s.s\"
else
echo shar: Extracting \"st01-s.s\" \(1234 characters\)
sed "s/^X//" >st01-s.s <<'END_OF_st01-s.s'
X#/* This copies data to the scsi data port as fast as possible.  This could
X#   even be coded in assembly language for efficiency. */
X
X#static sendtoscsi(buf,len,port)
X#register char *buf;
X#register int len;
X#volatile register char *port;
X
X#NO_APP
Xgcc_compiled.:
X.text
X	.align 2
X.globl _sendtoscsi
X_sendtoscsi:
X	pushl %ebp
X	movl %esp,%ebp
X	pushl %ebx
X	pushl %edi
X	pushl %esi
X	pushl %ecx
X	movl 8(%ebp),%esi	# buf
X	movl 16(%ebp),%edi	# port
X	movl 12(%ebp),%ecx	# len
X	rep
X	movsb
X#	jmp L6
X#L4:
X#	movb (%esi),%bl
X#	movb %bl,(%edi)
X#	incl %esi
X#L6:
X#	decl %ecx
X#	cmpl $-1,%ecx
X#	jne L4
X	popl %ecx
X	popl %esi
X	popl %edi
X	leal -4(%ebp),%esp
X	popl %ebx
X	leave
X	ret
X	.align 2
X# This reads data from the scsi data port as fast as possible
X#static getfromscsi(buf,len,port)
X#register char *buf;
X#register int len;
X#volatile register char *port;
X.globl _getfromscsi
X_getfromscsi:
X	pushl %ebp
X	movl %esp,%ebp
X	pushl %ebx
X	pushl %edi
X	pushl %esi
X	pushl %ecx
X	movl 8(%ebp),%edi	# buf
X	movl 16(%ebp),%esi	# port
X	movl 12(%ebp),%ecx	# len
X	rep
X	movsb
X#	jmp L12
X#L10:
X#	movb (%esi),%al
X#	movb %al,(%edi)
X#	incl %edi
X#L12:
X#	decl %ecx
X#	cmpl $-1,%ecx
X#	jne L10
X	popl %ecx
X	popl %esi
X	popl %edi
X	leal -4(%ebp),%esp
X	popl %ebx
X	leave
X	ret
X
X#	rep
X#	movsb
X
END_OF_st01-s.s
if test 1234 -ne `wc -c <st01-s.s`; then
    echo shar: \"st01-s.s\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f conf.c.diff -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"conf.c.diff\"
else
echo shar: Extracting \"conf.c.diff\" \(1172 characters\)
sed "s/^X//" >conf.c.diff <<'END_OF_conf.c.diff'
X*** conf.c.orig	Sun Nov  1 15:34:13 1992
X--- sgconf.c	Sat Oct 31 16:28:06 1992
X***************
X*** 102,107 ****
X--- 102,120 ----
X  #define	fdsize		NULL
X  #endif
X  
X+ #include "sg.h"
X+ #if NSG > 0
X+ int	sgopen(),sgclose(),sgstrategy(),sgioctl(),sgsize();
X+ #define	sgdump		enxio
X+ #else
X+ #define	sgopen		enxio
X+ #define	sgclose		enxio
X+ #define	sgstrategy	enxio
X+ #define	sgioctl		enxio
X+ #define	sgdump		enxio
X+ #define	sgsize		NULL
X+ #endif
X+ 
X  int	swstrategy(),swread(),swwrite();
X  
X  struct bdevsw	bdevsw[] =
X***************
X*** 116,121 ****
X--- 129,136 ----
X  	  wtdump,	wtsize,		B_TAPE },
X  	{ asopen,	asclose,	asstrategy,	asioctl,	/*4*/
X  	  asdump,	assize,		NULL },
X+ 	{ sgopen,	sgclose,	sgstrategy,	sgioctl,	/*5*/
X+ 	  sgdump,	sgsize,		NULL }
X  };
X  int	nblkdev = sizeof (bdevsw) / sizeof (bdevsw[0]);
X  
X***************
X*** 214,219 ****
X--- 229,237 ----
X  	{ asopen,	asclose,	rawread,	rawwrite,	/*D*/
X  	  asioctl,	enodev,		nullop,		NULL,
X  	  seltrue,	enodev,		asstrategy },
X+ 	{ sgopen,	sgclose,	rawread,	rawwrite,	/*E*/
X+ 	  sgioctl,	enodev,		nullop,		NULL,
X+ 	  seltrue,	enodev,		sgstrategy },
X  };
X  int	nchrdev = sizeof (cdevsw) / sizeof (cdevsw[0]);
X  
END_OF_conf.c.diff
if test 1172 -ne `wc -c <conf.c.diff`; then
    echo shar: \"conf.c.diff\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0