V7M/sys/dev/hk.c
/*
* UNIX/v7m RK06/7 disk driver
* Fred Canter 6/19/81
*
* This driver supports, overlapped seek,
* interleaved file systems, head offset positioning,
* and full ECC recovery on block and raw I/O transfers.
*
* Thanks to Jerry Brenner for 95% of this driver !
*/
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/seg.h"
/*
* Define REGDUMP to get a dump of all the
* RK06/7 device registers on an error.
*/
/*#define REGDUMP 1 */
#define HKEOFF 1 /* if 1 then enable offset recovery */
/*
*
* The following structure defines the order of the Control
* and Status registers for the RK06/07
*
*/
struct device
{
int hkcs1; /* Control and Status register 1 */
int hkwc; /* Word Count Register */
caddr_t hkba; /* Memory Address Register */
int hkda; /* Track Sector Address Register */
int hkcs2; /* Control And Status Register 2 */
int hkds; /* Drive Status Register */
int hkerr; /* Error Register */
union{
int w; /* High byte is Attention Summary */
char c[2]; /* Low is Offset Reg. */
}hkas;
int hkdc; /* Desired Cylinder Register */
int dum; /* Dummy for unused location */
int hkdb; /* Data Buffer */
int hkmr1; /* Maintenance Register 1 */
int hkec1; /* ECC Position Register */
int hkec2; /* ECC Pattern Register */
int hkmr2; /* Maint. Register 2 */
int hkmr3; /* Maint. Register 3 */
};
#define HKADDR ((struct device *)0177440) /* RK6/711 address */
#define NHK 2 /* number of drives on system */
#define NSECT 22 /* number of sectors per pack */
#define NTRAC 3 /* number of tracks per cylinder */
#define NUMTRY 28 /* max retry count */
/*
*
* The structure hk_sizes defines the logical separation of a disk pack
* into mountable file systems.
*
* The file systems are named in numeric order.
*
* the first entry in hk_sizes describes file system hk0
* the next hk1 , etc.
*
* The nblocks entry specifies the number of blocks (512 bytes) in
* the file system.
*
* The cyloff entry specifies the offset+1 from the physical beginning
* of the pack to the beginning of the file system in cylinders
*
*/
struct size
{
daddr_t nblocks;
int cyloff;
} hk_sizes[8] =
{
9636, 0, /* cyl 0-145, root on rk06/7 */
8910, 146, /* cyl 146-280, swap on rk06/7 */
8514, 281, /* cyl 281-409, rest of rk06 pack */
26598, 411, /* cyl 411-813, rest of rk07 pack */
0, 0, /* spare, for avoiding bad blocks */
0, 0, /* spare, for avoiding bad blocks */
27060, 0, /* cyl 0-409, all of rk06 pack */
53724, 0, /* cyl 0-813, all of rk07 back */
};
/*
*
* The following definitions specify the offset values
* used during error recovery
*
*/
#ifdef HKEOFF
#define P25 01 /* +25 Rk06, +12.5 Rk07 */
#define M25 0201 /* -25 RK06, -12.5 RK07 */
#define P200 010 /* +200 RK06, +100 RK07 */
#define M200 0210 /* -200, RK06, -100 RK07 */
#define P400 020 /* +400 RK06 , +200 RK07 */
#define M400 0220 /* -400 RK06 , -200 RK07 */
#define P800 040 /* +800 RK06 , +400 RK07 */
#define M800 0240 /* -800 RK06 , -400 RK07 */
#define P1200 060 /* +1200 RK06 , +600 RK07 */
#define M1200 0260 /* -1200 RK06 , -600 Rk07 */
/*
*
* The following array defines the order in which the offset values defined above
* are used during error recovery.
*
*/
int hk_offset[16] =
{
P25, M25, P200, M200,
P400, M400, P400, M400,
P800, M800, P800, M800,
P1200, M1200, P1200, M1200,
};
#endif
/*
* Control and Status bit definitions for hkcs1
*/
#define GO 01 /* GO bit */
#define SELECT 01 /* Select Function */
#define PAKACK 02 /* Pack Acknowledge Function */
#define DCLR 04 /* Drive Clear Function */
#define UNLOAD 06 /* Unload Heads Function */
#define STRSPN 010 /* Start Spindle Function */
#define RECAL 012 /* Recalibrate Function */
#define OFFSET 014 /* Offset Function */
#define SEEK 016 /* Seek Function */
#define RCOM 020 /* Read Command */
#define WCOM 022 /* Write Command */
#define RHDR 024 /* Read Header */
#define WHDR 026 /* Write Header */
#define WCHK 030 /* Write Check Function */
#define IEI 0100 /* Interrupt Inable bit */
#define CRDY 0200 /* Controller Ready bit */
#define SEL7 02000 /* Select RK07 bit */
#define CTO 04000 /* Controller Time Out bit */
#define CFMT 010000 /* Controller Format bit */
#define DTCPAR 020000 /* Drive to Controller Parity Error */
#define DINTR 040000 /* Drive Interrupt bit */
#define CCLR 0100000 /* Controller Clear bit */
#define CERR 0100000 /* Controller Error bit */
/*
* Control and Status bit definitions for hkcs2
*/
#define DLT 0100000 /* Data Late error */
#define WCE 040000 /* Write Check Error */
#define UPE 020000 /* Unibus Parity Error */
#define NED 010000 /* Nonexistent Drive error */
#define NEM 04000 /* Nonexistent Memory error */
#define PGE 02000 /* Programming error */
#define MDS 01000 /* Multiple Drive Select error */
#define UFE 0400 /* Unit Field Error */
#define SCLR 040 /* Subsystem Clear bit */
#define BAI 020 /* Bus Address Increment Inhibit bit */
#define RLS 010 /* Release bit */
/*
* Control and Status bit definitions for hkds
*/
#define SVAL 0100000 /* Status Valid bit */
#define CDA 040000 /* Current Drive Attention bit */
#define PIP 020000 /* Position In Progress bit */
#define WRL 04000 /* Write Lock bit */
#define DDT 0400 /* Disk Drive Type bit */
#define DRDY 0200 /* Drive Ready bit */
#define VV 0100 /* Volume Valid bit */
#define DROT 040 /* Drive Off Track Error */
#define SPLS 020 /* Speed Loss Error */
#define ACLO 010 /* Drive AC Low */
#define DOFST 04 /* Drive Offset bit */
#define DRA 01 /* Drive Available bit */
/*
* Control and Status bit definitions for hkerr
*/
#define DCK 0100000 /* Data Check error */
#define DUNS 040000 /* Drive Unsafe error */
#define OPI 020000 /* Operation Incomplete error */
#define DTE 010000 /* Drive Timing error */
#define DWLE 04000 /* Drive Write Lock error */
#define IDAE 02000 /* Invalid Disk Address Error */
#define COE 01000 /* Cylinder Overflow Error */
#define HRVC 0400 /* Header Vertical Redundance Check Error */
#define BSE 0200 /* Bad Sector Error */
#define ECH 0100 /* Error Correction Hard error */
#define DTYE 040 /* Drive Type error */
#define FMTE 020 /* Format Error */
#define CDPAR 010 /* Controller To Driver Parity Error */
#define NXF 04 /* Nonexecutable Function Error */
#define SKI 02 /* Seek Incomplete error */
#define ILF 01 /* Illegal Function Error */
struct
{
int drvtyp; /* This contains the drive type */
char mntflg; /* This contains a mounted flag it is set to indicate*/
/* that a drive type is known to the system */
char recal; /* recalibrate flag */
int ccyl; /* stores drives current cylinder */
int cs1; /* Error reporting save locations */
int wc;
int ba;
int da;
int cs2;
int ds;
int err;
int as;
int dc;
int mr1;
int mr2;
int mr3;
}hk_r[NHK];
struct buf hktab; /* This is the buffer header used exclusivly by the RK06/07 for block I/O */
/* the structure is the same as buf.h but useage is slightly different */
struct buf rhkbuf; /* This buffer header is used by the RK06/07 for raw I/O. struct. same as hktab */
struct buf hkutab[NHK]; /* Unit headers for overlapped seek driver */
#define trksec av_back
#define cylin b_resid
/*
* Instrumentation (iostat) structures
*/
struct ios hk_ios[NHK];
#define DK_N 2
#define DK_T 3 /* RK06/7 transfer rate indicator */
#define b_cylin b_resid
daddr_t dkblock();
/******** flag field defines for hk tables **********/
#define HKBUSY 01 /* drive or controller is busy */
#define HKSEEK 02 /* drive is seeking */
#define HKOFFS 04 /* drive in offset mode */
/*
*
* The following routine determines the drive type of the selected unit,
* saves drive type in hk_r[].drvtyp,
* and sets flag in hk_r[].mntflg
*
*/
hkmnt(dn)
{
register int pri;
hk_r[dn].drvtyp = 0; /* set default drive type to RK06 */
hk_ios[dn].dk_tr = DK_T; /* (iostat) xfer rate indicator */
/*
* Tell the instrumentation code in
* the clock routine, where the iostat
* structure for this controller is and
* how many drives are on this controller.
* Clock interrupts are locked out while
* iostat pointers and counts are being set up !
*/
pri = spl6();
dk_iop[DK_N] = &hk_ios[0];
dk_nd[DK_N] = NHK;
splx(pri);
hkcmd(dn, GO); /* select drive */
if(HKADDR->hkcs1 & CERR && HKADDR->hkerr & DTYE)
{
hk_r[dn].drvtyp = SEL7; /* Drive type is RK07 */
hkcmd(dn, DCLR|GO); /* clear error bits */
hkcmd(dn, GO); /* get status */
}
if((HKADDR->hkcs1 & CERR) == 0)
hk_r[dn].mntflg = 1; /* set mnt flag and return */
}
/*
*
* hkstrategy checks for overflow errors
*
*/
hkstrategy(bp)
register struct buf *bp;
{
register struct buf *dp;
register int unit;
long sz, bn;
int dn, dt;
if(bp->b_flags & B_PHYS)
mapalloc(bp); /* phys I/O. see about allocating ubmap */
unit = minor(bp->b_dev) & 077; /* unit number from buffer header */
dn = (unit >> 3) & 07; /* get drive number */
sz = bp->b_bcount; /* size from buffer header */
sz = (sz+511)>>9; /* convert to number of blocks */
/* if unit requested is greater than number available
* or
* block number is < 0
* or
* block number +size is >= number of blocks in file system
* or
* mount flag is less than 0. Drive not available or no drive
*
* ERROR
*
* dkblock is an interleaved file system routine that returns
* the actual block in the filesystem
*/
if(unit >= (NHK<<3)
|| bp->b_blkno < 0
|| (bn = dkblock(bp))+sz >= hk_sizes[unit&007].nblocks
|| hk_r[dn].mntflg < 0)
{
u.u_error |= ENXIO; /* zap the user process */
bp->b_flags |= B_ERROR; /* set ERROR flag */
iodone(bp); /* call iodone to return buffer to pool */
return; /* bye bye */
}
/*
* no error so continue
*/
bp->b_cylin = bn/(NSECT*NTRAC) + hk_sizes[unit&07].cyloff;
unit = dkunit(bp); /* get the actual drive number
could be other than specified
with an interleaved file system */
dp = &hkutab[unit]; /* get a pointer to the drives table */
spl5();
disksort(dp, bp); /* sort the drives queue in ascending
positional order and load this
buffer on the drives queue */
/* If drive is not active and no I/O pending
* then start it up.
*/
if(dp->b_active == 0 && hktab.b_active == 0)
{
hkustart(unit); /* start a drive seeking if needed */
hkstart(); /* start I/O */
}
spl0();
}
hkustart(unit)
register unit;
{
register struct buf *bp, *dp;
daddr_t bn;
int sn, tn, cn, dn;
long nblk;
dn = unit; /* get drive number */
hk_ios[dn].dk_busy = 0;
dp = &hkutab[dn]; /* grab pointer to unit table */
if(( bp = dp->b_actf) == NULL) /* nothing pending here so return */
return;
if(hk_r[dn].mntflg < 0)
{ /* drive unloaded or not mountable */
bp->b_flags |= B_ERROR; /* set error */
dp->b_actf = bp->av_forw; /* get next buffer */
iodone(bp); /* return buffer */
return; /* bye bye */
}
if(hk_r[dn].mntflg == 0)
{
hkmnt(dn); /* make sure we know drive type */
if(HKADDR->hkcs1 & CERR || (HKADDR->hkds & DRDY)== 0)
{ /* error from mount. drive not available ? */
hkerror(bp, dn, 0); /* count = 1 to save error */
bp->b_flags |= B_ERROR; /* set error */
hkerror(bp, dn, 1); /* fatal, log it */
hkcmd(dn, IEI); /* clear controller error */
hk_r[dn].mntflg = -1; /* say bad news */
hk_r[dn].ccyl = 0; /* reset these */
dp->b_actf = bp->av_forw; /* get next buffer */
iodone(bp); /* return buffer */
return; /* bye bye */
}
hkcmd(dn, (PAKACK|GO)); /* set volume valid */
hk_r[dn].ccyl = 0; /* set current cyl = 0 */
}
if(dp->b_active & HKSEEK || hktab.b_actf == NULL)
goto done;
bn = dkblock(bp); /*get block number */
cn = bp->b_cylin; /* get cylinder number */
sn = bn%(NSECT*NTRAC); /* calculate the */
tn = sn/NSECT; /* track number */
sn = sn%NSECT; /* get the sector number */
if(hk_r[dn].ccyl != cn && hktab.b_actf != NULL)
{ /* not on cylinder so seek */
dp->b_active = HKSEEK|HKBUSY; /* say drive is seeking */
HKADDR->hkdc = cn; /* desired cylinder */
HKADDR->hkda = (tn <<8)| sn; /* so no illegal addr */
HKADDR->hkcs2 = dn; /* select drive */
hk_ios[dn].dk_busy++; /* drive is active (seeking) */
HKADDR->hkcs1 = (hk_r[dn].drvtyp|IEI|SEEK|GO); /* do it */
return;
}
done:
dp->b_active = HKBUSY; /* say drive is active */
dp->b_forw = NULL; /* clear this forward link */
if(hktab.b_actf == NULL) /* if I/O table is 0 then */
hktab.b_actf = dp; /* load to I/O table (first in queue)*/
else
hktab.b_actl->b_forw = dp; /* link to end of queue */
hktab.b_actl = dp; /* new end of queue */
}
hkstart()
{
register struct buf *bp, *dp;
register int unit;
int com,cn,tn,sn,dn;
daddr_t bn;
loop:
if ((dp = hktab.b_actf) == NULL) /* get a Unit table ptr */
{
HKADDR->hkcs1 = IEI; /* nothing queued so make */
return; /* sure IEI is set and bye */
}
if(dp->b_active & HKSEEK)
{
HKADDR->hkcs1 = IEI;
return; /* drive is seeking. later */
}
if ((bp = dp->b_actf) == NULL)
{ /* no buffer for this drive */
hktab.b_actf = dp->b_forw; /* get next drive from queue*/
goto loop; /* look some more */
}
hktab.b_active |= HKBUSY; /* say that we're active */
unit = minor(bp->b_dev)&077; /* get the unit and file sys number */
dn = dkunit(bp); /* strip file sys #, get drive # */
bn = dkblock(bp); /* get the block number */
cn =(bn/(NSECT*NTRAC))+hk_sizes[unit&07].cyloff;/* calc cylinder # */
sn = bn%(NSECT*NTRAC);
tn = sn/NSECT; /* calculate track number */
sn = sn%NSECT; /* get the sector number */
#if HKEOFF
/* try offset recovery. Offset only good on read */
if((hktab.b_active & HKOFFS) == 0
&& hktab.b_errcnt >= 16
&& bp->b_flags & B_READ)
{
hktab.b_active = HKOFFS;
hkcmd(dn, GO);
HKADDR->hkas.w = hk_offset[hktab.b_errcnt &017];
HKADDR->hkcs2 = dn;
HKADDR->hkcs1 = (hk_r[dn].drvtyp|IEI|OFFSET|GO);
return;
}
hktab.b_active &= ~HKOFFS; /* clear offset flag */
#endif
HKADDR->hkdc = cn; /* load the desired cylinder */
HKADDR->hkda = (tn<<8)|sn; /* and the track , sector */
HKADDR->hkba = bp->b_un.b_addr; /* get memory address */
HKADDR->hkwc = -(bp->b_bcount>>1);/* comp and load the word count */
com = ((bp->b_xmem&3)<<8)|IEI|GO; /* ext mem, IEI, and go bits */
if(bp->b_flags & B_READ)
com |= RCOM; /* Read function */
else
com |= WCOM; /* write function */
com |= hk_r[dn].drvtyp; /* set drive type */
HKADDR->hkcs2 = dn; /* select drive number */
HKADDR->hkcs1 = com; /* and load it all into hkcs1 */
hk_ios[dn].dk_busy++; /* Instrumentation - disk active, */
hk_ios[dn].dk_numb++; /* count number of transfers, */
unit = bp->b_bcount >> 6; /* transfer size in 32 word chunks */
hk_ios[dn].dk_wds += unit; /* count total words transferred */
}
hkintr()
{
register struct buf *bp, *dp;
register int unit, ctr, dn;
int seg, uba;
int tpuis[2], tpbad[2], tppos, tpofst, bsp, pri;
if((HKADDR->hkcs1 & CRDY) == 0) /* controller not ready. I/O active */
return; /* wait for next interrupt */
if(hktab.b_active)
{
dp = hktab.b_actf; /* Doing I/O. get drive pointer */
bp = dp->b_actf; /* get buffer pointer */
dn = dkunit(bp); /* get drive number */
if(hk_r[dn].recal || hktab.b_active & HKOFFS)
{ /* recal in progress or offset active. see if done */
hkcmd(dn, GO); /* select the drive */
if((HKADDR->hkds & DRDY)==0)
{
HKADDR->hkcs1 = IEI;
return; /* recal still in prog */
}
hk_r[dn].recal = 0; /* reset flag and */
hktab.b_active &= ~HKBUSY; /* retry I/O function */
hkcmd(dn, GO); /* select the drive */
}
if(HKADDR->hkcs1 & CERR)
{
if(hktab.b_errcnt == 0)
hkerror(bp, dn, hktab.b_errcnt);
/* if exceed retry count or Non-existent mem or */
/* drive write lock error or cylinder overflow */
/* or format error or bad sector error */
/* or error during offset */
/* then FATAL error */
if(hktab.b_errcnt++ > NUMTRY
|| HKADDR->hkcs2 & NEM
|| HKADDR->hkerr & (DWLE|COE|FMTE|BSE)
|| hktab.b_active & HKOFFS)
{
hktab.b_active |= HKBUSY; /* make sure set */
bp->b_flags |= B_ERROR; /* Fatal error */
}
else if(HKADDR->hkds & DROT
|| HKADDR->hkerr & (OPI|SKI))
{ /* needs a recal */
hk_r[dn].recal++; /* set recal flag */
hkcmd(dn, (DCLR|GO)); /* clr drive */
hkcmd(dn, (IEI|RECAL|GO)); /* issue recal */
return; /* later */
}
else if((HKADDR->hkerr & (DCK|ECH))== DCK)
{
/* ECC error . recoverable */
pri = spl7(); /* need lots of priority */
hkerror(bp, dn, 0); /* make sure right registers */
tpuis[0] = UISA->r[0]; /* save User I PAR */
tpuis[1] = UISD->r[0]; /* save User I PDR */
uba = (HKADDR->hkcs1&01400)<<2; /* exmem */
uba += (((HKADDR->hkba>>1)&~0100000)>>5);
uba -= 8;
if(bp->b_flags&B_MAP)
{
ctr = (uba >> 6) & 076;
seg = UBMAP->r[ctr+1] << 10;
seg |= (UBMAP->r[ctr] >> 6) & 01777;
UISA->r[0] = seg + (uba & 0177);
} else
UISA->r[0] = uba;
UISD->r[0] = 077406; /* Set User I PDR */
tppos = (HKADDR->hkec1-1)/16; /* word addr */
tpofst = (HKADDR->hkec1-1)%16; /* bit in word */
bsp = HKADDR->hkba & 077; /* 64 byte range*/
bsp += (tppos*2);
tpbad[0] = fuiword(bsp); /* first bad word */
tpbad[1] = fuiword(bsp+2); /* second bad word */
tpbad[0] ^= (HKADDR->hkec2<<tpofst); /* xor first word */
if(tpofst > 5)
{ /* must fix second word */
ctr = (HKADDR->hkec2 >> 1) & ~0100000;
if(tpofst < 15)
ctr = ctr >> (15 - tpofst);
tpbad[1] =^ ctr; /* xor second word */
}
suiword(bsp,tpbad[0]); /* set first word */
suiword((bsp+2),tpbad[1]); /* set second word */
UISD->r[0] = tpuis[1]; /* restore User PDR */
UISA->r[0] = tpuis[0]; /* restore User PAR */
splx(pri); /* back to old pri */
printf("%D ",bp->b_blkno+(HKADDR->hkwc -(-(bp->b_bcount>>1)))/256);
prdev("ECC", bp->b_dev);
hktab.b_errcnt = 0;
/* now see if transfer finished. */
/* If not then reload xfer making sure */
/* the CCLR bit does not get set too */
if(HKADDR->hkwc != 0)
{
HKADDR->hkcs1 = CCLR;
while((HKADDR->hkcs1 & CRDY) == 0);
HKADDR->hkwc = hk_r[dn].wc;
HKADDR->hkba = hk_r[dn].ba;
HKADDR->hkda = hk_r[dn].da;
HKADDR->hkdc = hk_r[dn].dc;
HKADDR->hkcs2 = dn;
HKADDR->hkcs1=(hk_r[dn].cs1&03576)|IEI|GO;
return;
}
}
else if(HKADDR->hkerr & ECH)
{ /* soft error. retry */
hktab.b_active &= ~HKBUSY; /* retry I/O function */
hkcmd(dn , (DCLR, GO)); /* clear drive's attn */
}
else if(HKADDR->hkcs2 & (UPE|DLT)
|| HKADDR->hkerr & (HRVC|DTE)
|| hkcomer(dn, bp))
{ /* soft error. retry */
hkcmd(dn , (DCLR, GO)); /* clear drive's attn */
hktab.b_active &= ~HKBUSY; /* retry I/O function */
}
else
{
/* must be fatal error */
hktab.b_active |= HKBUSY;
bp->b_flags |= B_ERROR;
}
}
if(hktab.b_active & HKBUSY) /* need to return a buffer */
{
if(hktab.b_errcnt || bp->b_flags & B_ERROR)
hkerror(bp, dn, hktab.b_errcnt);
hktab.b_active = 0; /* no I/O active */
dp->b_active = 0; /* no drive active */
hktab.b_errcnt = 0; /* clr I/O error count */
dp->b_errcnt = 0; /* clr drive error count */
hktab.b_actf = dp->b_forw; /* get next drive link */
dp->b_actf = bp->av_forw; /* get next I/O link */
if(bp->b_flags & B_ERROR)
bp->b_resid = bp->b_bcount; /* nothing xfered */
else
bp->b_resid = 0; /* xfer complete */
iodone(bp); /* return buffer */
#if HKEOFF
if(hktab.b_errcnt >16 && bp->b_flags & B_READ)
{
hkcmd(dn, (DCLR|GO)); /* make sure attn is off */
hkcmd(dn, OFFSET|GO); /* return to zero */
}
#endif
hk_ios[dn].dk_busy = 0; /* drive no longer active */
}
hkcmd(dn, (DCLR|GO)); /* make sure attn is off */
#if HKEOFF
if(hktab.b_active & HKOFFS)
{
hkstart();
return;
}
#endif
}
hkattn(); /* go check attention lines */
} /* and start any pending I/O */
hkattn()
{
register struct buf *bp, *dp;
register int dn, as;
as = HKADDR->hkas.c[1]; /* get attn summary reg */
for( dn = 0; dn < NHK; dn++)
{
if((as & (1 << dn)) == 0)
continue; /* find attn bit(s) */
if(hk_r[dn].mntflg <= 0)
{
hkmnt(dn);
if(hk_r[dn].mntflg > 0)
hk_r[dn].mntflg = 0;
hkcmd(dn, DCLR|GO);
continue;
}
dp = &hkutab[dn]; /* get utable pointer */
bp = dp->b_actf; /* get active buffer pointer */
hkcmd(dn , GO); /* select drive */
while(HKADDR->hkds & PIP)
hkcmd(dn, GO);
if(HKADDR->hkcs1 & CERR)
{
if(dp->b_errcnt == 0)
hkerror(bp, dn, dp->b_errcnt);
if(dp->b_errcnt++ > NUMTRY
|| (hkcomer(dn, bp)) == 0)
{
hkerror(bp, dn, dp->b_errcnt);
if(dp->b_active){
dp->b_actf = bp->av_forw;/* next link*/
bp->b_flags |= B_ERROR; /* say bad */
dp->b_errcnt = 0;/* not recoverable */
iodone(bp); /* return buf */
}
}
dp->b_active = 0; /* drive not active */
hk_r[dn].ccyl = 0;
}
if((HKADDR->hkds & DRDY) == 0)
{ /* drive dropped off line */
hk_r[dn].mntflg = -1; /* not mounted */
if(dp->b_active)
{ /* something active */
dp->b_active = 0; /* say no more */
bp->b_flags |= B_ERROR; /* error */
dp->b_actf = bp->av_forw; /* get next buffer */
iodone(bp); /* return error */
}
}
dp->b_active &= ~HKBUSY; /* make sure clear */
hkcmd(dn, (DCLR|GO)); /* clear drive attn */
}
for(dn = 0; dn < NHK; dn++)
{
dp = &hkutab[dn]; /* get utable pointer */
if((dp->b_active & HKBUSY) == 0 && dp->b_actf)
hkustart(dn); /* if drive not busy and request
pending then start the drive */
}
if((hktab.b_active & HKBUSY) == 0)
hkstart(); /* If no I/O active then start it */
}
hkcmd(dn, comm)
{
int pri;
while((HKADDR->hkcs1 & CRDY) == 0); /* wait for ready */
pri = spl5(); /* no interrupts */
HKADDR->hkcs1 |= CCLR; /* clear controller */
while((HKADDR->hkcs1 & CRDY) == 0); /* wait for contr ready */
HKADDR->hkcs2 = dn; /* load drive # */
HKADDR->hkcs1 = (hk_r[dn].drvtyp|comm); /* load command */
if((comm & IEI) == 0)
while((HKADDR->hkcs1 & CRDY) == 0); /* need to wait here */
splx(pri);
}
hkcomer(dn, bp)
struct buf *bp;
int dn;
{
/** some errors are common to seek as well as i/o **/
/** this routine checks for them. **/
register struct buf *dp;
register int soft, cnt;
/* Check for Controller Type Errors first */
if(HKADDR->hkcs1 & (CTO|DTCPAR) /* Controller timeout */
|| HKADDR->hkcs2 & (UFE|PGE) /* Drive to Cont. Parity */
|| HKADDR->hkerr & CDPAR) /* Cont. to Drive Parity */
/* Unit Field Error */
/* Programming Error */
return(1); /* are all soft errors */
else if(HKADDR->hkcs2 & NED) /* Non-Exist Drive */
return(0); /* return non-recoverable */
else if(HKADDR->hkcs2 & MDS) /* Multiple Drive Select */
{ /* Have to clear the entire */
HKADDR->hkcs2 = SCLR; /* disk system to recover */
while((HKADDR->hkcs1 & CRDY) == 0)
; /* wait for controller rdy */
hktab.b_active = 0; /* no I/O active */
for(cnt = 0; cnt < NHK; cnt++)
{
dp = &hkutab[cnt]; /* no drive active */
dp->b_active = 0;
}
return(0); /* return Hard Error */
}
/* Now we can check for drive errors */
else
hkcmd(dn, GO); /* select drive */
if(HKADDR->hkds & (ACLO|SPLS) /* ACLO or Speed Loss */
|| HKADDR->hkerr & (ILF|NXF|IDAE|DUNS)) /* Illegal Function */
/* Nonexecutable Function */
/* Invalid Disk Address */
return(0); /* Drive Unsafe are fatal */
else
return(1); /* anything else is soft error?? */
}
hkerror(bp, dn, count)
struct buf *bp;
int dn, count;
{
if(count == 0)
{ /* first time through */
/* save some registers */
hk_r[dn].cs1 = HKADDR->hkcs1;
hk_r[dn].wc = HKADDR->hkwc;
hk_r[dn].ba = HKADDR->hkba;
hk_r[dn].da = HKADDR->hkda;
hk_r[dn].cs2 = HKADDR->hkcs2;
hk_r[dn].ds = HKADDR->hkds;
hk_r[dn].err = HKADDR->hkerr;
hk_r[dn].as = HKADDR->hkas.w;
hk_r[dn].dc = HKADDR->hkdc;
hk_r[dn].mr1 = HKADDR->hkmr1;
hk_r[dn].mr2 = HKADDR->hkmr2;
hk_r[dn].mr3 = HKADDR->hkmr3;
}
else
#ifndef REGDUMP
deverror(bp, hk_r[dn].cs2, hk_r[dn].err);
#else
{
if(bp->b_flags & B_ERROR || count >= NUMTRY)
printf("\nfatal ");
else
printf("\nrecoverable ");
if(bp)
prdev("error", bp->b_dev);
printf("Error Count = %d\n", count);
printf("cs1 = %o\t wc = %o\t ba = %o\n",hk_r[dn].cs1,hk_r[dn].wc,hk_r[dn].ba);
printf(" da = %o\t cs2 = %o\t ds = %o\n",hk_r[dn].da,hk_r[dn].cs2,hk_r[dn].ds);
printf("err = %o\t off = %o\t dc = %o\n",hk_r[dn].err,hk_r[dn].as,hk_r[dn].dc);
printf("mr1 = %o\t mr2 = %o\t mr3 = %o\n",hk_r[dn].mr1,hk_r[dn].mr2,hk_r[dn].mr3);
}
#endif
}
hkread(dev)
dev_t dev;
{
physio(hkstrategy, &rhkbuf, dev, B_READ);
}
hkwrite(dev)
dev_t dev;
{
physio(hkstrategy, &rhkbuf, dev, B_WRITE);
}