1*264Sbill int printsw; 2*264Sbill int slow1 = 1; 3*264Sbill int slow2 = 1; 4*264Sbill int slow3 = 1; 5*264Sbill int slow4 = 1; 6*264Sbill /* 10/14/12 3.1 06/16/80 */ 7*264Sbill 8*264Sbill /* 9*264Sbill * Emulex UNIBUS disk driver with overlapped seeks and ECC recovery. 10*264Sbill * 11*264Sbill * This driver has been tested on a SC-11B Controller, configured 12*264Sbill * with the following internal switch settings: 13*264Sbill * SW1-1 5/19 surfaces (off, 19 surfaces on Ampex 9300) 14*264Sbill * SW1-2 chksum enable (off, checksum disabled) 15*264Sbill * SW1-3 volume select (off, 815 cylinders) 16*264Sbill * SW1-4 sector select (on, 32 sectors) 17*264Sbill * SW1-5 unused (off) 18*264Sbill * SW1-6 port select (on, single port) 19*264Sbill * SW1-7 npr delay (off, disable) 20*264Sbill * SW1-8 ecc test mode (off, disable) 21*264Sbill * and top mounted switches: 22*264Sbill * SW2-1 extend opcodes (off=open, disable) 23*264Sbill * SW2-2 extend diag (off=open, disable) 24*264Sbill * SW2-3 4 wd dma burst (off=open, disable) 25*264Sbill * SW2-4 unused (off=open) 26*264Sbill * 27*264Sbill * The controller transfers data much more rapidly with SW2-3 set, 28*264Sbill * but we have previously experienced problems with it set this way. 29*264Sbill * We intend to try this again in the near future. 30*264Sbill * 31*264Sbill * wnj June 14, 1980 32*264Sbill */ 33*264Sbill 34*264Sbill #include "../h/param.h" 35*264Sbill #include "../h/systm.h" 36*264Sbill #include "../h/buf.h" 37*264Sbill #include "../h/conf.h" 38*264Sbill #include "../h/dir.h" 39*264Sbill #include "../h/user.h" 40*264Sbill #include "../h/map.h" 41*264Sbill #include "../h/mba.h" 42*264Sbill #include "../h/mtpr.h" 43*264Sbill #include "../h/pte.h" 44*264Sbill #include "../h/uba.h" 45*264Sbill #include "../h/vm.h" 46*264Sbill 47*264Sbill /* 48*264Sbill * Define number of drives, and range of sampling information to be used. 49*264Sbill * 50*264Sbill * Normally, DK_N .. DK_N+NUP-1 gather individual drive stats, 51*264Sbill * and DK_N+NUP gathers controller transferring stats. 52*264Sbill * 53*264Sbill * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive. 54*264Sbill * If DK_NMAX is yet smaller, some drives are not monitored. 55*264Sbill */ 56*264Sbill #define DK_N 1 57*264Sbill #define DK_NMAX 2 58*264Sbill 59*264Sbill #define ushort unsigned short 60*264Sbill 61*264Sbill struct device 62*264Sbill { 63*264Sbill ushort upcs1; /* control and status register 1 */ 64*264Sbill short upwc; /* word count register */ 65*264Sbill ushort upba; /* UNIBUS address register */ 66*264Sbill ushort upda; /* desired address register */ 67*264Sbill ushort upcs2; /* control and status register 2 */ 68*264Sbill ushort upds; /* drive Status */ 69*264Sbill ushort uper1; /* error register 1 */ 70*264Sbill ushort upas; /* attention summary */ 71*264Sbill ushort upla; /* look ahead */ 72*264Sbill ushort updb; /* data buffer */ 73*264Sbill ushort upmr; /* maintenance */ 74*264Sbill ushort updt; /* drive type */ 75*264Sbill ushort upsn; /* serial number */ 76*264Sbill ushort upof; /* offset register */ 77*264Sbill ushort updc; /* desired cylinder address register */ 78*264Sbill ushort upcc; /* current cylinder */ 79*264Sbill ushort uper2; /* error register 2 */ 80*264Sbill ushort uper3; /* error register 3 */ 81*264Sbill ushort upec1; /* burst error bit position */ 82*264Sbill ushort upec2; /* burst error bit pattern */ 83*264Sbill }; 84*264Sbill 85*264Sbill #define UPADDR ((struct device *)(UBA0_DEV + 0176700)) 86*264Sbill 87*264Sbill #define NUP 2 /* Number of drives this installation */ 88*264Sbill 89*264Sbill #define NSECT 32 90*264Sbill #define NTRAC 19 91*264Sbill 92*264Sbill /* 93*264Sbill * Constants controlling on-cylinder SEARCH usage. 94*264Sbill * 95*264Sbill * We assume that it takes SDIST sectors of time to set up a transfer. 96*264Sbill * If a drive is on-cylinder, and between SDIST and SDIST+RDIST sectors 97*264Sbill * from the first sector to be transferred, then we just perform the 98*264Sbill * transfer. SDIST represents interrupt latency, RDIST the amount 99*264Sbill * of rotation which is tolerable to avoid another interrupt. 100*264Sbill */ 101*264Sbill #define SDIST 4 /* 4 sectors ~~= 2 msec */ 102*264Sbill #define RDIST 6 /* 6 sectors ~~= 3 msec */ 103*264Sbill 104*264Sbill /* 105*264Sbill * To fill a 300M drive: 106*264Sbill * A is designed to be used as a root. 107*264Sbill * B is suitable for a swap area. 108*264Sbill * H is the primary storage area. 109*264Sbill * On systems with RP06'es, we normally use only 291346 blocks of the H 110*264Sbill * area, and use DEF or G to cover the rest of the drive. The C system 111*264Sbill * covers the whole drive and can be used for pack-pack copying. 112*264Sbill */ 113*264Sbill struct size 114*264Sbill { 115*264Sbill daddr_t nblocks; 116*264Sbill int cyloff; 117*264Sbill } up_sizes[8] = { 118*264Sbill 15884, 0, /* A=cyl 0 thru 26 */ 119*264Sbill 33440, 27, /* B=cyl 27 thru 81 */ 120*264Sbill 494912, 0, /* C=cyl 0 thru 814 */ 121*264Sbill 15884, 562, /* D=cyl 562 thru 588 */ 122*264Sbill 55936, 589, /* E=cyl 589 thru 680 */ 123*264Sbill 81472, 681, /* F=cyl 681 thru 814 */ 124*264Sbill 153824, 562, /* G=cyl 562 thru 814 */ 125*264Sbill 445664, 82, /* H=cyl 82 thru 814 */ 126*264Sbill /* Later, and more safely for H area... 127*264Sbill 291346, 82, /* H=cyl 82 thru 561 */ 128*264Sbill }; 129*264Sbill 130*264Sbill /* 131*264Sbill * The following defines are used in offset positioning 132*264Sbill * when trying to recover disk errors, with the constants being 133*264Sbill * +/- microinches. Note that header compare inhibit (HCI) is not 134*264Sbill * tried (this makes sense only during read, in any case.) 135*264Sbill * 136*264Sbill * ARE ALL THESE IMPLEMENTED ON 9300? 137*264Sbill */ 138*264Sbill #define P400 020 139*264Sbill #define M400 0220 140*264Sbill #define P800 040 141*264Sbill #define M800 0240 142*264Sbill #define P1200 060 143*264Sbill #define M1200 0260 144*264Sbill #define HCI 020000 145*264Sbill 146*264Sbill int up_offset[16] = 147*264Sbill { 148*264Sbill P400, M400, P400, M400, 149*264Sbill P800, M800, P800, M800, 150*264Sbill P1200, M1200, P1200, M1200, 151*264Sbill 0, 0, 0, 0, 152*264Sbill }; 153*264Sbill 154*264Sbill /* 155*264Sbill * Each drive has a table uputab[i]. On this table are sorted the 156*264Sbill * pending requests implementing an elevator algorithm (see dsort.c.) 157*264Sbill * In the upustart() routine, each drive is independently advanced 158*264Sbill * until it is on the desired cylinder for the next transfer and near 159*264Sbill * the desired sector. The drive is then chained onto the uptab 160*264Sbill * table, and the transfer is initiated by the upstart() routine. 161*264Sbill * When the transfer is completed the driver reinvokes the upustart() 162*264Sbill * routine to set up the next transfer. 163*264Sbill */ 164*264Sbill struct buf uptab; 165*264Sbill struct buf uputab[NUP]; 166*264Sbill 167*264Sbill struct buf rupbuf; /* Buffer for raw i/o */ 168*264Sbill 169*264Sbill /* Drive commands, placed in upcs1 */ 170*264Sbill #define GO 01 /* Go bit, set in all commands */ 171*264Sbill #define PRESET 020 /* Preset drive at init or after errors */ 172*264Sbill #define OFFSET 014 /* Offset heads to try to recover error */ 173*264Sbill #define RTC 016 /* Return to center-line after OFFSET */ 174*264Sbill #define SEARCH 030 /* Search for cylinder+sector */ 175*264Sbill #define RECAL 06 /* Recalibrate, needed after seek error */ 176*264Sbill #define DCLR 010 /* Drive clear, after error */ 177*264Sbill #define WCOM 060 /* Write */ 178*264Sbill #define RCOM 070 /* Read */ 179*264Sbill 180*264Sbill /* Other bits of upcs1 */ 181*264Sbill #define IE 0100 /* Controller wide interrupt enable */ 182*264Sbill #define TRE 040000 /* Transfer error */ 183*264Sbill 184*264Sbill /* Drive status bits of upds */ 185*264Sbill #define PIP 020000 /* Positioning in progress */ 186*264Sbill #define ERR 040000 /* Error has occurred, DCLR necessary */ 187*264Sbill #define VV 0100 /* Volume is valid, set by PRESET */ 188*264Sbill #define DPR 0400 /* Drive has been preset */ 189*264Sbill #define MOL 010000 /* Drive is online, heads loaded, etc */ 190*264Sbill #define DRY 0200 /* Drive ready */ 191*264Sbill 192*264Sbill /* Bits of uper1 */ 193*264Sbill #define DCK 0100000 /* Ecc error occurred */ 194*264Sbill #define ECH 0100 /* Ecc error was unrecoverable */ 195*264Sbill #define WLE 04000 /* Attempt to write read-only drive */ 196*264Sbill 197*264Sbill /* Bits of upof; the offset bits above are also in this register */ 198*264Sbill #define FMT22 010000 /* 16 bits/word, must be always set */ 199*264Sbill 200*264Sbill #define b_cylin b_resid 201*264Sbill 202*264Sbill int up_ubinfo; /* Information about UBA usage saved here */ 203*264Sbill /* 204*264Sbill * The EMULEX controller balks if accessed quickly after 205*264Sbill * certain operations. The exact timing has not yet been 206*264Sbill * determined, but delays are known to be needed when changing 207*264Sbill * the selected drive (by writing in upcs2), and thought to be 208*264Sbill * needed after operations like PRESET and DCLR. The following 209*264Sbill * variables control the delay, DELAY(n) is approximately n usec. 210*264Sbill */ 211*264Sbill int idelay = 500; /* Delay after PRESET or DCLR */ 212*264Sbill int sdelay = 500; /* Delay after selecting drive in upcs2 */ 213*264Sbill 214*264Sbill #define DELAY(N) { register int d; d = N; while (--d > 0); } 215*264Sbill 216*264Sbill int nwaitcs2; /* How many sdelay loops ? */ 217*264Sbill int neasycs2; /* How many sdelay loops not needed ? */ 218*264Sbill 219*264Sbill #ifdef INTRLVE 220*264Sbill daddr_t dkblock(); 221*264Sbill #endif 222*264Sbill 223*264Sbill /* 224*264Sbill * Queue an i/o request for a drive, checking first that it is in range. 225*264Sbill * 226*264Sbill * A unit start is issued if the drive is inactive, causing 227*264Sbill * a SEARCH for the correct cylinder/sector. If the drive is 228*264Sbill * already nearly on the money and the controller is not transferring 229*264Sbill * we kick it to start the transfer. 230*264Sbill */ 231*264Sbill upstrategy(bp) 232*264Sbill register struct buf *bp; 233*264Sbill { 234*264Sbill register struct buf *dp; 235*264Sbill register unit, xunit; 236*264Sbill long sz, bn; 237*264Sbill 238*264Sbill xunit = minor(bp->b_dev) & 077; 239*264Sbill sz = bp->b_bcount; 240*264Sbill sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ 241*264Sbill unit = dkunit(bp); 242*264Sbill if (unit >= NUP || 243*264Sbill bp->b_blkno < 0 || 244*264Sbill (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { 245*264Sbill bp->b_flags |= B_ERROR; 246*264Sbill iodone(bp); 247*264Sbill return; 248*264Sbill } 249*264Sbill bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; 250*264Sbill dp = &uputab[unit]; 251*264Sbill (void) spl5(); 252*264Sbill disksort(dp, bp); 253*264Sbill if (dp->b_active == 0) { 254*264Sbill upustart(unit); 255*264Sbill if (uptab.b_actf && uptab.b_active == 0) 256*264Sbill upstart(); 257*264Sbill } 258*264Sbill (void) spl0(); 259*264Sbill } 260*264Sbill 261*264Sbill /* 262*264Sbill * Start activity on specified drive; called when drive is inactive 263*264Sbill * and new transfer request arrives and also when upas indicates that 264*264Sbill * a SEARCH command is complete. 265*264Sbill */ 266*264Sbill upustart(unit) 267*264Sbill register unit; 268*264Sbill { 269*264Sbill register struct buf *bp, *dp; 270*264Sbill register struct device *upaddr = UPADDR; 271*264Sbill daddr_t bn; 272*264Sbill int sn, cn, csn; 273*264Sbill 274*264Sbill if (printsw&1) printf("upustart\n"); 275*264Sbill if (slow1) DELAY(idelay); 276*264Sbill upaddr->upas = 1<<unit; 277*264Sbill if (slow4) DELAY(idelay); 278*264Sbill upaddr->upcs1 = IE; 279*264Sbill if (slow4) DELAY(idelay); 280*264Sbill if (unit >= NUP) { 281*264Sbill printf("stray upustart\n"); /* can't happen */ 282*264Sbill return; 283*264Sbill } 284*264Sbill 285*264Sbill if (unit+DK_N <= DK_NMAX) 286*264Sbill dk_busy &= ~(1<<(unit+DK_N)); 287*264Sbill dp = &uputab[unit]; 288*264Sbill if((bp=dp->b_actf) == NULL) 289*264Sbill return; 290*264Sbill if (slow1) DELAY(idelay); 291*264Sbill if ((upaddr->upcs2 & 07) != unit) { 292*264Sbill upaddr->upcs2 = unit; 293*264Sbill DELAY(sdelay); 294*264Sbill nwaitcs2++; 295*264Sbill } else 296*264Sbill neasycs2++; 297*264Sbill if (slow2) DELAY(idelay); 298*264Sbill if((upaddr->upds & VV) == 0) { 299*264Sbill upaddr->upcs1 = IE|PRESET|GO; 300*264Sbill DELAY(idelay); 301*264Sbill upaddr->upof = FMT22; 302*264Sbill } 303*264Sbill /* 304*264Sbill * Don't SEARCH twice on same drive; avoids looping. 305*264Sbill */ 306*264Sbill if(dp->b_active) 307*264Sbill goto done; 308*264Sbill dp->b_active++; 309*264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) 310*264Sbill goto done; 311*264Sbill 312*264Sbill bn = dkblock(bp); 313*264Sbill cn = bp->b_cylin; 314*264Sbill sn = bn%(NSECT*NTRAC); 315*264Sbill sn = (sn+NSECT-SDIST)%NSECT; 316*264Sbill 317*264Sbill if(cn - upaddr->updc) 318*264Sbill goto search; 319*264Sbill csn = (upaddr->upla>>6) - sn - 1; 320*264Sbill if(csn < 0) 321*264Sbill csn += NSECT; 322*264Sbill if(csn > NSECT-RDIST) 323*264Sbill goto done; 324*264Sbill 325*264Sbill search: 326*264Sbill upaddr->updc = cn; 327*264Sbill upaddr->upda = sn; 328*264Sbill upaddr->upcs1 = IE|SEARCH|GO; 329*264Sbill unit += DK_N; 330*264Sbill if (unit <= DK_NMAX) { 331*264Sbill dk_busy |= 1<<unit; 332*264Sbill dk_numb[unit]++; 333*264Sbill } 334*264Sbill return; 335*264Sbill 336*264Sbill done: 337*264Sbill dp->b_forw = NULL; 338*264Sbill if(uptab.b_actf == NULL) 339*264Sbill uptab.b_actf = dp; 340*264Sbill else 341*264Sbill uptab.b_actl->b_forw = dp; 342*264Sbill uptab.b_actl = dp; 343*264Sbill } 344*264Sbill 345*264Sbill /* 346*264Sbill * Start a transfer; call from top level at spl5() or on interrupt. 347*264Sbill * 348*264Sbill * Pick a drive off the queue of ready drives and perform 349*264Sbill * the first transfer in its queue. 350*264Sbill */ 351*264Sbill upstart() 352*264Sbill { 353*264Sbill register struct buf *bp, *dp; 354*264Sbill register unit; 355*264Sbill register struct device *upaddr; 356*264Sbill daddr_t bn; 357*264Sbill int dn, sn, tn, cn; 358*264Sbill 359*264Sbill if (printsw&2) printf("upstart\n"); 360*264Sbill loop: 361*264Sbill if ((dp = uptab.b_actf) == NULL) 362*264Sbill return; 363*264Sbill if ((bp = dp->b_actf) == NULL) { 364*264Sbill uptab.b_actf = dp->b_forw; 365*264Sbill goto loop; 366*264Sbill } 367*264Sbill uptab.b_active++; 368*264Sbill unit = minor(bp->b_dev) & 077; 369*264Sbill dn = dkunit(bp); 370*264Sbill bn = dkblock(bp); 371*264Sbill cn = up_sizes[unit&07].cyloff; 372*264Sbill cn += bn/(NSECT*NTRAC); 373*264Sbill sn = bn%(NSECT*NTRAC); 374*264Sbill tn = sn/NSECT; 375*264Sbill sn = sn%NSECT; 376*264Sbill 377*264Sbill upaddr = UPADDR; 378*264Sbill if (slow3) DELAY(idelay); 379*264Sbill if ((upaddr->upcs2 & 07) != dn) { 380*264Sbill upaddr->upcs2 = dn; 381*264Sbill DELAY(sdelay); 382*264Sbill nwaitcs2++; 383*264Sbill } else 384*264Sbill neasycs2++; 385*264Sbill up_ubinfo = ubasetup(bp, 1); 386*264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 387*264Sbill uptab.b_active = 0; 388*264Sbill uptab.b_errcnt = 0; 389*264Sbill dp->b_actf = bp->av_forw; 390*264Sbill bp->b_flags |= B_ERROR; 391*264Sbill iodone(bp); 392*264Sbill ubafree(up_ubinfo), up_ubinfo = 0; 393*264Sbill goto loop; 394*264Sbill } 395*264Sbill if(uptab.b_errcnt >= 16) { 396*264Sbill upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; 397*264Sbill upaddr->upcs1 = OFFSET|GO; 398*264Sbill DELAY(idelay); 399*264Sbill while(upaddr->upds & PIP) 400*264Sbill DELAY(25); 401*264Sbill } 402*264Sbill upaddr->updc = cn; 403*264Sbill upaddr->upda = (tn << 8) + sn; 404*264Sbill upaddr->upba = up_ubinfo; 405*264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 406*264Sbill if (bp->b_flags & B_READ) 407*264Sbill upaddr->upcs1 = IE|GO|RCOM; 408*264Sbill else 409*264Sbill upaddr->upcs1 = IE|GO|WCOM; 410*264Sbill 411*264Sbill unit = dn+DK_N; 412*264Sbill if (NUP+DK_N == DK_NMAX) 413*264Sbill unit = NUP+DK_N; 414*264Sbill if (unit <= DK_NMAX) { 415*264Sbill dk_busy |= 1<<unit; 416*264Sbill dk_numb[unit]++; 417*264Sbill dk_wds[unit] += bp->b_bcount>>6; 418*264Sbill } 419*264Sbill } 420*264Sbill 421*264Sbill /* 422*264Sbill * Handle a device interrupt. 423*264Sbill * 424*264Sbill * If the transferring drive needs attention, service it 425*264Sbill * retrying on error or beginning next transfer. 426*264Sbill * Service all other ready drives, calling ustart to transfer 427*264Sbill * their blocks to the ready queue in uptab, and then restart 428*264Sbill * the controller if there is anything to do. 429*264Sbill */ 430*264Sbill upintr() 431*264Sbill { 432*264Sbill register struct buf *bp, *dp; 433*264Sbill register unit; 434*264Sbill register struct device *upaddr = UPADDR; 435*264Sbill int as = upaddr->upas & 0377; 436*264Sbill 437*264Sbill if (printsw&4) printf("upintr\n"); 438*264Sbill if(uptab.b_active) { 439*264Sbill dp = uptab.b_actf; 440*264Sbill bp = dp->b_actf; 441*264Sbill unit = dkunit(bp); 442*264Sbill if (DK_N+NUP == DK_NMAX) 443*264Sbill dk_busy &= ~(1<<(DK_N+NUP)); 444*264Sbill else if (DK_N+unit <= DK_NMAX) 445*264Sbill dk_busy &= ~(1<<(DK_N+unit)); 446*264Sbill if (upaddr->upcs1 & TRE) { 447*264Sbill if ((upaddr->upcs2 & 07) != unit) { 448*264Sbill upaddr->upcs2 = unit; 449*264Sbill DELAY(sdelay); 450*264Sbill nwaitcs2++; 451*264Sbill } else 452*264Sbill neasycs2++; 453*264Sbill while((upaddr->upds & DRY) == 0) 454*264Sbill DELAY(25); 455*264Sbill if(++uptab.b_errcnt > 28 || upaddr->uper1&WLE) 456*264Sbill bp->b_flags |= B_ERROR; 457*264Sbill else 458*264Sbill uptab.b_active = 0; 459*264Sbill if(uptab.b_errcnt > 27) 460*264Sbill deverror(bp, upaddr->upcs2, upaddr->uper1); 461*264Sbill if ((upaddr->uper1&(DCK|ECH)) == DCK) { 462*264Sbill if (upecc(upaddr, bp)) 463*264Sbill return; 464*264Sbill } 465*264Sbill upaddr->upcs1 = TRE|IE|DCLR|GO; 466*264Sbill DELAY(idelay); 467*264Sbill if((uptab.b_errcnt&07) == 4) { 468*264Sbill upaddr->upcs1 = RECAL|GO|IE; 469*264Sbill DELAY(idelay); 470*264Sbill while(upaddr->upds & PIP) 471*264Sbill DELAY(25); 472*264Sbill } 473*264Sbill } 474*264Sbill if(uptab.b_active) { 475*264Sbill if(uptab.b_errcnt) { 476*264Sbill upaddr->upcs1 = RTC|GO; 477*264Sbill DELAY(idelay); 478*264Sbill while(upaddr->upds & PIP) 479*264Sbill DELAY(25); 480*264Sbill } 481*264Sbill uptab.b_active = 0; 482*264Sbill uptab.b_errcnt = 0; 483*264Sbill uptab.b_actf = dp->b_forw; 484*264Sbill dp->b_active = 0; 485*264Sbill dp->b_errcnt = 0; 486*264Sbill dp->b_actf = bp->av_forw; 487*264Sbill bp->b_resid = (-upaddr->upwc * 2); 488*264Sbill iodone(bp); 489*264Sbill if(dp->b_actf) 490*264Sbill upustart(unit); 491*264Sbill } 492*264Sbill as &= ~(1<<unit); 493*264Sbill ubafree(up_ubinfo), up_ubinfo = 0; 494*264Sbill } else { 495*264Sbill if (upaddr->upcs1 & TRE) { 496*264Sbill upaddr->upcs1 = TRE; 497*264Sbill DELAY(idelay); 498*264Sbill } 499*264Sbill if (slow4) DELAY(idelay); 500*264Sbill if(as == 0) 501*264Sbill upaddr->upcs1 = IE; 502*264Sbill if (slow4) DELAY(idelay); 503*264Sbill } 504*264Sbill for(unit=0; unit<NUP; unit++) 505*264Sbill if(as & (1<<unit)) 506*264Sbill upustart(unit); 507*264Sbill upstart(); 508*264Sbill } 509*264Sbill 510*264Sbill upread(dev) 511*264Sbill { 512*264Sbill 513*264Sbill physio(upstrategy, &rupbuf, dev, B_READ, minphys); 514*264Sbill } 515*264Sbill 516*264Sbill upwrite(dev) 517*264Sbill { 518*264Sbill 519*264Sbill physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); 520*264Sbill } 521*264Sbill 522*264Sbill upecc(up, bp) 523*264Sbill register struct device *up; 524*264Sbill register struct buf *bp; 525*264Sbill { 526*264Sbill struct uba_regs *ubp = (struct uba_regs *)UBA0; 527*264Sbill register int i, off; 528*264Sbill caddr_t addr; 529*264Sbill int reg, bit, byte, npf, mask, o; 530*264Sbill extern char buffers[NBUF][BSIZE]; 531*264Sbill int bn, cn, tn, sn; 532*264Sbill 533*264Sbill if (printsw&8) printf("upecc\n"); 534*264Sbill /* 535*264Sbill * Npf is number of page frames (= disk blocks) completed before ecc. 536*264Sbill */ 537*264Sbill npf = btop((UPADDR->upwc * sizeof(short)) + bp->b_bcount) - 1; 538*264Sbill reg = btop(up_ubinfo&0xffff) + npf; 539*264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 540*264Sbill printf("%D ", bp->b_blkno+npf); 541*264Sbill prdev("ECC", bp->b_dev); 542*264Sbill mask = up->upec2; 543*264Sbill if (mask == 0) { 544*264Sbill up->upof = FMT22; 545*264Sbill DELAY(idelay); 546*264Sbill return (0); 547*264Sbill } 548*264Sbill i = up->upec1 - 1; 549*264Sbill bit = i&017; 550*264Sbill i = (i&~017)>>3; 551*264Sbill byte = i + o; 552*264Sbill if (byte & 1) { 553*264Sbill byte--; 554*264Sbill bit += 8; 555*264Sbill } 556*264Sbill i += (int)ptob(reg); 557*264Sbill for (off = 0; off <= 32; off += 16) { 558*264Sbill if (i <= bp->b_bcount) { 559*264Sbill addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 560*264Sbill (byte & PGOFSET); 561*264Sbill putmemw(addr, getmemw(addr)^(mask<<bit)); 562*264Sbill } 563*264Sbill byte += sizeof (short); 564*264Sbill i += sizeof (short); 565*264Sbill bit -= 16; 566*264Sbill } 567*264Sbill uptab.b_active++; 568*264Sbill if (up->upwc == 0) 569*264Sbill return (0); 570*264Sbill up->upcs1 = DCLR|GO; 571*264Sbill DELAY(idelay); 572*264Sbill bn = dkblock(bp); 573*264Sbill cn = bp->b_cylin; 574*264Sbill sn = bn%(NSECT*NTRAC); 575*264Sbill tn = sn/NSECT; 576*264Sbill sn %= NSECT; 577*264Sbill sn += npf + 1; 578*264Sbill cn += sn/NSECT; 579*264Sbill sn %= NSECT; 580*264Sbill up->updc = cn; 581*264Sbill up->upda = ((i/NSECT)<<8) + (i%NSECT); 582*264Sbill up->upba = (int)ptob(reg+1)|((int)bp->b_un.b_addr&PGOFSET); 583*264Sbill up->upcs1 = IE|GO|RCOM; 584*264Sbill return (1); 585*264Sbill } 586