V7M/sys/dev/hm.c
/*
* UNIX/v7m - RM02/3, RP04/5/6, & ML11 disk driver.
* Disks on second RH controller (176300)
* Fred Canter 9/4/81
*
* This is a combination driver, it supports any mix
* of RM02/3, RP04/5/6, & ML11 disks, up to eight
* drives on the same controller.
* If support for the ML11 drive type is not
* required, it may be removed by redefining the control
* parameter for the ML11 (ML) to zero.
*/
#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"
/*
* The following parameter controls the inclusion
* of code to support the ML11 disk drive.
*/
#define ML 1 /* ML11 */
struct device
{
union {
int w;
char c[2];
} hmcs1; /* Control and Status register 1 */
int hmwc; /* Word count register */
caddr_t hmba; /* UNIBUS address register */
int hmda; /* Desired address register */
union {
int w;
char c[2];
} hmcs2; /* Control and Status register 2 */
int hmds; /* Drive Status */
int hmer1; /* Error register 1 */
/* mler */ /* Error register */
int hmas; /* Attention Summary */
int hmla; /* Look ahead */
/* mlpa */ /* Prom address register */
int hmdb; /* Data buffer */
int hmmr; /* Maintenance register */
/* rmmr1 */ /* Maintenance register 1 */
int hmdt; /* Drive type */
int hmsn; /* Serial number */
int hmof; /* Offset register */
/* mle1 */ /* ECC CRC word 1 */
int hmdc; /* Desired Cylinder address register */
/* mle2 */ /* ECC CRC word 2 */
int hmcc; /* Current Cylinder */
/* mld1 */ /* Data diagnostic 1 register */
/* rmhr */ /* Holding register */
int hmer2; /* Error register 2 */
/* mld2 */ /* Data diagnostic 2 register */
/* rmmr2 */ /* Maintenance register 2 */
int hmer3; /* Error register 3 */
/* mlee */ /* ECC error register */
/* rmer2 */ /* Error register 2 */
int hmec1; /* Burst error bit position */
/* mlel */ /* ECC error location register */
int hmec2; /* Burst error bit pattern */
/* mlpd */ /* Prom data register */
int hmbae; /* 11/70 bus extension */
int hmcs3; /* 11/70 control & status 3 */
};
#define HMADDR ((struct device *)0176300)
#define NUNIT 2
/*
* Instrumentation (iostat) structures
*/
struct ios hm_ios[NUNIT];
#define DK_N 1
/*
* Drive type definitions
*/
#define RP04 020
#define RP05 021
#define RP06 022
#define RM03 024
#define RM02 025
#define ML11 0110
/*
* Disk geometry definitions
*/
#define NRMSEC 32
#define NRMTRK 5
#define NRPSEC 22
#define NRPTRK 19
#define SDIST 2
#define RDIST 6
/*
* The number of elements in the hm_sizes[]
* array has been changed from 8 to 24.
* The first 8 elements describe the disk partitions
* for the RP04/5/6 and the second 8 elements
* specify the RM02/3 disk partitions,
* the last 8 elements are the size of each ML11
* unit in blocks.
* The size of the ML11 unit is determined by reading
* the ML maintenance register the first time the drive
* is accessed.
* The last cylinder of the disk pack is not used,
* so that a bad block replacement strategy may be
* implemented later.
*/
struct size
{
daddr_t nblocks;
int cyloff;
} hm_sizes[24] =
{
9614, 0, /* cyl 0- 22, root file system on rp04/5/6 */
8778, 23, /* cyl 23- 43, swap area on rp04/5/6 */
8778, 44, /* cyl 44- 64, /sys (system sources) rp04/5/6 */
144210, 65, /* cyl 65-409, /usr (user + sources) rp04/5 */
313082, 65, /* cyl 65-813, /usr (user + sources) rp06 */
168454, 411, /* cyl 411-813, user file system on rp06 */
171380, 0, /* cyl 0-409, user - all of rp04/5 pack */
340252, 0, /* cyl 0-813, user - all of rp06 pack */
9600, 0, /* cyl 0- 59, root file system on rm02/3 */
8800, 60, /* cyl 60-114, swap area on rm02/3 */
8000, 115, /* cyl 115-164, /sys (system sources) rm02/3 */
105120, 165, /* cyl 165-821, /usr (user + sources) rm02/3 */
0, 0, /* spare for avoiding bad blocks */
0, 0, /* spare for avoiding bad blocks */
0, 0, /* spare for avoiding bad blocks */
131520, 0, /* cyl 0-821, user - all of rm02/3 pack */
0, 0, /* ML11 unit 0 */
0, 0, /* 1 */
0, 0, /* 2 */
0, 0, /* 3 */
0, 0, /* 4 */
0, 0, /* 5 */
0, 0, /* 6 */
0, 0, /* 7 */
};
#define P400 020
#define M400 0220
#define P800 040
#define M800 0240
#define P1200 060
#define M1200 0260
int hm_offset[16] =
{
P400, M400, P400, M400,
P800, M800, P800, M800,
P1200, M1200, P1200, M1200,
0, 0, 0, 0,
};
struct buf hmtab;
struct buf rhmbuf;
struct buf hmutab[NUNIT];
char hm_dt[NUNIT]; /* drive type */
#define GO 01
#define PRESET 020
#define RTC 016
#define OFFSET 014
#define SEARCH 030
#define RECAL 06
#define DCLR 010
#define WCOM 060
#define RCOM 070
#define IE 0100
#define PIP 020000
#define DRY 0200
#define ERR 040000
#define TRE 040000
#define DCK 0100000
#define WLE 04000
#define ECH 0100
#define VV 0100
#define DPR 0400
#define MOL 010000
#define FMT22 010000
#define b_cylin b_resid
daddr_t dkblock();
hmstrategy(bp)
register struct buf *bp;
{
register struct buf *dp;
register unit;
long sz, bn;
int nbpc, dn, dt, fs, pri;
if((bp->b_flags&B_PHYS) && !rh70hm)
mapalloc(bp);
unit = minor(bp->b_dev) & 077;
dn = (unit >> 3) & 07; /* get drive number */
if(hm_dt[dn] == 0) { /* get drive type, if not known */
spl5();
HMADDR->hmcs2.c[0] = dn;
hm_dt[dn] = HMADDR->hmdt & 0377;
#if ML
/*
* Find the size of the ML11 unit by reading the
* number of array modules from the ML maintenance register.
* There are 512 blocks per array unless the array type
* bit is set, in which case there are 2048 blocks per array.
*/
if(hm_dt[dn] == ML11) {
hm_sizes[dn+16].nblocks = (HMADDR->hmmr >> 2) & 037000;
if(HMADDR->hmmr & 02000)
hm_sizes[dn+16].nblocks <<= 2;
/*
* Check the ML11 transfer rate.
* 2mb is too fast for any pdp11
* 1mb allowed on pdp11/70 only
* .5mb & .25mb ok on any processor.
*/
fs = HMADDR->hmmr & 01400;
if((fs == 0) || ((fs == 0400) && !rh70hm)) {
printf("\nML11 xfer rate error\n");
goto bad;
}
hm_ios[dn].dk_tr = ((fs >> 8) & 3) + 4;
}
spl0();
#endif
if(hm_dt[dn] != ML11) {
hm_ios[dn].dk_tr = 1;
if(hm_dt[dn] == RM03)
hm_ios[dn].dk_tr = 2;
}
pri = spl6();
dk_iop[DK_N] = &hm_ios[0];
dk_nd[DK_N] = NUNIT;
splx(pri);
}
/*
* Set up the index into the sizes table (dt)
* and the number of blocks per cylinder (nbpc).
*/
#if ML
if(hm_dt[dn] == ML11)
dt = 16;
else
#endif
if((hm_dt[dn] == RM02) || (hm_dt[dn] == RM03)) {
nbpc = NRMSEC*NRMTRK;
dt = 8;
}
else
if(hm_dt[dn] <= RP06) {
nbpc = NRPSEC*NRPTRK;
dt = 0;
}
else
goto bad;
sz = bp->b_bcount;
sz = (sz+511) >> 9;
fs = unit & 07; /* For RP & RM, fs is partition */
if(dt == 16) /* For ML, fs is unit number */
fs = dn;
#if ML
if((hm_dt[dn] == ML11) && (unit&7))
goto bad; /* ML11 is not partitioned */
#endif
if (unit >= (NUNIT<<3) ||
bp->b_blkno < 0 ||
(bn = dkblock(bp))+sz > hm_sizes[fs+dt].nblocks) {
bad:
u.u_error = ENXIO;
bp->b_flags |= B_ERROR;
iodone(bp);
return;
}
#if ML
if(dt == 16)
bp->b_cylin = 0; /* fake out dsort for ML11 */
else
#endif
bp->b_cylin = bn/nbpc + hm_sizes[fs+dt].cyloff;
unit = dkunit(bp);
dp = &hmutab[unit];
spl5();
disksort(dp, bp);
if (dp->b_active == 0) {
hmustart(unit);
if(hmtab.b_active == 0)
hmstart();
}
spl0();
}
hmustart(unit)
register unit;
{
register struct buf *bp, *dp;
daddr_t bn;
int sn, cn, csn;
int nsect, ntrac;
HMADDR->hmcs2.w = unit;
HMADDR->hmcs1.c[0] = IE;
HMADDR->hmas = 1<<unit;
if(unit >= NUNIT)
return;
if(hm_dt[unit] <= RP06) {
nsect = NRPSEC;
ntrac = NRPTRK;
} else {
nsect = NRMTRK;
ntrac = NRMTRK;
}
hm_ios[unit].dk_busy = 0;
dp = &hmutab[unit];
if((bp=dp->b_actf) == NULL)
return;
if((HMADDR->hmds & VV) == 0) {
HMADDR->hmcs1.c[0] = IE|PRESET|GO;
#if ML
if(hm_dt[unit] != ML11)
#endif
HMADDR->hmof = FMT22;
}
if(dp->b_active)
goto done;
#if ML
if(hm_dt[unit] == ML11) {
sn = dkblock(bp);
dp->b_active++;
goto mlsrch;
}
#endif
dp->b_active++;
if ((HMADDR->hmds & (DPR|MOL)) != (DPR|MOL))
goto done;
bn = dkblock(bp);
cn = bp->b_cylin;
sn = bn%(nsect*ntrac);
sn = (sn+nsect-SDIST)%nsect;
if(HMADDR->hmcc != cn)
goto search;
csn = (HMADDR->hmla>>6) - sn + SDIST - 1;
if(csn < 0)
csn += nsect;
if(csn > nsect-RDIST)
goto done;
search:
HMADDR->hmdc = cn;
mlsrch:
HMADDR->hmda = sn;
hm_ios[unit].dk_busy++;
HMADDR->hmcs1.c[0] = IE|SEARCH|GO;
return;
done:
dp->b_forw = NULL;
if(hmtab.b_actf == NULL)
hmtab.b_actf = dp; else
hmtab.b_actl->b_forw = dp;
hmtab.b_actl = dp;
}
hmstart()
{
register struct buf *bp, *dp;
register unit;
daddr_t bn;
int dn, sn, tn, cn, dt;
int nsect, ntrac;
loop:
if ((dp = hmtab.b_actf) == NULL)
return;
if ((bp = dp->b_actf) == NULL) {
hmtab.b_actf = dp->b_forw;
goto loop;
}
hmtab.b_active++;
unit = minor(bp->b_dev) & 077;
dn = dkunit(bp);
spl5();
HMADDR->hmcs2.w = dn;
if(hm_dt[dn] <= RP06) {
dt = 0;
nsect = NRPSEC;
ntrac = NRPTRK;
} else {
dt = 8;
nsect = NRMSEC;
ntrac = NRMTRK;
}
bn = dkblock(bp);
if(hm_dt[dn] != ML11) {
cn = bn/(nsect*ntrac) + hm_sizes[(unit&07)+dt].cyloff;
sn = bn%(nsect*ntrac);
tn = sn/nsect;
sn = sn%nsect;
}
if ((HMADDR->hmds & (DPR|MOL)) != (DPR|MOL)) {
hmtab.b_active = 0;
hmtab.b_errcnt = 0;
dp->b_actf = bp->av_forw;
bp->b_flags |= B_ERROR;
iodone(bp);
spl0();
goto loop;
}
if((hmtab.b_errcnt >= 16) && (hm_dt[dn] != ML11)) {
HMADDR->hmof = hm_offset[hmtab.b_errcnt & 017] | FMT22;
HMADDR->hmcs1.w = OFFSET|GO;
while(HMADDR->hmds & PIP)
;
}
if(hm_dt[dn] == ML11)
HMADDR->hmda = bn;
else {
HMADDR->hmdc = cn;
HMADDR->hmda = (tn << 8) + sn;
}
HMADDR->hmba = bp->b_un.b_addr;
if(rh70hm)
HMADDR->hmbae = bp->b_xmem;
HMADDR->hmwc = -(bp->b_bcount>>1);
unit = ((bp->b_xmem&3) << 8) | IE | GO;
if(bp->b_flags & B_READ)
unit |= RCOM; else
unit |= WCOM;
HMADDR->hmcs1.w = unit;
hm_ios[dn].dk_busy++; /* Instrumentation - disk active, */
hm_ios[dn].dk_numb++; /* count number of transfers, */
unit = bp->b_bcount >> 6; /* transfer size in 32 word chunks, */
hm_ios[dn].dk_wds += unit; /* count total words transferred */
spl0();
}
hmintr()
{
register struct buf *bp, *dp;
register unit;
register seg, uba;
int as;
int tpuis[2], tpbad[2], tppos, tpofst;
int bsp;
int pri, ctr, dt
as = HMADDR->hmas & 0377;
if(hmtab.b_active) {
dp = hmtab.b_actf;
bp = dp->b_actf;
unit = dkunit(bp);
HMADDR->hmcs2.c[0] = unit;
hm_ios[unit].dk_busy = 0;
if (HMADDR->hmcs1.w & TRE) { /* error bit */
while((HMADDR->hmds & DRY) == 0)
;
#if ML
if((hm_dt[unit] == ML11) && (hmtab.b_errcnt == 0))
hmtab.b_errcnt = 18;
#endif
if(++hmtab.b_errcnt > 28 || HMADDR->hmer1&WLE)
bp->b_flags |= B_ERROR; else
hmtab.b_active = 0;
if(hmtab.b_errcnt > 27)
deverror(bp, HMADDR->hmcs2.w, HMADDR->hmer1);
/* Start of ECC code, please excuse the improper indentation */
if(((HMADDR->hmer1&(DCK|ECH)) == DCK) && (hm_dt[unit] != ML11))
{
pri = spl7();
tpuis[0] = UISA->r[0]; /* save User I space PAR */
tpuis[1] = UISD->r[0]; /* save User I space PDR */
if(rh70hm)
uba = (HMADDR->hmbae << 10) & 0176000;
else
uba = (HMADDR->hmcs1.w & 01400) << 2;
uba += (((HMADDR->hmba >> 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 space PDR */
tppos = (HMADDR->hmec1 - 1)/16; /* word addr of error burst */
tpofst = (HMADDR->hmec1 - 1)%16; /* bit in word */
bsp = HMADDR->hmba & 077;
bsp += (tppos*2);
tpbad[0] = fuiword(bsp);
tpbad[1] = fuiword(bsp+2);
tpbad[0] ^= (HMADDR->hmec2 << tpofst); /* xor first word */
if(tpofst > 5) { /* must fix second word */
ctr = (HMADDR->hmec2 >> 1) & ~0100000;
if(tpofst < 15)
ctr = ctr >> (15 - tpofst);
tpbad[1] ^= ctr; /* xor second word */
}
suiword(bsp,tpbad[0]);
suiword((bsp+2),tpbad[1]);
UISD->r[0] = tpuis[1]; /* restore User PDR */
UISA->r[0] = tpuis[0]; /* restore User PAR */
splx(pri);
/* now see if transfer finished. */
/* If not then reset;track,sector,etc. */
if(HMADDR->hmwc != 0)
{
tpbad[0] = bp->b_bcount >> 1;
tpbad[1] = -(HMADDR->hmwc);
bp->b_blkno =+ (tpbad[0] - tpbad[1]) / 256;
bp->b_bcount = -(HMADDR->hmwc) << 1;
bp->b_un.b_addr = HMADDR->hmba;
if(rh70hm)
bp->b_xmem = HMADDR->hmbae & 077;
else
bp->b_xmem = (HMADDR->hmcs1.w & 01400) >> 8;
printf("%D ", (bp->b_blkno - 1));
prdev("ECC", bp->b_dev);
HMADDR->hmcs1.w = TRE|DCLR|GO;
hmstart(); /* Continue transfer */
return;
}
hmtab.b_active++;
printf("%D ", bp->b_blkno);
prdev("ECC", bp->b_dev);
}
/* End of ECC code */
HMADDR->hmcs1.w = TRE|IE|DCLR|GO;
if(((hmtab.b_errcnt&07) == 4) && (hm_dt[unit] != ML11)) {
HMADDR->hmcs1.w = RECAL|IE|GO;
while(HMADDR->hmds & PIP)
;
}
}
if(hmtab.b_active) {
if((hmtab.b_errcnt) && (hm_dt[unit] != ML11)) {
HMADDR->hmcs1.w = RTC|GO;
while(HMADDR->hmds & PIP)
;
}
hmtab.b_active = 0;
hmtab.b_errcnt = 0;
hmtab.b_actf = dp->b_forw;
dp->b_active = 0;
dp->b_errcnt = 0;
dp->b_actf = bp->av_forw;
bp->b_resid = -(HMADDR->hmwc<<1);
iodone(bp);
HMADDR->hmcs1.w = IE;
if(dp->b_actf)
hmustart(unit);
}
as &= ~(1<<unit);
} else {
if(as == 0)
HMADDR->hmcs1.w = IE;
HMADDR->hmcs1.c[1] = TRE>>8;
}
for(unit=0; unit<NUNIT; unit++)
if(as & (1<<unit))
hmustart(unit);
hmstart();
}
hmread(dev)
{
physio(hmstrategy, &rhmbuf, dev, B_READ);
}
hmwrite(dev)
{
physio(hmstrategy, &rhmbuf, dev, B_WRITE);
}