# /* * RWP06 driver * modified by A. G. Nemeth - BBN - 18 May 1978 * ECC additions by A. G. Nemeth - BBN - 3 January 1979 */ #include "../h/param.h" #include "../h/buf.h" #include "../h/conf.h" #include "../h/systm.h" #include "../h/user.h" #include "../h/reg.h" struct { int hpcs1; /* Control and Status register 1 */ int hpwc; /* Word count register */ int hpba; /* UNIBUS address register */ int hpda; /* Desired address register */ int hpcs2; /* Control and Status register 2 */ int hpds; /* Drive Status */ int hper1; /* Error register 1 */ int hpas; /* Attention Summary */ int hpla; /* Look ahead */ int hpdb; /* Data buffer */ int hpmr; /* Maintenance register */ int hpdt; /* Drive type */ int hpsn; /* Serial number */ int hpof; /* Offset register */ int hpdc; /* Desired Cylinder address register */ int hpcc; /* Current Cylinder */ int hper2; /* Error register 2 */ int hper3; /* Error register 3 */ int hpec1; /* Burst error bit position */ int hpec2; /* Burst error bit pattern */ int hpbae; /* Bus address extension */ int hpcs3; /* Control and status register 3 */ }; #define HPADDR 0176700 #define NHP 2 /* number of HP drives */ #define NMDLOG2 4 /* number of meta disks log 2 */ #define MASK ((1<<NMDLOG2)-1) /* mask for meta disk ID */ #define SECSZ 256 struct { char *nblocks; int cyloff; } hp_sizes[] { 9614, 0, /* 0 - hpsx8 */ 9614, 23, /* 1 - hpsx7 */ 9614, 46, /* 2 - hpsx6 */ 9614, 69, /* 3 - hpsx5 */ 63536, 92, /* 4 - hplx2 */ 9614, 244, /* 5 - hpsx4 */ 9614, 267, /* 6 - hpsx3 */ 9614, 290, /* 7 - hpsx2 */ 9614, 313, /* 8 - hpsx1 */ 9614, 336, /* 9 - hpsx0 */ 63536, 359, /* 10 - hplx0 */ 63536, 511, /* 11 - hplx1 */ 63536, 663, /* 12 - hplx3 */ }; struct devtab hptab; struct devtab hputab[NHP]; struct buf hpbuf; char hpstatus[NHP]; int hpsioc[NHP]; int hpsios[NHP]; int ghper1, ghper2, ghper3, ghpds, ghpcs2; /* Drive Commands */ #define GO 01 #define UNLOAD 02 #define SEEK 04 #define DCLR 010 #define RELEASE 012 #define OFFSET 014 #define PRESET 020 #define SEARCH 030 #define READ 070 #define ERR 040000 /* hpds - Error */ #define MOL 010000 /* hpds - Medium online */ #define DRY 0200 /* hpds - drive ready */ #define VV 0100 /* hpds - volume valid */ #define SC 0100000 /* hpcs1 - Special condition */ #define TRE 040000 /* hpcs1 - transfer error */ #define DVA 04000 /* hpcs1 - drive available */ #define IE 0100 /* hpcs1 - interrupt enable */ #define RDY 0200 /* hpcs1 - controller ready */ #define NED 010000 /* hpcs2 - Nonexistent drive */ #define WLE 04000 /* hper1 - Write lock error */ #define DCK 0100000 /* hper1 - data check error */ #define ECH 0100 /* hper1 - ECC hard error */ #define FMT22 010000 /* hpof - 16 bit /word format */ #define ECI 04000 /* hpof - ecc inhibit */ /* * Use av_back to save track+sector, * b_resid for cylinder. */ #define trksec av_back #define cylin b_resid hpstrategy (abp) struct buf *abp; { register struct buf *bp; register char *p1, *p2; struct devtab *dp; int unit; bp = abp; p1 = &hp_sizes[bp -> b_dev.d_minor & MASK]; unit = bp -> b_dev.d_minor >> NMDLOG2; if (unit >= NHP || hpstatus[unit] || bp -> b_blkno >= p1 -> nblocks) { bp -> b_flags =| B_ERROR; iodone (bp); return; } bp -> av_forw = 0; bp -> cylin = p1 -> cyloff; p1 = bp -> b_blkno; p2 = lrem (p1, 22); p1 = ldiv (p1, 22); bp -> trksec = (p1 % 19) << 8 | p2; bp -> cylin =+ p1 / 19; spl5 (); dp = &hputab[unit]; hpsioc[unit]++; if ((p1 = dp -> d_actf) == 0) dp -> d_actf = bp; else { for (; p2 = p1 -> av_forw; p1 = p2) { if (p1 -> cylin <= bp -> cylin && bp -> cylin < p2 -> cylin || p1 -> cylin >= bp -> cylin && bp -> cylin > p2 -> cylin) break; } bp -> av_forw = p2; p1 -> av_forw = bp; } if (dp -> d_active == 0) hpustart (unit); spl0 (); } hpustart (dev) { register struct buf *bp; register struct devtab *dp; register unit; int search; int i; unit = dev; dp = &hputab[unit]; HPADDR -> hpcs2.lobyte = unit; HPADDR -> hpas = 1 << unit; /* hpstatus[unit] = 0 was here, moved lower */ if ((HPADDR -> hpcs1 & DVA) == 0) { /* either NED or dual-port not avail */ printf ("NED "); goto abort; } if ((HPADDR -> hpds & MOL) == 0) { if (hpstatus[unit] == 0) /* check added to prevent */ printf ("MOL "); /* multiple printfs: 9/25/77:mob */ goto abort; } /* check on dp->d_actf was here: moved for error handling 9/23/77:mob */ hpstatus[unit] = 0; /* moved from above 9/25/77:mob */ if (HPADDR -> hpds & ERR) { printf ("SE %d %o\n", unit, HPADDR -> hper1); HPADDR -> hpcs1.lobyte = IE | DCLR | GO; if (++dp -> d_errcnt > 16) { printf ("Unit %d unloaded\n", unit); HPADDR -> hpcs1.lobyte = IE | UNLOAD | GO; goto abort; } if (dp -> d_actf) /* error handling 9/23/77:mob */ dp -> d_active--; } if ((HPADDR -> hpds & VV) == 0) { HPADDR -> hpcs1.lobyte = IE | PRESET | GO; HPADDR -> hpof = FMT22; } if ((bp = dp -> d_actf) == 0) /* error handling (moved) 9/23/77:mob */ return; /* " */ dp -> d_active++; i = bp -> cylin - HPADDR -> hpcc; /* systm[11] =+ (i > 0 ? i : -i); /* monitoring 9/22/77:rsk */ HPADDR -> hpdc = bp -> cylin; search = bp -> trksec.lobyte - (HPADDR -> hpla >> 6) - 1; if (search < 0) search =+ 22; if ((bp -> cylin != HPADDR -> hpcc || search > 6) && dp -> d_active < 3) { search = bp -> trksec; search.lobyte =- 4; if (search.lobyte < 0) search.lobyte =+ 22; hpsios[unit]++; HPADDR -> hpda = search; HPADDR -> hpcs1.lobyte = IE | SEARCH | GO; } else { dp -> b_forw = 0; if (hptab.d_actf == 0) hptab.d_actf = dp; else hptab.d_actl -> b_forw = dp; hptab.d_actl = dp; if (hptab.d_active == 0) hpstart (0); } return; abort: if (hpstatus[unit] == 0) { /* check must be here since we want, * in all, cases, to free pending blocks */ printf ("er1 %o, er2 %o, er3 %o, ds %o, cs1 %o, cs2 %o\n", HPADDR -> hper1, HPADDR -> hper2, HPADDR -> hper3, HPADDR -> hpds, HPADDR -> hpcs1, HPADDR -> hpcs2); /* helpful debugging info: 9/21/77:mob */ HPADDR -> hpcs1.lobyte = IE | DCLR | GO; /* error handling: 9/23/77:mob */ } hpstatus[unit]++; while (bp = dp -> d_actf) { bp -> b_flags =| B_ERROR; dp -> d_actf = bp -> av_forw; iodone (bp); } dp -> d_active = 0; dp -> d_errcnt = 0; HPADDR -> hpcs1 = IE | DCLR | GO; printf ("RP06 drive %d offline\n", unit); } hpstart (off) int off; { register struct buf *bp; register struct devtab *dp; if ((dp = hptab.d_actf) == 0) return; bp = dp -> d_actf; HPADDR -> hpcs2.lobyte = bp -> b_dev.d_minor >> NMDLOG2; hptab.d_active++; HPADDR -> hpdc = bp -> cylin; rhstart (bp, &HPADDR -> hpda, bp -> trksec, &HPADDR -> hpbae, off); } char *oldbk; int lecc; int off { 0 }; hpintr () { struct { int high; int low;}; long secloc,mask; int wd,ct,errpos; register struct buf *bp; register struct devtab *dp; register unit; if (hptab.d_active) { /* data transfer underway */ off = 0; dp = hptab.d_actf; bp = dp -> d_actf; unit = bp -> b_dev.d_minor >> NMDLOG2; HPADDR -> hpcs2.lobyte = unit; if (HPADDR -> hpcs1 & TRE) { /* error bit */ oldbk = bp -> b_blkno; ghper1 = HPADDR -> hper1; ghper2 = HPADDR -> hper2; ghper3 = HPADDR -> hper3; ghpds = HPADDR -> hpds; ghpcs2 = HPADDR -> hpcs2; /* * use ecc only if not hard error (rsk:12/20/77) */ /**/ printf ("** ERR%o %o **\n", ghper1, ghper2); if ((!(ghper1 & ECH)) && (ghper1 & DCK)) { /**/ printf ("Entered ECC "); /**/ printf ("EA%o BA%o WL%d WT%d POS%d MSK%o\n", HPADDR -> hpbae, HPADDR -> hpba, /**/ HPADDR -> hpwc, -bp -> b_wcount, HPADDR -> hpec1, HPADDR -> hpec2); ct = HPADDR->hpwc; ct =- bp->b_wcount; /* number of words xfered */ errpos = HPADDR->hpec1; /* position of error */ if (errpos-- == 0) { printf ("ECC position zero\n"); goto notecc; } secloc.high = bp->b_xmem; secloc.low = bp->b_addr; wd = 2*((ct & (~ (SECSZ-1)))+errpos/16); /* convenient temp */ secloc =+ wd; /* start of sector + word of error */ ct = ct%SECSZ; /* amount of last sector xfer'ed */ ct = (ct == 0)? SECSZ:ct; mask.high = 0; mask.low = HPADDR->hpec2; mask =<< (errpos&017); if (errpos/16 < ct) pphys(secloc,mask.low^gphys(secloc)); if ((errpos+11)/16 < ct) { secloc++; pphys(secloc,mask.high^gphys(secloc)); } if (HPADDR->hpwc != 0) /*more to xfer ? */ { HPADDR->hpcs1 = IE|READ|GO; /* just continue on to read more */ return; } } notecc: HPADDR -> hpcs1 = TRE | IE | DCLR | GO; if (++hptab.d_errcnt > 16 || ghper1 & WLE) bp -> b_flags =| B_ERROR; else hptab.d_active = 0; } /* * check not only for data transfer but also be sure this is * really the interrupt for it and not some seek interrupt. */ if (hptab.d_active && (HPADDR -> hpcs1 & RDY)) { /* mob:10/07/77 */ hptab.d_active = 0; hptab.d_errcnt = 0; hptab.d_actf = dp -> b_forw; dp -> d_active = 0; dp -> d_errcnt = 0; dp -> d_actf = bp -> av_forw; bp -> b_resid = HPADDR -> hpwc; iodone (bp); HPADDR -> hpcs1 = IE | RELEASE | GO; hpustart (unit); } if (hptab.d_active == 0) hpstart (); /* start I/O */ } else HPADDR -> hpcs1.hibyte = TRE >> 8; for (unit = 0; unit < NHP; unit++) { if (HPADDR -> hpas & (1 << unit)) hpustart (unit); } if ((HPADDR -> hpcs1 & IE) == 0) { HPADDR -> hpcs1 = IE; if (HPADDR -> hpcs1 & TRE) HPADDR -> hpcs1.hibyte = TRE >> 8; } } hpread (dev) { if (hpphys (dev)) physio (hpstrategy, &hpbuf, dev, B_READ); } hpwrite (dev) { if (hpphys (dev)) physio (hpstrategy, &hpbuf, dev, B_WRITE); } hpphys (dev) { register c; c = lshift (u.u_offset, -9); c =+ ldiv (u.u_count + 511, 512); if (c > hp_sizes[dev.d_minor & MASK].nblocks) { u.u_error = ENXIO; return (0); } return (1); }