Ausam/sys/dmr/msc.c
#
/*
* Optimised Microcomputer Systems 1100 driver
* used with Ampex DM 9100 disc drives.
* Dated 8 - 3 - 78.
*
* When instructed correctly (via the ifdefs) this driver will attempt
* to queue io requests by drive, cylinder and sector, and endeavour
* to overlap seeks with data transfers for more than one drive.
* Method is very similar to Childrens Museum RK driver.
*
* Some simple error correction and recovery is attempted, but nothing
* fancy. You have been WARNED.
*
* Bugs and comments to Peter Ivanov
* Dept. Computer Science,
* University of New South Wales.
*/
#include "../defines.h"
#include "../param.h"
#include "../buf.h"
#include "../conf.h"
#include "../seg.h"
#include "../user.h"
/* global defines */
#define SWEEP /* enable checking to force max head travel */
/*#define NO_SEEK /* to stop pre-seeking */
/*#define VOLUME_STATS /* define to enable stats collection per volume */
/*#define DRIVE_STATS /* define to enable stats collection per drive and cylinder */
#define IO_STATS /* count sectors read & written */
/*#define NO_OFFLINEQ /* stop qing of requests for offlined drives */
/*#define DROP_OFFLINEQ /* delete drive request q when offlined */
/*#define RETRIES_ONLY /* don't print error messages for first soft error */
/*#define LAST_CYL /* to accumulate the info about the last seek distance on drive */
/* configuration details */
#define MSADDR 0176700 /* address of device */
#define NDRV 2 /* the number of real 9100 drives on controller */
#define MSCYLS 411 /* 411 cylinders per pack */
#define MSTRK 19 /* number of tracks per cyl (platters) */
#define MSSEC 21 /* number of sectors per track */
#define MSBYTE 512 /* number of bytes per sector */
#define TVOL (sizeof ms_sizes/sizeof ms_sizes[0]) /* total number of volumes */
#define MSAGE 100 /* number of times this block may be pre-empted for io
before io is forced */
#define INTLV 5 /* interleaving factor for rp04 type drives */
#define MSSPL spl5 /* priority of ms disc */
/* definition of device registers */
struct {
int msccra; /* command completion register A */
int msccrb; /* command completion register B */
int mscsr; /* control and status register */
int msctra; /* command transmission register A */
int msctrb; /* command transmission register B */
/* int filler; /* dummy filler */
/* int mscsr; /* second occurrence of control and status reg */
};
/* structure of an incore disc command descriptor - one per drive */
struct msdesc
{
int ds_id; /* command identifier - drive number for us */
int ds_stat; /* status word */
char ds_nexthi; /* hi portion of link to next descriptor */
char ds_sfc; /* system fault code */
unsigned ds_nextlo; /* lo portion of link to next descriptor */
int ds_discop; /* disc operation code */
int ds_drive; /* disc drive number */
int ds_cylinder; /* disc cylinder number */
int ds_head; /* disc head number (track) */
int ds_sector; /* disc sector number */
unsigned ds_wcount; /* word count */
unsigned ds_memlo; /* lo part of memory address */
unsigned ds_memhi; /* hi part of memory address - watch bit 6! */
unsigned ds_rid1; /* part one of record id on "readid"s */
unsigned ds_rid2; /* part two of record id on "readid"s */
/*
* it is possible for the controller to return many more words than this
* up to 41 infact but the command which causes this will NEVER be used.
*/
};
/*
* The following maps logical volumes of various sizes to locations
* on various drives. This must be altered to add extra drives.
* PLEASE GET THEM CORRECT FIRST TIME.
*/
struct {
unsigned nblocks;
int cyloff;
int drive;
# ifdef VOLUME_STATS
long refcount; /* count of accesses to this volume */
# endif
} ms_sizes[] {
/* 0: 0->163 (164cyls) drive 0 */ { 65436l, 0, 0, } /* system and source */
/* 1: 164->213 ( 50cyls) drive 0 */ ,{ 19950l, 164, 0, } /* users and swap */
/* 2: 214->245 ( 32cyls) drive 0 */ ,{ 12768l, 214, 0, } /* system disc */
/* 3: 246->409 ( 164cyls) drive 0 */ ,{ 65436l, 246, 0, } /* system users */
/* 4: 410->411 ( 1cyls) drive 0 */ ,{ 399l, 410, 0, } /* MSC DIAGNOSTIC AREA */
/* 5: 0->163 (164cyls) drive 1 */ ,{ 65436l, 0, 1, } /* secondary system and swap */
/* 6: 164->213 ( 50cyls) drive 1 */ ,{ 19950l, 164, 1, } /* user disk 1 */
/* 7: 214->245 ( 32cyls) drive 1 */ ,{ 12768l, 214, 1, } /* tmp disc */
/* 8: 246->409 ( 164cyls) drive 1 */ ,{ 65436l, 246, 1, } /* user disc 2 */
/* 9: 410->411 ( 1cyls) drive 1 */ ,{ 399l, 410, 1, } /* MSC DIAGNOSTIC AREA */
};
# ifdef IO_STATS
long msrsec; /* sectors read */
long mswsec; /* sectors writ */
# endif
/*
* structure of a disc queue
*/
struct msq
{
struct buf *ms_bufp; /* pointer to first buffer in queue for this drive */
struct msdesc ms_desc; /* descriptor for this drive */
int ms_flags; /* flags for qs */
#ifdef SWEEP
int ms_lcyl; /* last cylinder accessed on this drive */
char ms_dirf; /* current direction of head movement */
#endif
#ifdef DRIVE_STATS
char ms_nreq; /* number of requests in queue - stats */
char ms_rmax; /* high water mark for q */
unsigned ms_cyls[MSCYLS]; /* accesses per cylinder */
#endif
} ms_q[NDRV];
/* definition of io error retry strategy */
int retry[]
{
0, /* no variance */
0, /* no variance */
0, /* no variance */
1, /* early data strobe */
2, /* late data strobe */
3, /* positive cylinder offset, normal data strobe */
4, /* negative cylinder offset, normal data strobe */
5, /* positive cylinder offset, early data strobe */
6, /* negative cylinder offset, early data strobe */
7, /* positive cylinder offset, late data strobe */
8 /* negative cylinder offset, late data strobe */
};
/* and so the max number of retries may be defined as */
#define MSERRMAX (sizeof retry/2)
/*
* We use a few buffer variables for other purposes here, ie:
* b_scratch to hold unit/cylinder address of this request
* b_resid to hold track/sector address of this request
* av_back to hold request age count
*/
#define ms_cyl b_scratch /* at least an int */
#define ms_sector b_resid.lobyte
#define ms_track b_resid.hibyte
#define ms_age av_back.integ
struct devtab mstab;
#ifndef RAW_BUFFER_POOL
struct buf msbuf;
#endif
/* some handy masks */
#define BYTE 0377
#define LSD 07 /* least significant octal digit */
#define UNIT 016000 /* the position of unit no. in ms_cyl */
#define CYL 0777 /* the desired/present cylinder bits */
/* some equally handy numbers */
#define UP 1 /* the disc cyls are moving up */
#define DOWN 0 /* the disc cyld are moving down */
#define SBYTE 8 /* amount by which to shift for a byte */
#define SUNIT 10 /* amount by which to shift unit nos. */
/* some flags */
#define MS_IDLE 0 /* drive is quiescent */
#define MS_SEEK 1 /* seeking flag for q header */
#define MS_READY 2 /* ready for io flag for q header */
#define MS_ERROR 3 /* doing error recovery */
#define MS_IO 4 /* actually doing io */
#define MS_RECAL 5 /* actually doing a recal */
/* ccra bits */
#define DSKPON 0100000 /* disk drive powered on */
#define CMDQERR 0010000 /* command queue error */
#define CMDNXM 0004000 /* nxm during command descriptor fetch */
#define CMDPAR 0002000 /* memory parity during descriptor fetch */
/* csr bits */
#define RDYCC 0100000 /* ready from command completion */
#define RESET 0002000 /* reset controller */
#define CCACK 0001000 /* command completion acknowledge */
#define CARDY 0000400 /* command address ready */
#define RDYCT 0000200 /* ready for command transmission */
#define CTIE 0000100 /* command transmission interrupt enable */
#define CCIE 0000040 /* command completion interrupt enable */
#define PWRCYC 0000020 /* controller power cycle */
#define CTLRON 0000010 /* controller power is on */
#define DMAON 0000004 /* direct memory access in progress */
#define BOOT 0000001 /* load bootstrap into location 0 */
/* status bits */
#define RETRY 0100000 /* retry occurred due to data miss */
#define DATAERR 0040000 /* uncorrectable data error (read) */
#define ECCERR 0020000 /* ecc correction made */
#define IDERR 0010000 /* error in sector ID field */
#define ADDRERR 0004000 /* bad address in sector ID field */
#define WRTPRTC 0002000 /* write protected sector */
#define ALTSECT 0001000 /* alternate sector flag */
#define DATMISS 0000400 /* data miss (hard) */
#define SEEKERR 0000200 /* illegal seek or seek incomplete */
#define DRVNRDY 0000100 /* drive not ready */
#define DRVFALT 0000020 /* drive fault or select lock */
#define NXMERR 0000010 /* transfer referenced non-existant memory */
#define PARERR 0000004 /* memory parity error during transfer */
#define SYSFLT 0000002 /* system fault */
#define PRTCDRV 0000001 /* write protected drive */
/* a few opcodes */
#define DSTATE 02 /* obtain drive status */
#define RECAL 04 /* recalibrate drive */
#define SEEK 05 /* seek specified cylinder */
#define WRITE 06 /* write data */
#define READ 020 /* read data */
char ms_onlinef;
#ifdef UNIBUS_MAP & MAPPED_BUFFERS
int *ms_map, ms_offset; /* UNIBUS MAP register and address */
#endif UNIBUS_MAP & MAPPED_BUFFERS
long ms_transf; /* words transferred (for error rate calculations) */
unsigned ms_softerrs;
msopen()
{
register int n;
register struct msq *qp;
register struct msdesc *msd;
#ifdef MALLOC_UMAP
extern int umap[];
#endif MALLOC_UMAP
/*
* if there are no active drives,
* then clear controller
* initialise
*/
#ifdef UNIBUS_MAP & MAPPED_BUFFERS
if( !ms_map)
{
if( (n = malloc( umap, 1)) == NULL)
{
u.u_error = EIO;
return;
}
ms_map = &(UBMAP->r[ (--n) << 1]);
*ms_map = ms_q;
*(ms_map + 1) = 0;
ms_offset = n;
}
#endif UNIBUS_MAP & MAPPED_BUFFERS
MSSPL();
if( !ms_onlinef)
{
MSADDR->mscsr = RESET;
#ifndef UNIBUS_MAP & MAPPED_BUFFERS
MSADDR->msctra = 0;
#endif UNIBUS_MAP & MAPPED_BUFFERS
MSADDR->mscsr = CCIE;
ms_onlinef = 0377;
for(n=0; n<NDRV; n++)
{
/* initialise the relevant memory places */
msd = &(qp = &ms_q[n])->ms_desc;
qp->ms_flags = MS_IDLE;
msd->ds_nexthi=0;
msd->ds_nextlo=0;
msd->ds_id=n;
msd->ds_drive=n;
}
}
spl0();
}
msstrategy(bp)
register struct buf *bp;
{
register unsigned p1, p2;
register int f;
bp->b_error = 0; /* so that error retrys are correct */
p1 = bp->b_dev.d_minor & BYTE;
/*
* Validate the request
*/
if(
(p1 >= TVOL)
|| ((p2 = &ms_sizes[p1])->nblocks <= bp->b_blkno)
#ifdef NO_OFFLINEQ | DROP_OFFLINEQ
|| ( !(ms_onlinef & (1<< (p2->drive))))
#endif
)
{
bp->b_flags =| B_ERROR;
iodone(bp);
return;
}
#ifdef _1170
if(bp->b_flags & B_PHYS)
mapalloc(bp);
#endif
# ifdef VOLUME_STATS
p2->refcount++;
# endif
# ifdef IO_STATS
if( bp->b_flags & B_READ )
msrsec =- bp->b_wcount.hibyte;
else
mswsec =- bp->b_wcount.hibyte;
# endif
bp->av_forw = 0;
bp->ms_age = MSAGE;
bp->ms_cyl = p2->cyloff | ((f = p2->drive & LSD) << SUNIT);
p1 = bp->b_blkno;
bp->ms_sector = lrem(p1,MSSEC); /* sector */
p1 = ldiv(p1,MSSEC); /* trk and cyl */
bp->ms_track = p1 % MSTRK;
bp->ms_cyl =+ p1/MSTRK;
p2 = &ms_q[f];
/*
* Now "ms_cyl" contains "drive and cylinder"
* "ms_track" contains the head number,
* and "ms_sector" contains the sector.
*/
#ifdef DRIVE_STATS
p2->ms_cyls[bp->ms_cyl & CYL]++;
p2->ms_nreq++;
#endif
MSSPL();
if ((p1 = p2->ms_bufp) == NULL)
{
/* this queue is empty */
p2->ms_bufp = bp;
msstart();
}
else
{
/*
* the queue is not empty, so place in queue so as to
* minimise head movement.
*/
#ifdef SWEEP
f = p2->ms_dirf;
#endif
p2 = p1->av_forw;
while(p2)
{
/* skip any overtaken blocks */
if( !(p2->ms_age))
p1 = p2;
p2 = p2->av_forw;
}
for(; p2 = p1->av_forw; p1 = p2)
if ( p1->ms_cyl <= bp->ms_cyl
&& bp->ms_cyl <= p2->ms_cyl
|| p1->ms_cyl >= bp->ms_cyl
&& bp->ms_cyl >= p2->ms_cyl
){
while (bp->ms_cyl == p2->ms_cyl)
{
/*
* for a cylinder match, do the
* rotational optimisation.
* INTLV should be set to an optimal
* value, thought to be around 5
* and should NOT be altered if the discs
* are reformatted or the free list
* reordered...... good luck.
*/
if(p2->ms_sector > p1->ms_sector)
{
if(bp->ms_sector > p1->ms_sector + INTLV
&& bp->ms_sector < p2->ms_sector - INTLV)
goto out;
}
else
if(bp->ms_sector > p1->ms_sector + INTLV
|| bp->ms_sector < p2->ms_sector - INTLV)
goto out;
p1 = p2;
if( !(p2 = p1->av_forw)) goto out;
}
break;
}
#ifdef SWEEP
else
if (f == UP)
{
if(p2->ms_cyl < p1->ms_cyl)
if(bp->ms_cyl > p1->ms_cyl)
goto out;
else
f = DOWN;
}
else
{
if(p2->ms_cyl > p1->ms_cyl)
if(bp->ms_cyl < p1->ms_cyl)
goto out;
else
f = UP;
}
#endif
out:
bp->av_forw = p2;
p1->av_forw = bp;
while(p2)
{
/* count down overtaken blocks */
if( p2->ms_age)
p2->ms_age--;
p2 = p2->av_forw;
}
}
spl0();
}
/*
* msstart() goes through all the queues and sets everybody
* seeking that should be.
*/
msstart()
{
/* called at MSSPL or greater */
register struct buf *bp;
register struct msq *qp;
register int n;
int ioflg;
static int drv, ioage;
#ifndef NO_SEEK
for(n=0; n<NDRV; n++)
if((bp=(qp= &ms_q[n])->ms_bufp) && (ms_onlinef & (1<<n)))
{
/*
* for all active drives
*/
if(qp->ms_flags == MS_IDLE)
{
/*
* which are seekable, start seeking
*/
if(qp->ms_lcyl == bp->ms_cyl)
qp->ms_flags = MS_READY;
else
{
#ifdef SWEEP
if(bp->ms_cyl > qp->ms_lcyl)
qp->ms_dirf = UP;
else
qp->ms_dirf = DOWN;
#endif SWEEP
mscstart(bp, qp, SEEK);
qp->ms_flags = MS_SEEK;
}
}
}
#endif
/*
* start a retry with recalibrate
*/
for(n=0; n<NDRV; n++)
if((bp = (qp = &ms_q[n])->ms_bufp) && (ms_onlinef & (1<<n)))
{
/*
* for all active drives
*/
if( qp->ms_flags == MS_ERROR)
{
/*
* which are recalibratable
*/
mscstart(bp, qp, RECAL);
qp->ms_flags = MS_RECAL;
qp->ms_lcyl = 0;
}
}
/*
* try to start an io
*/
ioflg = 0;
do
{
for(n = 0; n < NDRV; n++)
{
/*
* for the next io-able drive in a circular
* lookup, start an io if waiting
*/
if((bp = (qp = &ms_q[drv])->ms_bufp) &&
#ifndef NO_SEEK
(qp->ms_flags == MS_READY) &&
#endif
(ms_onlinef & (1<<drv)))
{
ioflg++;
if(ioage <= MSAGE)
{
if(bp->b_flags & B_READ)
mscstart(bp, qp, READ);
else
mscstart(bp, qp, WRITE);
qp->ms_flags = MS_IO;
ioage++;
return;
}
}
if( ++drv >= NDRV) drv = 0;
ioage = 0;
}
}
while( ioflg); /* Ioage caused us to miss an I/O */
}
msintr()
{
/* called at MSSPL or greater */
register struct msdesc *msd;
register struct msq *qp;
register struct buf *bp;
int n;
/*
* Here we have three possibilities, these being
* 1) Normal command complete
* 2) Disc powered up
* 3) A command (NXM, PAR, QERR)
*/
if(MSADDR->msccra & (CMDQERR | CMDNXM | CMDPAR))
{
/* absolute failure, cannot recover */
printf("MSERR FATAL CCRA ERROR\n");
MSADDR->mscsr =| CCACK;
return;
}
/*
* now have three further possibilities, namely
* 1) drive powered up
* 2) error in completed command
* 3) command complete
*/
if(MSADDR->msccra & DSKPON)
{
/* a drive has powered up */
printf("DRIVE POWERED ON, CCRB=%o\n", MSADDR->msccrb);
ms_onlinef =| MSADDR->msccrb & BYTE;
MSADDR->mscsr =| CCACK;
}
else
{
/* command complete, with or without errors */
bp = (qp = &ms_q[n = MSADDR->msccrb & BYTE])->ms_bufp;
msd = &qp->ms_desc;
MSADDR->mscsr =| CCACK;
if(msd->ds_stat & ~(RETRY | ECCERR))
{
/* an error!!! */
if(msd->ds_stat & (PRTCDRV|WRTPRTC|ALTSECT|NXMERR|PARERR))
{
/*
* operation cannot continue.
* NXMERR or PARERR are pretty nasty
* but we mustn't hang up driver.
*/
printf("MSERR UNRECOVERABLE\n");
/*debugging*/ printf("DS_WC=%d, DS_ML=%d, DS_MH=%d\n",msd->ds_wcount,msd->ds_memlo,msd->ds_memhi);
/*debugging*/ printf("BP_WC=%d, BP_ML=%d, BP_MH=%d\n",bp->b_wcount, bp->b_addr, bp->b_xmem);
mserr(qp, bp, 1);
bp->b_flags =| B_ERROR;
msdone(qp, bp);
}
else
{
/* error but can continue */
mserr(qp, bp, 0); /* only give error if already tried twice */
if(msd->ds_stat & (ADDRERR | SEEKERR | DRVNRDY
| SYSFLT | DRVFALT))
{
/* retry and recalibrate */
qp->ms_flags = MS_ERROR;
}
else
{
/* data error, retry by positioning */
qp->ms_flags = MS_READY;
}
if(errcnt(bp))
{
msdone(qp, bp);
if( (msd->ds_stat & DRVNRDY) ||
(msd->ds_sfc == 1) )
{
ms_onlinef =& ~(1<<n);
#ifdef DROP_OFFLINEQ
while(bp=qp->ms_bufp)
{
bp->b_flags =| B_ERROR;
#ifdef DRIVE_STATS
if(qp->ms_nreq-- > qp->ms_rmax)
qp->ms_rmax = qp->ms_nreq+1;
#endif
qp->ms_bufp = bp->av_forw;
iodone(bp);
}
#endif
}
}
}
}
else
{
/* command complete */
switch (qp->ms_flags)
{
case MS_SEEK: /* seek complete */
qp->ms_flags = MS_READY;
break;
case MS_RECAL: /* error recovery complete */
qp->ms_flags = MS_IDLE;
break;
case MS_IO: /* IO complete */
#ifdef SWEEP
qp->ms_lcyl = bp->ms_cyl;
#endif
msdone(qp, bp);
break;
default: /* can NEVER happen */
printf("MSERR \"FUCK\"\n");
mserr(qp, bp, 1);
break;
}
}
}
msstart();
}
errcnt(bp)
register struct buf *bp;
{
if( ++bp->b_error >= MSERRMAX)
{
bp->b_flags =| B_ERROR;
return(1);
}
return(0);
}
msdone(qp, bp)
register struct msq *qp;
register struct buf *bp;
{
qp->ms_flags = MS_IDLE;
qp->ms_bufp = bp->av_forw;
#ifdef DRIVE_STATS
if(qp->ms_nreq-- > qp->ms_rmax)
qp->ms_rmax = qp->ms_nreq+1;
#endif
bp->b_resid = -qp->ms_desc.ds_wcount;
iodone(bp);
}
mserr(qp, bp, flag)
register struct msq *qp;
struct buf *bp;
int flag;
{
#ifdef RETRIES_ONLY
if(bp->b_error > 0 /* second try at least */ || flag)
#endif RETRIES_ONLY
{
printf("MS %o,CRA=%o,SR=%o,ST=%o,FC=%o,OP=%o,CYL=%o,HD=%o,SEC=%o,RETRYS=%o\n",
qp->ms_desc.ds_drive,
MSADDR->msccra,
MSADDR->mscsr,
qp->ms_desc.ds_stat,
qp->ms_desc.ds_sfc,
qp->ms_desc.ds_discop,
qp->ms_desc.ds_cylinder,
qp->ms_desc.ds_head,
qp->ms_desc.ds_sector,
bp->b_error);
}
if((++ms_softerrs & 017) == 0)
{
printf("Error rate %dx1,000 words per error\n",
longdiv(((ms_transf + (1<<7))>>8), (ms_softerrs<<2)));
/* really times 1024, but this is close enough */
}
#ifdef LAST_CYL
{
int diff;
printf("lastcyl=%o, currcyl=%o, distance=",
qp->ms_lcyl & CYL,
bp->ms_cyl & CYL);
if( (diff = (bp->ms_cyl & CYL) - (qp->ms_lcyl & CYL)) < 0)
printf("-%o\n", -diff);
else
printf("+%o\n", diff);
}
#endif LAST_CYL
}
/*
* Physical IO:
* truncate transfers at the ends of logical volumes
*/
unsigned msresidue;
msread(dev)
int dev;
{
register unsigned resid;
if(msphys(dev))
{
resid = msresidue;
#ifndef RAW_BUFFER_POOL
physio(msstrategy, &msbuf, dev, B_READ);
#else RAW_BUFFER_POOL
physio(msstrategy, 0, dev, B_READ);
#endif RAW_BUFFER_POOL
u.u_count =+ resid;
}
}
mswrite(dev)
int dev;
{
register unsigned resid;
if(msphys(dev))
{
resid = msresidue;
#ifndef RAW_BUFFER_POOL
physio(msstrategy, &msbuf, dev, B_WRITE);
#else RAW_BUFFER_POOL
physio(msstrategy, 0, dev, B_WRITE);
#endif RAW_BUFFER_POOL
u.u_count =+ resid;
}
}
msphys(dev)
int dev;
{
register unsigned a, b, c;
b = u.u_offset >> 9;
if( ((c = dev.d_minor & BYTE) >= TVOL) ||
((a = ms_sizes[c].nblocks) <= b) )
{
u.u_error = ENXIO;
return(0);
}
c = u.u_count;
if(ldiv(c+511, 512) + b > a)
c = (a - b) << 9;
msresidue = u.u_count - c;
u.u_count = c;
return(1);
}
#ifdef MSPOWERFAIL
mspowerf(rdev)
{
/*
* Msc-Ampex disc power fail recovery routine
*
* It is ASSUMED that
*
* . a power fail has occured and the cpu did a reset
* . mem management registers have been restored
* . as have unibus map regs as have
* . the other vital ones.
*/
register struct buf *bp;
register struct msq *qp;
register d;
long n;
MSADDR->hpcs2=CLR;
#ifndef NO_SEEK
for(d=0; d< NDRV; d++)
ms_q[n].ms_flags = MS_IDLE;
#endif
if(rdev)
{
for(d=0; d<NDRV; d++)
if(ms_onlinef & (1<<d))
{
/*
* this drive was on when it died
*/
MSADDR->hpcs2 = d;
for(;;)
{
if( (MSADDR->hpcs1 & CRDY) && (MSADDR->hpds & MOL) && (MSADDR->hpds & DRY) )
{
msdinit(d);
break;
}
for(n=2000000; n>0; n--);
printf("MSERR PWR FAIL RECVRY WAITING DRIVE %o\n", d);
}
}
}
else
{
ms_onlinef = 0;
}
msstart();
printf("MSERR PWR FAIL RECVRD\n");
}
#endif
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*
* Start routine for MSC controller
*/
mscstart(bp, qp, com)
register struct buf *bp;
register struct msq *qp;
int com;
{
register char *msd;
/*
* ensure that RDYCT is up before doing it
*/
while( !(MSADDR->mscsr & RDYCT));
msd = &qp->ms_desc;
if(com & (READ | WRITE))
{
msd->ds_memlo = bp->b_addr;
msd->ds_memhi = bp->b_xmem & 03;
msd->ds_wcount = -bp->b_wcount;
ms_transf =- bp->b_wcount;
msd->ds_sector = bp->ms_sector;
msd->ds_head = bp->ms_track;
}
msd->ds_cylinder = bp->ms_cyl & CYL;
if(com == READ)
com =| retry[bp->b_error];
msd->ds_discop = com;
#ifndef MAPPED_BUFFERS & UNIBUS_MAP
MSADDR->msctrb = msd;
#else MAPPED_BUFFERS & UNIBUS_MAP
MSADDR->msctrb = msd + (ms_offset << 13) - *ms_map;
MSADDR->msctra = ms_offset >> 3;
#endif MAPPED_BUFFERS & UNIBUS_MAP
MSADDR->mscsr = CCIE | CARDY;
}