1: /* 2: * SCCS id @(#)dvhp.c 2.1 (Berkeley) 9/1/83 3: */ 4: 5: /* 6: * Disk driver for Diva Comp V controller. 7: */ 8: 9: #include "dvhp.h" 10: #if NDVHP > 0 11: #include "param.h" 12: #include <sys/systm.h> 13: #include <sys/buf.h> 14: #include <sys/conf.h> 15: #include <sys/dir.h> 16: #include <sys/user.h> 17: #include <sys/hpreg.h> 18: #ifndef INTRLVE 19: #include <sys/inline.h> 20: #endif 21: #include <sys/uba.h> 22: 23: #define DVHP_NSECT 33 24: #define DVHP_NTRAC 19 25: #define DVHP_SDIST 2 26: #define DVHP_RDIST 6 27: 28: extern struct size dvhp_sizes[]; 29: extern struct hpdevice *DVHPADDR; 30: 31: int dvhp_offset[] = 32: { 33: HPOF_P400, HPOF_M400, HPOF_P400, HPOF_M400, 34: HPOF_P800, HPOF_M800, HPOF_P800, HPOF_M800, 35: HPOF_P1200, HPOF_M1200, HPOF_P1200, HPOF_M1200, 36: 0, 0, 0, 0 37: }; 38: 39: struct buf dvhptab; 40: #ifdef UCB_DBUFS 41: struct buf rdvhpbuf[NHP]; 42: #else 43: struct buf rdvhpbuf; 44: #endif 45: struct buf dvhputab[NDVHP]; 46: 47: #ifdef INTRLVE 48: extern daddr_t dkblock(); 49: #endif 50: 51: void 52: dvhproot() 53: { 54: dvhpattach(DVHPADDR, 0); 55: } 56: 57: dvhpattach(addr, unit) 58: register struct hpdevice *addr; 59: { 60: if (unit != 0) 61: return(0); 62: if ((addr != (struct hpdevice *) NULL) && (fioword(addr) != -1)) { 63: DVHPADDR = addr; 64: #if PDP11 == 70 || PDP11 == GENERIC 65: if (fioword(&(addr->hpbae)) != -1) 66: dvhptab.b_flags |= B_RH70; 67: #endif 68: return(1); 69: } 70: DVHPADDR = (struct hpdevice *) NULL; 71: return(0); 72: } 73: 74: dvhpstrategy(bp) 75: register struct buf *bp; 76: { 77: register struct buf *dp; 78: register unit; 79: long bn; 80: 81: unit = minor(bp->b_dev) & 077; 82: if (unit >= (NDVHP << 3) || (DVHPADDR == (struct hpdevice *) NULL)) { 83: bp->b_error = ENXIO; 84: goto errexit; 85: } 86: if (bp->b_blkno < 0 || 87: (bn = dkblock(bp)) + (long) ((bp->b_bcount + 511) >> 9) 88: > dvhp_sizes[unit & 07].nblocks) { 89: bp->b_error = EINVAL; 90: errexit: 91: bp->b_flags |= B_ERROR; 92: iodone(bp); 93: return; 94: } 95: #ifdef UNIBUS_MAP 96: if ((dvhptab.b_flags & B_RH70) == 0) 97: mapalloc(bp); 98: #endif 99: bp->b_cylin = bn / (DVHP_NSECT * DVHP_NTRAC) + dvhp_sizes[unit & 07].cyloff; 100: unit = dkunit(bp); 101: dp = &dvhputab[unit]; 102: (void) _spl5(); 103: disksort(dp, bp); 104: if (dp->b_active == 0) { 105: dvhpustart(unit); 106: if (dvhptab.b_active == 0) 107: dvhpstart(); 108: } 109: (void) _spl0(); 110: } 111: 112: /* 113: * Unit start routine. 114: * Seek the drive to where the data are 115: * and then generate another interrupt 116: * to actually start the transfer. 117: * If there is only one drive on the controller 118: * or we are very close to the data, don't 119: * bother with the search. If called after 120: * searching once, don't bother to look 121: * where we are, just queue for transfer (to avoid 122: * positioning forever without transferring). 123: */ 124: dvhpustart(unit) 125: register unit; 126: { 127: register struct hpdevice *dvhpaddr = DVHPADDR; 128: register struct buf *dp; 129: struct buf *bp; 130: daddr_t bn; 131: int sn, cn, csn; 132: 133: dvhpaddr->hpcs2.w = unit; 134: dvhpaddr->hpcs1.c[0] = HP_IE; 135: dvhpaddr->hpas = 1 << unit; 136: 137: if (unit >= NDVHP) 138: return; 139: #ifdef DVHP_DKN 140: dk_busy &= ~(1 << (unit + DVHP_DKN)); 141: #endif 142: dp = &dvhputab[unit]; 143: if ((bp=dp->b_actf) == NULL) 144: return; 145: /* 146: * If we have already positioned this drive, 147: * then just put it on the ready queue. 148: */ 149: if (dp->b_active) 150: goto done; 151: dp->b_active++; 152: /* 153: * If drive has just come up, 154: * set up the pack. 155: */ 156: if ((dvhpaddr->hpds & HPDS_VV) == 0) { 157: /* SHOULD WARN SYSTEM THAT THIS HAPPENED */ 158: dvhpaddr->hpcs1.c[0] = HP_IE | HP_PRESET | HP_GO; 159: dvhpaddr->hpof = HPOF_FMT22; 160: } 161: #if NDVHP > 1 162: /* 163: * If drive is offline, forget about positioning. 164: */ 165: if ((dvhpaddr->hpds & (HPDS_DPR | HPDS_MOL)) != (HPDS_DPR | HPDS_MOL)) 166: goto done; 167: /* 168: * Not on cylinder at correct position: seek 169: */ 170: bn = dkblock(bp); 171: cn = bp->b_cylin; 172: sn = bn % (DVHP_NSECT * DVHP_NTRAC); 173: sn = (sn + DVHP_NSECT - DVHP_SDIST) % DVHP_NSECT; 174: 175: if (dvhpaddr->hpcc != cn) { 176: dvhpaddr->hpdc = cn; 177: dvhpaddr->hpcs1.c[0] = HP_IE | HP_SEEK | HP_GO; 178: #ifdef DVHP_DKN 179: /* 180: * Mark unit busy for iostat. 181: */ 182: unit += DVHP_DKN; 183: dk_busy |= 1 << unit; 184: dk_numb[unit]++; 185: #endif 186: return; 187: } 188: #endif NDVHP > 1 189: 190: done: 191: /* 192: * Device is ready to go. 193: * Put it on the ready queue for the controller. 194: */ 195: dp->b_forw = NULL; 196: if (dvhptab.b_actf == NULL) 197: dvhptab.b_actf = dp; 198: else 199: dvhptab.b_actl->b_forw = dp; 200: dvhptab.b_actl = dp; 201: } 202: 203: /* 204: * Start up a transfer on a drive. 205: */ 206: dvhpstart() 207: { 208: register struct hpdevice *dvhpaddr = DVHPADDR; 209: register struct buf *bp; 210: struct buf *dp; 211: register unit; 212: daddr_t bn; 213: int dn, sn, tn, cn; 214: 215: loop: 216: /* 217: * Pull a request off the controller queue. 218: */ 219: if ((dp = dvhptab.b_actf) == NULL) 220: return; 221: if ((bp = dp->b_actf) == NULL) { 222: dvhptab.b_actf = dp->b_forw; 223: goto loop; 224: } 225: /* 226: * Mark controller busy and 227: * determine destination of this request. 228: */ 229: dvhptab.b_active++; 230: unit = minor(bp->b_dev) & 077; 231: dn = dkunit(bp); 232: bn = dkblock(bp); 233: cn = bn / (DVHP_NSECT * DVHP_NTRAC) + dvhp_sizes[unit & 07].cyloff; 234: sn = bn % (DVHP_NSECT * DVHP_NTRAC); 235: tn = sn / DVHP_NSECT; 236: sn = sn % DVHP_NSECT; 237: 238: /* 239: * Select drive. 240: */ 241: dvhpaddr->hpcs2.w = dn; 242: 243: /* 244: * Check that it is ready and online. 245: */ 246: if ((dvhpaddr->hpds & (HPDS_DPR | HPDS_MOL)) != (HPDS_DPR | HPDS_MOL)) { 247: dvhptab.b_active = 0; 248: dvhptab.b_errcnt = 0; 249: dp->b_actf = bp->av_forw; 250: bp->b_flags |= B_ERROR; 251: iodone(bp); 252: goto loop; 253: } 254: if (dvhptab.b_errcnt >= 16 && (bp->b_flags & B_READ)) { 255: dvhpaddr->hpof = dvhp_offset[dvhptab.b_errcnt & 017] | HPOF_FMT22; 256: dvhpaddr->hpcs1.w = HP_OFFSET | HP_GO; 257: while ((dvhpaddr->hpds & (HPDS_PIP | HPDS_DRY)) != HPDS_DRY) 258: ; 259: } 260: dvhpaddr->hpdc = cn; 261: dvhpaddr->hpda = (tn << 8) + sn; 262: dvhpaddr->hpba = bp->b_un.b_addr; 263: #if PDP11 == 70 || PDP11 == GENERIC 264: if (dvhptab.b_flags & B_RH70) 265: dvhpaddr->hpbae = bp->b_xmem; 266: #endif 267: dvhpaddr->hpwc = -(bp->b_bcount >> 1); 268: /* 269: * Warning: unit is being used as a temporary. 270: */ 271: unit = ((bp->b_xmem & 3) << 8) | HP_IE | HP_GO; 272: if (bp->b_flags & B_READ) 273: unit |= HP_RCOM; 274: else 275: unit |= HP_WCOM; 276: dvhpaddr->hpcs1.w = unit; 277: 278: #ifdef DVHP_DKN 279: dk_busy |= 1 << (DVHP_DKN + NDVHP); 280: dk_numb[DVHP_DKN + NDVHP]++; 281: dk_wds[DVHP_DKN + NDVHP] += bp->b_bcount >> 6; 282: #endif DVHP_DKN 283: } 284: 285: /* 286: * Handle a disk interrupt. 287: */ 288: dvhpintr() 289: { 290: register struct hpdevice *dvhpaddr = DVHPADDR; 291: register struct buf *dp; 292: struct buf *bp; 293: register unit; 294: int as, i, j; 295: 296: as = dvhpaddr->hpas & 0377; 297: if (dvhptab.b_active) { 298: #ifdef DVHP_DKN 299: dk_busy &= ~(1 << (DVHP_DKN + NDVHP)); 300: #endif DVHP_DKN 301: /* 302: * Get device and block structures. Select the drive. 303: */ 304: dp = dvhptab.b_actf; 305: bp = dp->b_actf; 306: unit = dkunit(bp); 307: dvhpaddr->hpcs2.c[0] = unit; 308: /* 309: * Check for and process errors. 310: */ 311: if (dvhpaddr->hpcs1.w & HP_TRE) { /* error bit */ 312: while ((dvhpaddr->hpds & HPDS_DRY) == 0) 313: ; 314: if (dvhpaddr->hper1 & HPER1_WLE) { 315: /* 316: * Give up on write locked devices 317: * immediately. 318: */ 319: printf("dvhp%d: write locked\n", unit); 320: bp->b_flags |= B_ERROR; 321: } else { 322: /* 323: * After 28 retries (16 without offset and 324: * 12 with offset positioning), give up. 325: */ 326: if (++dvhptab.b_errcnt > 28) { 327: bp->b_flags |= B_ERROR; 328: #ifdef UCB_DEVERR 329: harderr(bp, "dvhp"); 330: printf("cs2=%b er1=%b\n", 331: dvhpaddr->hpcs2.w, HPCS2_BITS, 332: dvhpaddr->hper1, HPER1_BITS); 333: #else 334: deverror(bp, dvhpaddr->hpcs2.w, 335: dvhpaddr->hper1); 336: #endif 337: } else 338: dvhptab.b_active = 0; 339: } 340: #ifdef UCB_ECC 341: /* 342: * If soft ecc, correct it (continuing 343: * by returning if necessary). 344: * Otherwise, fall through and retry the transfer. 345: */ 346: if ((dvhpaddr->hper1 & (HPER1_DCK | HPER1_ECH)) == HPER1_DCK) 347: if (dvhpecc(bp)) 348: return; 349: #endif 350: dvhpaddr->hpcs1.w = HP_TRE | HP_IE | HP_DCLR | HP_GO; 351: if ((dvhptab.b_errcnt & 07) == 4) { 352: dvhpaddr->hpcs1.w = HP_RECAL | HP_IE | HP_GO; 353: while ((dvhpaddr->hpds & (HPDS_PIP | HPDS_DRY)) != HPDS_DRY) 354: ; 355: } 356: } 357: if (dvhptab.b_active) { 358: if (dvhptab.b_errcnt) { 359: dvhpaddr->hpcs1.w = HP_RTC | HP_GO; 360: while ((dvhpaddr->hpds & (HPDS_PIP | HPDS_DRY)) != HPDS_DRY) 361: ; 362: } 363: dvhptab.b_active = 0; 364: dvhptab.b_errcnt = 0; 365: dvhptab.b_actf = dp->b_forw; 366: dp->b_active = 0; 367: dp->b_actf = bp->av_forw; 368: bp->b_resid = -(dvhpaddr->hpwc << 1); 369: iodone(bp); 370: dvhpaddr->hpcs1.w = HP_IE; 371: if (dp->b_actf) 372: dvhpustart(unit); 373: } 374: as &= ~(1 << unit); 375: } else 376: { 377: if (as == 0) 378: dvhpaddr->hpcs1.w = HP_IE; 379: dvhpaddr->hpcs1.c[1] = HP_TRE >> 8; 380: } 381: for (unit = 0; unit < NDVHP; unit++) 382: if (as & (1 << unit)) 383: dvhpustart(unit); 384: dvhpstart(); 385: } 386: 387: dvhpread(dev) 388: dev_t dev; 389: { 390: #ifdef UCB_DBUFS 391: register int unit = (minor(dev) >> 3) & 07; 392: 393: if (unit >= NHP) 394: u.u_error = ENXIO; 395: else 396: physio(dvhpstrategy, &rdvhpbuf[unit], dev, B_READ); 397: #else 398: physio(dvhpstrategy, &rdvhpbuf, dev, B_READ); 399: #endif 400: } 401: 402: dvhpwrite(dev) 403: dev_t dev; 404: { 405: #ifdef UCB_DBUFS 406: register int unit = (minor(dev) >> 3) & 07; 407: 408: if (unit >= NHP) 409: u.u_error = ENXIO; 410: else 411: physio(dvhpstrategy, &rdvhpbuf[unit], dev, B_WRITE); 412: #else 413: physio(dvhpstrategy, &rdvhpbuf, dev, B_WRITE); 414: #endif 415: } 416: 417: #ifdef UCB_ECC 418: #define exadr(x,y) (((long)(x) << 16) | (unsigned)(y)) 419: 420: /* 421: * Correct an ECC error and restart the i/o to complete 422: * the transfer if necessary. This is quite complicated because 423: * the transfer may be going to an odd memory address base 424: * and/or across a page boundary. 425: */ 426: dvhpecc(bp) 427: register struct buf *bp; 428: { 429: register struct hpdevice *dvhpaddr = DVHPADDR; 430: register unsigned byte; 431: ubadr_t bb, addr; 432: long wrong; 433: int bit, wc; 434: unsigned ndone, npx; 435: int ocmd; 436: int cn, tn, sn; 437: daddr_t bn; 438: #ifdef UNIBUS_MAP 439: struct ubmap *ubp; 440: #endif 441: 442: /* 443: * ndone is #bytes including the error 444: * which is assumed to be in the last disk page transferred. 445: */ 446: wc = dvhpaddr->hpwc; 447: ndone = (wc * NBPW) + bp->b_bcount; 448: npx = ndone / PGSIZE; 449: printf("dvhp%d%c: soft ecc bn %D\n", 450: dkunit(bp), 'a' + (minor(bp->b_dev) & 07), 451: bp->b_blkno + (npx - 1)); 452: wrong = dvhpaddr->hpec2; 453: if(wrong == 0) { 454: dvhpaddr->hpof = HPOF_FMT22; 455: dvhpaddr->hpcs1.w |= HP_IE; 456: return (0); 457: } 458: 459: /* 460: * Compute the byte/bit position of the err 461: * within the last disk page transferred. 462: * Hpec1 is origin-1. 463: */ 464: byte = dvhpaddr->hpec1 - 1; 465: bit = byte & 07; 466: byte >>= 3; 467: byte += ndone - PGSIZE; 468: bb = exadr(bp->b_xmem, bp->b_un.b_addr); 469: wrong <<= bit; 470: 471: /* 472: * Correct until mask is zero or until end of transfer, 473: * whichever comes first. 474: */ 475: while (byte < bp->b_bcount && wrong != 0) { 476: addr = bb + byte; 477: #ifdef UNIBUS_MAP 478: if (bp->b_flags & (B_MAP | B_UBAREMAP)) { 479: /* 480: * Simulate UNIBUS map if UNIBUS transfer. 481: */ 482: ubp = UBMAP + ((addr >> 13) & 037); 483: addr = exadr(ubp->ub_hi, ubp->ub_lo) + (addr & 017777); 484: } 485: #endif 486: putmemc(addr, getmemc(addr) ^ (int) wrong); 487: byte++; 488: wrong >>= 8; 489: } 490: 491: dvhptab.b_active++; 492: if (wc == 0) 493: return (0); 494: 495: /* 496: * Have to continue the transfer. Clear the drive 497: * and compute the position where the transfer is to continue. 498: * We have completed npx sectors of the transfer already. 499: */ 500: ocmd = (dvhpaddr->hpcs1.w & ~HP_RDY) | HP_IE | HP_GO; 501: dvhpaddr->hpcs2.w = dkunit(bp); 502: dvhpaddr->hpcs1.w = HP_TRE | HP_DCLR | HP_GO; 503: 504: bn = dkblock(bp); 505: cn = bp->b_cylin - bn / (DVHP_NSECT * DVHP_NTRAC); 506: bn += npx; 507: addr = bb + ndone; 508: 509: cn += bn / (DVHP_NSECT * DVHP_NTRAC); 510: sn = bn % (DVHP_NSECT * DVHP_NTRAC); 511: tn = sn / DVHP_NSECT; 512: sn %= DVHP_NSECT; 513: 514: dvhpaddr->hpdc = cn; 515: dvhpaddr->hpda = (tn << 8) + sn; 516: dvhpaddr->hpwc = ((int)(ndone - bp->b_bcount)) / NBPW; 517: dvhpaddr->hpba = (int) addr; 518: #if PDP11 == 70 || PDP11 == GENERIC 519: if (dvhptab.b_flags & B_RH70) 520: dvhpaddr->hpbae = (int) (addr >> 16); 521: #endif 522: dvhpaddr->hpcs1.w = ocmd; 523: return (1); 524: } 525: #endif UCB_ECC 526: #endif NDVHP