h43494
s 00065/00036/00310
d D 1.3 82/09/13 13:47:48 ross 3 2
c Release drive properly in multi disc configs.
c Read extended selch address, not just 16 bit address.
e
s 00000/00000/00346
d D 1.2 82/09/13 12:06:16 ross 2 1
c 
e
s 00346/00000/00000
d D 1.1 82/09/13 10:29:48 ross 1 0
e
u
U
t
T
I 1
/*
 * 'MSM' disk driver - 67MB and 256MB
 */

#include "../h/param.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/selch.h"
#include "../h/systm.h"
#include "../h/dskmap.h"

D 3
#define DN	3		/* disk number for monitoring */
#define NBPT	32		/* blocks per track */
#define NBPC	(msmsize? 608 : 160)	/* blocks per cylinder */
#define NMSMERR	10		/* number of error retries */
E 3
I 3
#define	DN	3			/* disk number for monitoring */
#define	NBPT	32			/* blocks per track */
#define	NBPC	(msmsize? 608 : 160)	/* blocks per cylinder */
#define	NMSMERR	10			/* number of error retries */
E 3

D 3
extern int	nmsm;		/* number of disks */
extern int	msmselch;	/* selch address for MSM */
extern int	msmcntl;	/* controller address */
extern char	msmaddr[];	/* drive addresses */
extern int	msmsize;	/* 0 => 67MB,  1 => 256MB */
extern struct dskmap msmmap[];	/* logical device mapping */
E 3
I 3
extern int	nmsm;			/* number of disks */
extern int	msmselch;		/* selch address for MSM */
extern int	msmcntl;		/* controller address */
extern char	msmaddr[];		/* drive addresses */
extern int	msmsize;		/* 0 => 67MB,  1 => 256MB */
extern struct dskmap msmmap[];		/* logical device mapping */
E 3

int msmseek(), msmscintr();

struct buf	msmtab;
struct buf	rmsmbuf;

struct selchq msmscq {
	&msmseek,
	&msmscintr,
	0
};

int	msmdrive;
int	msmcyl;
int	msmhead;
int	msmsector;
int	msmsad, msmead;


/* disk drive status & commands */
#define	ALTBSY	0x20
#define	UNS	0x10
#define UNREADY	0x08
#define	SINC	0x02
#define	OFFL	0x01

#define	DISABLE	0x80
#define ENABLE	0x40
#define	DISARM	0xc0
#define SETHEAD	0x20
#define	SETCYL	0x10
#define	SEEK	0x02
#define RELEASE	0xb0
I 3
#define	RESTORE	0x01
E 3

/* disk controller status & commands */
#define	CNTL_UNRCV	0xc1
#define	CYL_OV		0x10
#define IDLE		0x02

#define	READ		0x01
#define	WRITE		0x02
#define	RESET		0x08

I 3

#define	CNTLIDLE	while ((ss(msmcntl) & IDLE) == 0)

E 3
/*
 * Status in msmtab.b_active
 */
#define WSELCH	1	/* waiting for selch */
#define WALT	2	/* waiting for alternate channel release */
#define WSEEK	3	/* waiting for seek to complete */
#define WIO	4	/* waiting for I/O to complete */

/*
 *	disk strategy routine
 */
msmstrategy(bp)
register struct buf	*bp;
{
	bp->b_resid = bp->b_bcount;

	if ((minor(bp->b_dev)>>3) >= nmsm) {
		bp->b_flags |= B_ERROR;
		iodone(bp);
		return;
	}

	/*
	 * Block no. too high -- looks like EOF for raw read, error otherwise
	 */
	if (bp->b_blkno >= msmmap[minor(bp->b_dev)&07].dm_nblk) {
		if ((bp->b_flags&(B_PHYS|B_READ)) != (B_PHYS|B_READ))
			bp->b_flags |= B_ERROR;
		iodone(bp);
		return;
	}
	dk_numb[DN]++;
	dk_wds[DN] += (bp->b_bcount>>6);
	bp->av_forw = 0;
	spl5();
	if (msmtab.b_actf == 0)
		msmtab.b_actf = bp;
	else
		msmtab.b_actl->av_forw = bp;
	msmtab.b_actl = bp;
	if (msmtab.b_active == 0)
		msmstart();
	spl0();
}

/*
 * start next disk i/o operation
 *	- set up drive address, cylinder/head/sector address
 *	- set up memory start & end addresses
 *	- initiate seek
 */

msmstart()
{
	register struct buf *bp;
	register stat;
	register bn;

	if (!(bp = msmtab.b_actf))
		return;
	msmtab.b_active = WSELCH;
	dk_busy |= 1<<DN;

	trace(0x80<<16, "mstart", bp);

	msmdrive = msmaddr[minor(bp->b_dev)>>3];
	bn = bp->b_blkno + msmmap[minor(bp->b_dev)&07].dm_baddr;
	msmcyl = bn / NBPC;
	bn %= NBPC;
	msmhead = bn / NBPT;
	msmsector = (bn % NBPT)<<1;

	msmsad = bp->b_un.b_addr;
	msmead = bp->b_un.b_addr + bp->b_bcount - 1;

	selchreq(msmselch, &msmscq);
}

msmseek()
{
	register stat;

	trace(0x80<<16, "seek", msmdrive);
	trace(0x80<<16, "cyl", msmcyl);

D 3
	while ((ss(msmcntl)&IDLE) == 0)
		;
E 3
I 3
	CNTLIDLE;
E 3
	trace(0x80<<16, "stat", ss(msmdrive));
	trace(0x80<<16, "stat", ss(msmdrive));
	if ((stat = ss(msmdrive))&ALTBSY) {
		trace(0x80<<16, "alt", stat);
		msmtab.b_active = WALT;
		oc(msmdrive, ENABLE);
		selchfree(msmselch);
		return;
	}
	trace(0x80<<16, "stat", stat);
	msmtab.b_active = WSEEK;
	wh(msmdrive, msmcyl);
D 3
	while (((stat=ss(msmcntl))&IDLE) == 0)
		;
E 3
I 3
	CNTLIDLE;
E 3
	oc(msmdrive, SETCYL|DISARM);
D 3
	while (((stat=ss(msmcntl))&IDLE) == 0)
		;
E 3
I 3
	CNTLIDLE;
	oc(msmdrive, 0x30);		/*** dummy command for possible timing problem ***/
E 3
	oc(msmdrive, SEEK|ENABLE);
D 3
	while (((stat=ss(msmcntl))&IDLE) == 0)
		;
E 3
I 3
	CNTLIDLE;
E 3
	trace(0x80<<16, "stat", ss(msmdrive));
}

/*
 * disk interrupt routine
 *	-disk interrupts after a seek, or when Alternate Channel Busy clears
 *	-check status, initiate read/write or seek
 */
D 3

E 3
msmintr(dev,stat)
{
	register struct buf *bp;
	register scmd, ccmd;

D 3
	trace(0x40<<16, "interrupt", msmaddr[dev]);
	trace(0x40<<16, "status", stat);
E 3
I 3
	trace(0x80<<16, "interrupt", msmaddr[dev]);
	trace(0x80<<16, "status", stat);
E 3

	if (!(bp = msmtab.b_actf))
		return;
	switch (msmtab.b_active) {
		case WALT:
			msmtab.b_active = WSELCH;
			selchreq(msmselch, &msmscq);
			return;

		case WSEEK:
			msmtab.b_active = WIO;
			break;

		default:
			return;
	}

	if (stat & (UNS|UNREADY|SINC|OFFL|ALTBSY)) {
		msmerror(bp, stat, msmdrive);
		return;
	}

	if (bp->b_flags & B_READ) {
		scmd = READ_GO;
		ccmd = READ|ENABLE;
	} else {
		scmd = GO;
		ccmd = WRITE|ENABLE;
	}

	trace(0x80<<16, "mcmd", ccmd);
	trace(0x80<<16, "head", msmhead);
	trace(0x80<<16, "sector", msmsector);

	oc(msmselch, STOP);
	trace(0x80<<16, "bufstart", msmsad);
	trace(0x80<<16, "bufend", msmead);
	wdh(msmselch, msmsad);
	wdh(msmselch, msmead);
	wh(msmdrive, msmhead);
	oc(msmdrive, SETHEAD|DISARM);
D 3
	while ((ss(msmcntl)&IDLE) == 0)
		;
E 3
I 3
	CNTLIDLE;

E 3
	wd(msmcntl, msmsector);
	wh(msmcntl, (msmhead<<10)+msmcyl);
	wh(msmdrive, msmhead);
	oc(msmdrive, SETHEAD|DISARM);
D 3
	while ((ss(msmcntl)&IDLE) == 0)
		;
E 3
I 3
	CNTLIDLE;

E 3
	oc(msmcntl, ccmd);
	oc(msmselch, scmd);
I 3
	trace(0x40<<16, "msmiogoing", (msmdrive<<16)|(msmcntl<<8)|msmselch);
E 3
}

/*
 * selch interrupt routine
 *	-selch interrupt will be followed by a controller interrupt
 *		( nothing to do here? )
I 3
 *		read selch end addr!
E 3
 */

msmscintr(dev, stat)
{
I 3
	register int ead;
E 3
	register struct buf *bp;

I 3
	trace(0x80<<16, "selch intr", msmselch);

E 3
	oc(msmselch, STOP);
I 3
	oc(msmselch, 0x40);		/*** read extended address ***/
	trace(0x80<<16, "ead", rdh(msmselch));
E 3
}

/*
 * disk controller interrupt routine
 *	-check ending status, signal i/o done
 */
D 3

E 3
msmcintr(dev, stat)
{
	register struct buf *bp;
	register struct dskmap *dm;

D 3
	trace(0x40<<16, "interrupt", msmcntl);
	trace(0x40<<16, "status", stat);
E 3
I 3
	trace(0x80<<16, "interrupt", msmcntl);
	trace(0x80<<16, "status", stat);
E 3


	if (!(bp = msmtab.b_actf))
		return;
	if (msmtab.b_active != WIO)
		return;

	if (stat & CNTL_UNRCV) {
D 3
		oc(msmcntl, RESET);
E 3
I 3
		/*** oc(msmcntl, RESET); ***/
E 3
		msmerror(bp, stat, msmcntl);
		return;
	}
	bp->b_resid = 0;

	/*
	 * Cylinder overflow
	 */
	if (stat & CYL_OV) {
		oc(msmcntl, RESET);
		oc(msmselch, STOP);
I 3
		/*** should send read-extended addr ??? ***/
E 3
		msmsad += (rdh(msmselch)-msmsad+2)&~0377;
D 3
		trace(0x40<<16, "cyl ov", msmsad);
E 3
I 3
		trace(0x80<<16, "cyl ov", msmsad);
E 3
		msmsector = msmhead = 0;
		bp->b_resid = msmead - msmsad;
		dm = &msmmap[minor(bp->b_dev)&07];
		if ((++msmcyl*NBPC) < dm->dm_baddr + dm->dm_nblk) {
			msmseek();
			return;
		} else
			if ((bp->b_flags&(B_PHYS|B_READ)) != (B_PHYS|B_READ))
				bp->b_flags |= B_ERROR;
	}

D 3
	trace(0x80<<16, "release", msmdrive);
E 3
I 3
	trace(0x40<<16, "release", msmdrive);
	oc(msmcntl, DISARM);
	CNTLIDLE;
E 3
	oc(msmdrive, RELEASE);
D 3
	while ((ss(msmcntl)&IDLE) == 0)
		;
E 3
I 3
	/***
	     This sense status is required to fully release the
	     drive when operating with more than one drive on a
	     single controller.
	***/
	ss(msmdrive);
	/***
	     CNTLIDLE here will re-assign the drive
	     and then we will have to wait for hardware
	     release time-out!
	***/

E 3
	msmtab.b_errcnt = 0;
	msmtab.b_active = 0;
	dk_busy &= ~(1<<DN);
	msmtab.b_actf = bp->av_forw;
	iodone(bp);
	selchfree(msmselch);
	msmstart();
}

msmerror(bp, stat, addr)
register struct buf	 *bp;
{
	deverror(bp, stat, addr);
I 3
	if (addr == msmcntl)
		oc(msmcntl, RESET);
	if (addr == msmdrive)
		oc(msmaddr, RESTORE);
	CNTLIDLE;

E 3
	if (++msmtab.b_errcnt <= NMSMERR) {
		msmseek();
		return;
	}
D 3
	trace(0x80<<16, "release", msmdrive);
E 3
I 3
	trace(0x40<<16, "release", msmdrive);
	oc(msmcntl, DISARM);
	CNTLIDLE;
E 3
	oc(msmdrive, RELEASE);
D 3
	while ((ss(msmcntl)&IDLE) == 0)
		;
E 3
I 3
	/*** See comment above on ss after release ***/
	ss(msmdrive);
	/*** See comment above on CNTLIDLE ***/

E 3
	msmtab.b_errcnt = 0;
	bp->b_flags |= B_ERROR;
	msmtab.b_active = 0;
	dk_busy &= ~(1<<DN);
	msmtab.b_actf = bp->av_forw;
	iodone(bp);
	selchfree(msmselch);
	msmstart();
}

/*
 * 'Raw' disc interface
 */
msmread(dev)
{
	physio(msmstrategy, &rmsmbuf, dev, B_READ);
}

msmwrite(dev)
{
	physio(msmstrategy, &rmsmbuf, dev, B_WRITE);
}
E 1
