1*11211Ssam /* up.c 4.71 83/02/21 */ 2264Sbill 31937Swnj #include "up.h" 42646Swnj #if NSC > 0 5264Sbill /* 69548Ssam * UNIBUS disk driver with: 79548Ssam * overlapped seeks, 89548Ssam * ECC recovery, and 99548Ssam * bad sector forwarding. 102889Swnj * 112889Swnj * TODO: 123445Sroot * Check that offset recovery code works 13264Sbill */ 149782Ssam #include "../machine/pte.h" 15264Sbill 16264Sbill #include "../h/param.h" 17264Sbill #include "../h/systm.h" 18308Sbill #include "../h/dk.h" 199548Ssam #include "../h/dkbad.h" 20264Sbill #include "../h/buf.h" 21264Sbill #include "../h/conf.h" 22264Sbill #include "../h/dir.h" 23264Sbill #include "../h/user.h" 24264Sbill #include "../h/map.h" 252571Swnj #include "../h/vm.h" 262379Swnj #include "../h/cmap.h" 279357Ssam #include "../h/uio.h" 2810871Ssam #include "../h/kernel.h" 29264Sbill 309357Ssam #include "../vax/cpu.h" 319357Ssam #include "../vax/nexus.h" 329357Ssam #include "../vaxuba/ubavar.h" 339357Ssam #include "../vaxuba/ubareg.h" 349357Ssam #include "../vaxuba/upreg.h" 359357Ssam 362395Swnj struct up_softc { 372395Swnj int sc_softas; 382607Swnj int sc_ndrive; 392395Swnj int sc_wticks; 402674Swnj int sc_recal; 412646Swnj } up_softc[NSC]; 42275Sbill 432395Swnj /* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */ 4410858Ssam struct size { 45264Sbill daddr_t nblocks; 46264Sbill int cyloff; 47*11211Ssam } up9300_sizes[8] = { 48264Sbill 15884, 0, /* A=cyl 0 thru 26 */ 49264Sbill 33440, 27, /* B=cyl 27 thru 81 */ 50341Sbill 495520, 0, /* C=cyl 0 thru 814 */ 51264Sbill 15884, 562, /* D=cyl 562 thru 588 */ 52264Sbill 55936, 589, /* E=cyl 589 thru 680 */ 533730Sroot 81376, 681, /* F=cyl 681 thru 814 */ 543730Sroot 153728, 562, /* G=cyl 562 thru 814 */ 55264Sbill 291346, 82, /* H=cyl 82 thru 561 */ 56*11211Ssam }, up9766_sizes[8] = { 57*11211Ssam 15884, 0, /* A=cyl 0 thru 26 */ 58*11211Ssam 33440, 27, /* B=cyl 27 thru 81 */ 59*11211Ssam 500384, 0, /* C=cyl 0 thru 822 */ 60*11211Ssam 15884, 562, /* D=cyl 562 thru 588 */ 61*11211Ssam 55936, 589, /* E=cyl 589 thru 680 */ 62*11211Ssam 86240, 681, /* F=cyl 681 thru 822 */ 63*11211Ssam 158592, 562, /* G=cyl 562 thru 822 */ 64*11211Ssam 291346, 82, /* H=cyl 82 thru 561 */ 65*11211Ssam }, up160_sizes[8] = { 662395Swnj 15884, 0, /* A=cyl 0 thru 49 */ 672395Swnj 33440, 50, /* B=cyl 50 thru 154 */ 682395Swnj 263360, 0, /* C=cyl 0 thru 822 */ 69*11211Ssam 15884, 155, /* D=cyl 155 thru 204 */ 70*11211Ssam 55936, 205, /* E=cyl 205 thru 379 */ 71*11211Ssam 141664, 380, /* F=cyl 380 thru 822 */ 72*11211Ssam 213664, 155, /* G=cyl 155 thru 822 */ 732395Swnj 0, 0, 746851Ssam }, upam_sizes[8] = { 756305Sroot 15884, 0, /* A=cyl 0 thru 31 */ 766305Sroot 33440, 32, /* B=cyl 32 thru 97 */ 776305Sroot 524288, 0, /* C=cyl 0 thru 1023 */ 78*11211Ssam 15884, 668, /* D=cyl 668 thru 699 */ 79*11211Ssam 55936, 700, /* E=cyl 700 thru 809 */ 80*11211Ssam 109472, 810, /* F=cyl 810 thru 1023 */ 81*11211Ssam 182176, 668, /* G=cyl 668 thru 1023 */ 826305Sroot 291346, 98, /* H=cyl 98 thru 667 */ 83264Sbill }; 842395Swnj /* END OF STUFF WHICH SHOULD BE READ IN PER DISK */ 85264Sbill 866346Swnj /* 876346Swnj * On a 780 upSDIST could be 2, but 886346Swnj * in the interest of 750's... 896346Swnj */ 906346Swnj #define _upSDIST 3 /* 1.5 msec */ 912395Swnj #define _upRDIST 4 /* 2.0 msec */ 92264Sbill 932395Swnj int upSDIST = _upSDIST; 942395Swnj int upRDIST = _upRDIST; 952395Swnj 962607Swnj int upprobe(), upslave(), upattach(), updgo(), upintr(); 972983Swnj struct uba_ctlr *upminfo[NSC]; 982983Swnj struct uba_device *updinfo[NUP]; 996383Swnj #define UPIPUNITS 8 1006383Swnj struct uba_device *upip[NSC][UPIPUNITS]; /* fuji w/fixed head gives n,n+4 */ 1012395Swnj 1022607Swnj u_short upstd[] = { 0776700, 0774400, 0776300, 0 }; 1032616Swnj struct uba_driver scdriver = 1042607Swnj { upprobe, upslave, upattach, updgo, upstd, "up", updinfo, "sc", upminfo }; 1052395Swnj struct buf uputab[NUP]; 1069548Ssam char upinit[NUP]; 1072395Swnj 1082395Swnj struct upst { 1092395Swnj short nsect; 1102395Swnj short ntrak; 1112395Swnj short nspc; 1122395Swnj short ncyl; 1132395Swnj struct size *sizes; 1142395Swnj } upst[] = { 115*11211Ssam 32, 19, 32*19, 815, up9300_sizes, /* 9300 */ 116*11211Ssam 32, 19, 32*19, 823, up9766_sizes, /* 9766 */ 117*11211Ssam 32, 10, 32*10, 823, up160_sizes, /* fujitsu 160m */ 1186851Ssam 32, 16, 32*16, 1024, upam_sizes, /* ampex capricorn */ 11911119Ssam 0, 0, 0, 0, 0 1202395Swnj }; 1212395Swnj 1222629Swnj u_char up_offset[16] = { 1239548Ssam UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400, 1249548Ssam UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800, 1259548Ssam UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200, 1269548Ssam 0, 0, 0, 0 1272629Swnj }; 128264Sbill 1292616Swnj struct buf rupbuf[NUP]; 1309548Ssam struct buf bupbuf[NUP]; 1319548Ssam struct dkbad upbad[NUP]; 132264Sbill 133264Sbill #define b_cylin b_resid 134264Sbill 135264Sbill #ifdef INTRLVE 136264Sbill daddr_t dkblock(); 137264Sbill #endif 1382395Swnj 1392395Swnj int upwstart, upwatch(); /* Have started guardian */ 1402470Swnj int upseek; 1412681Swnj int upwaitdry; 1422395Swnj 1432395Swnj /*ARGSUSED*/ 1442607Swnj upprobe(reg) 1452395Swnj caddr_t reg; 1462395Swnj { 1472459Swnj register int br, cvec; 1482459Swnj 1492607Swnj #ifdef lint 1502607Swnj br = 0; cvec = br; br = cvec; 1512607Swnj #endif 1522629Swnj ((struct updevice *)reg)->upcs1 = UP_IE|UP_RDY; 1532607Swnj DELAY(10); 1542629Swnj ((struct updevice *)reg)->upcs1 = 0; 1559357Ssam return (sizeof (struct updevice)); 1562395Swnj } 1572395Swnj 1582607Swnj upslave(ui, reg) 1592983Swnj struct uba_device *ui; 1602395Swnj caddr_t reg; 1612395Swnj { 1622629Swnj register struct updevice *upaddr = (struct updevice *)reg; 1632395Swnj 1642395Swnj upaddr->upcs1 = 0; /* conservative */ 1652607Swnj upaddr->upcs2 = ui->ui_slave; 1666843Swnj upaddr->upcs1 = UP_NOP|UP_GO; 1673445Sroot if (upaddr->upcs2&UPCS2_NED) { 1682629Swnj upaddr->upcs1 = UP_DCLR|UP_GO; 1692395Swnj return (0); 1702395Swnj } 1712607Swnj return (1); 1722607Swnj } 1732607Swnj 1742607Swnj upattach(ui) 1752983Swnj register struct uba_device *ui; 1762607Swnj { 1772629Swnj register struct updevice *upaddr; 1782607Swnj 1792395Swnj if (upwstart == 0) { 1802759Swnj timeout(upwatch, (caddr_t)0, hz); 1812395Swnj upwstart++; 1822395Swnj } 1832571Swnj if (ui->ui_dk >= 0) 1842571Swnj dk_mspw[ui->ui_dk] = .0000020345; 1852607Swnj upip[ui->ui_ctlr][ui->ui_slave] = ui; 1862607Swnj up_softc[ui->ui_ctlr].sc_ndrive++; 1872629Swnj upaddr = (struct updevice *)ui->ui_addr; 18811119Ssam ui->ui_type = upmaptype(ui); 18911119Ssam } 19011119Ssam 19111119Ssam upmaptype(ui) 19211119Ssam register struct uba_device *ui; 19311119Ssam { 19411119Ssam register struct updevice *upaddr = (struct updevice *)ui->ui_addr; 19511119Ssam int type = ui->ui_type; 19611119Ssam register struct upst *st; 19711119Ssam 1982629Swnj upaddr->upcs1 = 0; 1992629Swnj upaddr->upcs2 = ui->ui_slave; 2003496Sroot upaddr->uphr = UPHR_MAXTRAK; 20111119Ssam for (st = upst; st->nsect != 0; st++) 20211119Ssam if (upaddr->uphr == st->ntrak - 1) { 20311119Ssam type = st - upst; 20411119Ssam break; 20511119Ssam } 20611119Ssam if (st->nsect == 0) 20711119Ssam printf("up%d: uphr=%x\n", ui->ui_slave, upaddr->uphr); 20811119Ssam if (type == 0) { 20911112Shelge upaddr->uphr = UPHR_MAXCYL; 21011112Shelge if (upaddr->uphr == 822) 21111119Ssam type++; 21211112Shelge } 2133496Sroot upaddr->upcs2 = UPCS2_CLR; 21411119Ssam return (type); 2152395Swnj } 216264Sbill 2179548Ssam upopen(dev) 2189548Ssam dev_t dev; 2199548Ssam { 2209548Ssam register int unit = minor(dev) >> 3; 2219548Ssam register struct uba_device *ui; 2229548Ssam 2239548Ssam if (unit >= NUP || (ui = updinfo[unit]) == 0 || ui->ui_alive == 0) 2249548Ssam return (ENXIO); 2259548Ssam return (0); 2269548Ssam } 2279548Ssam 228264Sbill upstrategy(bp) 2292395Swnj register struct buf *bp; 230264Sbill { 2312983Swnj register struct uba_device *ui; 2322395Swnj register struct upst *st; 2332395Swnj register int unit; 2342470Swnj register struct buf *dp; 2352395Swnj int xunit = minor(bp->b_dev) & 07; 2362470Swnj long bn, sz; 237264Sbill 2382470Swnj sz = (bp->b_bcount+511) >> 9; 239264Sbill unit = dkunit(bp); 2402395Swnj if (unit >= NUP) 2412395Swnj goto bad; 2422395Swnj ui = updinfo[unit]; 2432395Swnj if (ui == 0 || ui->ui_alive == 0) 2442395Swnj goto bad; 2452395Swnj st = &upst[ui->ui_type]; 2462395Swnj if (bp->b_blkno < 0 || 2472395Swnj (bn = dkblock(bp))+sz > st->sizes[xunit].nblocks) 2482395Swnj goto bad; 2492395Swnj bp->b_cylin = bn/st->nspc + st->sizes[xunit].cyloff; 2506305Sroot (void) spl5(); 2512470Swnj dp = &uputab[ui->ui_unit]; 2522470Swnj disksort(dp, bp); 2532470Swnj if (dp->b_active == 0) { 2542395Swnj (void) upustart(ui); 2552395Swnj bp = &ui->ui_mi->um_tab; 2562395Swnj if (bp->b_actf && bp->b_active == 0) 2572395Swnj (void) upstart(ui->ui_mi); 258264Sbill } 2596305Sroot (void) spl0(); 2602395Swnj return; 2612395Swnj 2622395Swnj bad: 2632395Swnj bp->b_flags |= B_ERROR; 2642395Swnj iodone(bp); 2652395Swnj return; 266264Sbill } 267264Sbill 2682674Swnj /* 2692674Swnj * Unit start routine. 2702674Swnj * Seek the drive to be where the data is 2712674Swnj * and then generate another interrupt 2722674Swnj * to actually start the transfer. 2732674Swnj * If there is only one drive on the controller, 2742674Swnj * or we are very close to the data, don't 2752674Swnj * bother with the search. If called after 2762674Swnj * searching once, don't bother to look where 2772674Swnj * we are, just queue for transfer (to avoid 2782674Swnj * positioning forever without transferrring.) 2792674Swnj */ 2802395Swnj upustart(ui) 2812983Swnj register struct uba_device *ui; 282264Sbill { 283264Sbill register struct buf *bp, *dp; 2842983Swnj register struct uba_ctlr *um; 2852629Swnj register struct updevice *upaddr; 2862395Swnj register struct upst *st; 287264Sbill daddr_t bn; 2882674Swnj int sn, csn; 2892607Swnj /* 2902607Swnj * The SC21 cancels commands if you just say 2912629Swnj * cs1 = UP_IE 2922607Swnj * so we are cautious about handling of cs1. 2932607Swnj * Also don't bother to clear as bits other than in upintr(). 2942607Swnj */ 2952674Swnj int didie = 0; 2962674Swnj 2972674Swnj if (ui == 0) 2982674Swnj return (0); 2992983Swnj um = ui->ui_mi; 3002395Swnj dk_busy &= ~(1<<ui->ui_dk); 3012395Swnj dp = &uputab[ui->ui_unit]; 302266Sbill if ((bp = dp->b_actf) == NULL) 303268Sbill goto out; 3042674Swnj /* 3052674Swnj * If the controller is active, just remember 3062674Swnj * that this device would like to be positioned... 3072674Swnj * if we tried to position now we would confuse the SC21. 3082674Swnj */ 3092395Swnj if (um->um_tab.b_active) { 3102459Swnj up_softc[um->um_ctlr].sc_softas |= 1<<ui->ui_slave; 311275Sbill return (0); 312275Sbill } 3132674Swnj /* 3142674Swnj * If we have already positioned this drive, 3152674Swnj * then just put it on the ready queue. 3162674Swnj */ 317276Sbill if (dp->b_active) 318276Sbill goto done; 319276Sbill dp->b_active = 1; 3202629Swnj upaddr = (struct updevice *)um->um_addr; 3212395Swnj upaddr->upcs2 = ui->ui_slave; 3222674Swnj /* 3232674Swnj * If drive has just come up, 3242674Swnj * setup the pack. 3252674Swnj */ 3269548Ssam if ((upaddr->upds & UPDS_VV) == 0 || upinit[ui->ui_unit] == 0) { 3279548Ssam struct buf *bbp = &bupbuf[ui->ui_unit]; 32810858Ssam 3292607Swnj /* SHOULD WARN SYSTEM THAT THIS HAPPENED */ 3309548Ssam upinit[ui->ui_unit] = 1; 3312629Swnj upaddr->upcs1 = UP_IE|UP_DCLR|UP_GO; 3322629Swnj upaddr->upcs1 = UP_IE|UP_PRESET|UP_GO; 3333445Sroot upaddr->upof = UPOF_FMT22; 334268Sbill didie = 1; 3359548Ssam st = &upst[ui->ui_type]; 3369548Ssam bbp->b_flags = B_READ|B_BUSY; 3379548Ssam bbp->b_dev = bp->b_dev; 3389548Ssam bbp->b_bcount = 512; 3399548Ssam bbp->b_un.b_addr = (caddr_t)&upbad[ui->ui_unit]; 3409548Ssam bbp->b_blkno = st->ncyl * st->nspc - st->nsect; 3419548Ssam bbp->b_cylin = st->ncyl - 1; 3429548Ssam dp->b_actf = bbp; 3439548Ssam bbp->av_forw = bp; 3449548Ssam bp = bbp; 345264Sbill } 3462674Swnj /* 3472674Swnj * If drive is offline, forget about positioning. 3482674Swnj */ 3493445Sroot if ((upaddr->upds & (UPDS_DPR|UPDS_MOL)) != (UPDS_DPR|UPDS_MOL)) 350275Sbill goto done; 3512674Swnj /* 3522674Swnj * If there is only one drive, 3532674Swnj * dont bother searching. 3542674Swnj */ 3552607Swnj if (up_softc[um->um_ctlr].sc_ndrive == 1) 3562607Swnj goto done; 3572674Swnj /* 3582674Swnj * Figure out where this transfer is going to 3592674Swnj * and see if we are close enough to justify not searching. 3602674Swnj */ 3612395Swnj st = &upst[ui->ui_type]; 362264Sbill bn = dkblock(bp); 3632395Swnj sn = bn%st->nspc; 3642395Swnj sn = (sn + st->nsect - upSDIST) % st->nsect; 3652674Swnj if (bp->b_cylin - upaddr->updc) 366266Sbill goto search; /* Not on-cylinder */ 367275Sbill else if (upseek) 368275Sbill goto done; /* Ok just to be on-cylinder */ 369264Sbill csn = (upaddr->upla>>6) - sn - 1; 370266Sbill if (csn < 0) 3712395Swnj csn += st->nsect; 3722395Swnj if (csn > st->nsect - upRDIST) 373264Sbill goto done; 374264Sbill search: 3752674Swnj upaddr->updc = bp->b_cylin; 3762674Swnj /* 3772674Swnj * Not on cylinder at correct position, 3782674Swnj * seek/search. 3792674Swnj */ 380275Sbill if (upseek) 3812629Swnj upaddr->upcs1 = UP_IE|UP_SEEK|UP_GO; 3822470Swnj else { 383275Sbill upaddr->upda = sn; 3842629Swnj upaddr->upcs1 = UP_IE|UP_SEARCH|UP_GO; 385275Sbill } 386268Sbill didie = 1; 3872674Swnj /* 3882674Swnj * Mark unit busy for iostat. 3892674Swnj */ 3902395Swnj if (ui->ui_dk >= 0) { 3912395Swnj dk_busy |= 1<<ui->ui_dk; 3922395Swnj dk_seek[ui->ui_dk]++; 393264Sbill } 394268Sbill goto out; 395264Sbill done: 3962674Swnj /* 3972674Swnj * Device is ready to go. 3982674Swnj * Put it on the ready queue for the controller 3992674Swnj * (unless its already there.) 4002674Swnj */ 4012629Swnj if (dp->b_active != 2) { 4022629Swnj dp->b_forw = NULL; 4032629Swnj if (um->um_tab.b_actf == NULL) 4042629Swnj um->um_tab.b_actf = dp; 4052629Swnj else 4062629Swnj um->um_tab.b_actl->b_forw = dp; 4072629Swnj um->um_tab.b_actl = dp; 4082629Swnj dp->b_active = 2; 4092629Swnj } 410268Sbill out: 411268Sbill return (didie); 412264Sbill } 413264Sbill 4142674Swnj /* 4152674Swnj * Start up a transfer on a drive. 4162674Swnj */ 4172395Swnj upstart(um) 4182983Swnj register struct uba_ctlr *um; 419264Sbill { 420264Sbill register struct buf *bp, *dp; 4212983Swnj register struct uba_device *ui; 4222629Swnj register struct updevice *upaddr; 4232470Swnj struct upst *st; 424264Sbill daddr_t bn; 4252681Swnj int dn, sn, tn, cmd, waitdry; 426264Sbill 427264Sbill loop: 4282674Swnj /* 4292674Swnj * Pull a request off the controller queue 4302674Swnj */ 4312395Swnj if ((dp = um->um_tab.b_actf) == NULL) 432268Sbill return (0); 433264Sbill if ((bp = dp->b_actf) == NULL) { 4342395Swnj um->um_tab.b_actf = dp->b_forw; 435264Sbill goto loop; 436264Sbill } 4372674Swnj /* 4382674Swnj * Mark controller busy, and 4392674Swnj * determine destination of this request. 4402674Swnj */ 4412395Swnj um->um_tab.b_active++; 4422395Swnj ui = updinfo[dkunit(bp)]; 443264Sbill bn = dkblock(bp); 4442395Swnj dn = ui->ui_slave; 4452395Swnj st = &upst[ui->ui_type]; 4462395Swnj sn = bn%st->nspc; 4472395Swnj tn = sn/st->nsect; 4482395Swnj sn %= st->nsect; 4492629Swnj upaddr = (struct updevice *)ui->ui_addr; 4502674Swnj /* 4512674Swnj * Select drive if not selected already. 4522674Swnj */ 4532674Swnj if ((upaddr->upcs2&07) != dn) 4542674Swnj upaddr->upcs2 = dn; 4552674Swnj /* 4562674Swnj * Check that it is ready and online 4572674Swnj */ 4582681Swnj waitdry = 0; 4593445Sroot while ((upaddr->upds&UPDS_DRY) == 0) { 4607036Swnj printf("up%d: ds wait ds=%o\n",dkunit(bp),upaddr->upds); 4612681Swnj if (++waitdry > 512) 4622681Swnj break; 4632681Swnj upwaitdry++; 4642681Swnj } 4653445Sroot if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { 4662931Swnj printf("up%d: not ready", dkunit(bp)); 4673445Sroot if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { 4682607Swnj printf("\n"); 4692395Swnj um->um_tab.b_active = 0; 4702395Swnj um->um_tab.b_errcnt = 0; 471893Sbill dp->b_actf = bp->av_forw; 472893Sbill dp->b_active = 0; 473893Sbill bp->b_flags |= B_ERROR; 474893Sbill iodone(bp); 475893Sbill goto loop; 476893Sbill } 4772674Swnj /* 4782674Swnj * Oh, well, sometimes this 4792674Swnj * happens, for reasons unknown. 4802674Swnj */ 4812629Swnj printf(" (flakey)\n"); 482264Sbill } 4832674Swnj /* 4842674Swnj * Setup for the transfer, and get in the 4852674Swnj * UNIBUS adaptor queue. 4862674Swnj */ 4872424Skre upaddr->updc = bp->b_cylin; 488264Sbill upaddr->upda = (tn << 8) + sn; 489264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 490264Sbill if (bp->b_flags & B_READ) 4912629Swnj cmd = UP_IE|UP_RCOM|UP_GO; 492264Sbill else 4932629Swnj cmd = UP_IE|UP_WCOM|UP_GO; 4942571Swnj um->um_cmd = cmd; 4953107Swnj (void) ubago(ui); 496268Sbill return (1); 497264Sbill } 498264Sbill 4992674Swnj /* 5002674Swnj * Now all ready to go, stuff the registers. 5012674Swnj */ 5022571Swnj updgo(um) 5032983Swnj struct uba_ctlr *um; 5042395Swnj { 5052629Swnj register struct updevice *upaddr = (struct updevice *)um->um_addr; 5062470Swnj 5076953Swnj um->um_tab.b_active = 2; /* should now be 2 */ 5082571Swnj upaddr->upba = um->um_ubinfo; 5092571Swnj upaddr->upcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x300); 5102395Swnj } 5112395Swnj 5122674Swnj /* 5132674Swnj * Handle a disk interrupt. 5142674Swnj */ 5152707Swnj upintr(sc21) 5162395Swnj register sc21; 517264Sbill { 518264Sbill register struct buf *bp, *dp; 5192983Swnj register struct uba_ctlr *um = upminfo[sc21]; 5202983Swnj register struct uba_device *ui; 5212629Swnj register struct updevice *upaddr = (struct updevice *)um->um_addr; 522264Sbill register unit; 5232470Swnj struct up_softc *sc = &up_softc[um->um_ctlr]; 5242607Swnj int as = (upaddr->upas & 0377) | sc->sc_softas; 5252681Swnj int needie = 1, waitdry; 526264Sbill 5272470Swnj sc->sc_wticks = 0; 5282607Swnj sc->sc_softas = 0; 5292674Swnj /* 5302674Swnj * If controller wasn't transferring, then this is an 5312674Swnj * interrupt for attention status on seeking drives. 5322674Swnj * Just service them. 5332674Swnj */ 5346346Swnj if (um->um_tab.b_active != 2 && !sc->sc_recal) { 5352674Swnj if (upaddr->upcs1 & UP_TRE) 5362674Swnj upaddr->upcs1 = UP_TRE; 5372674Swnj goto doattn; 5382674Swnj } 5396953Swnj um->um_tab.b_active = 1; 5402674Swnj /* 5412674Swnj * Get device and block structures, and a pointer 5422983Swnj * to the uba_device for the drive. Select the drive. 5432674Swnj */ 5442674Swnj dp = um->um_tab.b_actf; 5452674Swnj bp = dp->b_actf; 5462674Swnj ui = updinfo[dkunit(bp)]; 5472674Swnj dk_busy &= ~(1 << ui->ui_dk); 5482674Swnj if ((upaddr->upcs2&07) != ui->ui_slave) 5492395Swnj upaddr->upcs2 = ui->ui_slave; 5509548Ssam if (bp->b_flags&B_BAD) { 5519548Ssam if (upecc(ui, CONT)) 5529548Ssam return; 5539548Ssam } 5542674Swnj /* 5552674Swnj * Check for and process errors on 5562674Swnj * either the drive or the controller. 5572674Swnj */ 5583445Sroot if ((upaddr->upds&UPDS_ERR) || (upaddr->upcs1&UP_TRE)) { 5592681Swnj waitdry = 0; 5603445Sroot while ((upaddr->upds & UPDS_DRY) == 0) { 5612681Swnj if (++waitdry > 512) 5622681Swnj break; 5632681Swnj upwaitdry++; 5642681Swnj } 5653445Sroot if (upaddr->uper1&UPER1_WLE) { 5662674Swnj /* 5672674Swnj * Give up on write locked devices 5682674Swnj * immediately. 5692674Swnj */ 5702931Swnj printf("up%d: write locked\n", dkunit(bp)); 5712674Swnj bp->b_flags |= B_ERROR; 5722674Swnj } else if (++um->um_tab.b_errcnt > 27) { 5732674Swnj /* 5742674Swnj * After 28 retries (16 without offset, and 5752674Swnj * 12 with offset positioning) give up. 57610904Shelge * If the error was header CRC, the header is 57710904Shelge * screwed up, and the sector may in fact exist 57810904Shelge * in the bad sector table, better check... 5792674Swnj */ 58010904Shelge if (upaddr->uper1&UPER1_HCRC) { 58110904Shelge if (upecc(ui, BSE)) 58210904Shelge return; 58310904Shelge } 5849548Ssam hard: 5852931Swnj harderr(bp, "up"); 5869548Ssam printf("cn=%d tn=%d sn=%d cs2=%b er1=%b er2=%b\n", 5879548Ssam upaddr->updc, ((upaddr->upda)>>8)&077, 5889548Ssam (upaddr->upda)&037, 5899548Ssam upaddr->upcs2, UPCS2_BITS, 5909548Ssam upaddr->uper1, UPER1_BITS, 5919548Ssam upaddr->uper2, UPER2_BITS); 5922674Swnj bp->b_flags |= B_ERROR; 5939548Ssam } else if (upaddr->uper2 & UPER2_BSE) { 5949548Ssam if (upecc(ui, BSE)) 5959548Ssam return; 5969548Ssam else 5979548Ssam goto hard; 5982674Swnj } else { 5992674Swnj /* 6002674Swnj * Retriable error. 6012674Swnj * If a soft ecc, correct it (continuing 6022674Swnj * by returning if necessary. 6032674Swnj * Otherwise fall through and retry the transfer 6042674Swnj */ 6057183Sroot if ((upaddr->uper1&(UPER1_DCK|UPER1_ECH))==UPER1_DCK) { 6069548Ssam if (upecc(ui, ECC)) 6072629Swnj return; 6087183Sroot } else 6097183Sroot um->um_tab.b_active = 0; /* force retry */ 6102674Swnj } 6112674Swnj /* 6122674Swnj * Clear drive error and, every eight attempts, 6132674Swnj * (starting with the fourth) 6142674Swnj * recalibrate to clear the slate. 6152674Swnj */ 6162674Swnj upaddr->upcs1 = UP_TRE|UP_IE|UP_DCLR|UP_GO; 6172674Swnj needie = 0; 6183182Swnj if ((um->um_tab.b_errcnt&07) == 4 && um->um_tab.b_active == 0) { 6192674Swnj upaddr->upcs1 = UP_RECAL|UP_IE|UP_GO; 6203160Swnj sc->sc_recal = 0; 6213160Swnj goto nextrecal; 6222674Swnj } 6232674Swnj } 6242674Swnj /* 6253160Swnj * Advance recalibration finite state machine 6263160Swnj * if recalibrate in progress, through 6273160Swnj * RECAL 6283160Swnj * SEEK 6293160Swnj * OFFSET (optional) 6303160Swnj * RETRY 6312674Swnj */ 6323160Swnj switch (sc->sc_recal) { 6333160Swnj 6343160Swnj case 1: 6353160Swnj upaddr->updc = bp->b_cylin; 6363160Swnj upaddr->upcs1 = UP_SEEK|UP_IE|UP_GO; 6373160Swnj goto nextrecal; 6383160Swnj case 2: 6393160Swnj if (um->um_tab.b_errcnt < 16 || (bp->b_flags&B_READ) == 0) 6403160Swnj goto donerecal; 6413445Sroot upaddr->upof = up_offset[um->um_tab.b_errcnt & 017] | UPOF_FMT22; 6423160Swnj upaddr->upcs1 = UP_IE|UP_OFFSET|UP_GO; 6433160Swnj goto nextrecal; 6443160Swnj nextrecal: 6453160Swnj sc->sc_recal++; 6463160Swnj um->um_tab.b_active = 1; 6473160Swnj return; 6483160Swnj donerecal: 6493160Swnj case 3: 6502674Swnj sc->sc_recal = 0; 6513160Swnj um->um_tab.b_active = 0; 6523160Swnj break; 6532674Swnj } 6542674Swnj /* 6552674Swnj * If still ``active'', then don't need any more retries. 6562674Swnj */ 6572674Swnj if (um->um_tab.b_active) { 6582674Swnj /* 6592674Swnj * If we were offset positioning, 6602674Swnj * return to centerline. 6612674Swnj */ 6622674Swnj if (um->um_tab.b_errcnt >= 16) { 6633445Sroot upaddr->upof = UPOF_FMT22; 6642674Swnj upaddr->upcs1 = UP_RTC|UP_GO|UP_IE; 6653445Sroot while (upaddr->upds & UPDS_PIP) 6662674Swnj DELAY(25); 667268Sbill needie = 0; 668264Sbill } 6692674Swnj um->um_tab.b_active = 0; 6702674Swnj um->um_tab.b_errcnt = 0; 6712674Swnj um->um_tab.b_actf = dp->b_forw; 6722674Swnj dp->b_active = 0; 6732674Swnj dp->b_errcnt = 0; 6742674Swnj dp->b_actf = bp->av_forw; 6752674Swnj bp->b_resid = (-upaddr->upwc * sizeof(short)); 6762674Swnj iodone(bp); 6772674Swnj /* 6782674Swnj * If this unit has more work to do, 6792674Swnj * then start it up right away. 6802674Swnj */ 6812674Swnj if (dp->b_actf) 6822674Swnj if (upustart(ui)) 683268Sbill needie = 0; 684264Sbill } 6852674Swnj as &= ~(1<<ui->ui_slave); 6863403Swnj /* 6873403Swnj * Release unibus resources and flush data paths. 6883403Swnj */ 6893403Swnj ubadone(um); 6902674Swnj doattn: 6912674Swnj /* 6922674Swnj * Process other units which need attention. 6932674Swnj * For each unit which needs attention, call 6942674Swnj * the unit start routine to place the slave 6952674Swnj * on the controller device queue. 6962674Swnj */ 6973160Swnj while (unit = ffs(as)) { 6983160Swnj unit--; /* was 1 origin */ 6993160Swnj as &= ~(1<<unit); 7003160Swnj upaddr->upas = 1<<unit; 7016383Swnj if (unit < UPIPUNITS && upustart(upip[sc21][unit])) 7023160Swnj needie = 0; 7033160Swnj } 7042674Swnj /* 7052674Swnj * If the controller is not transferring, but 7062674Swnj * there are devices ready to transfer, start 7072674Swnj * the controller. 7082674Swnj */ 7092395Swnj if (um->um_tab.b_actf && um->um_tab.b_active == 0) 7102395Swnj if (upstart(um)) 711268Sbill needie = 0; 712275Sbill if (needie) 7132629Swnj upaddr->upcs1 = UP_IE; 714264Sbill } 715264Sbill 7169357Ssam upread(dev, uio) 7172616Swnj dev_t dev; 7189357Ssam struct uio *uio; 719264Sbill { 7202616Swnj register int unit = minor(dev) >> 3; 7212470Swnj 7222616Swnj if (unit >= NUP) 7239357Ssam return (ENXIO); 7249357Ssam return (physio(upstrategy, &rupbuf[unit], dev, B_READ, minphys, uio)); 725264Sbill } 726264Sbill 7279357Ssam upwrite(dev, uio) 7282616Swnj dev_t dev; 7299357Ssam struct uio *uio; 730264Sbill { 7312616Swnj register int unit = minor(dev) >> 3; 7322470Swnj 7332616Swnj if (unit >= NUP) 7349357Ssam return (ENXIO); 7359357Ssam return (physio(upstrategy, &rupbuf[unit], dev, B_WRITE, minphys, uio)); 736264Sbill } 737264Sbill 738266Sbill /* 739266Sbill * Correct an ECC error, and restart the i/o to complete 740266Sbill * the transfer if necessary. This is quite complicated because 741266Sbill * the transfer may be going to an odd memory address base and/or 742266Sbill * across a page boundary. 743266Sbill */ 7449548Ssam upecc(ui, flag) 7452983Swnj register struct uba_device *ui; 7469548Ssam int flag; 747264Sbill { 7482629Swnj register struct updevice *up = (struct updevice *)ui->ui_addr; 7492395Swnj register struct buf *bp = uputab[ui->ui_unit].b_actf; 7502983Swnj register struct uba_ctlr *um = ui->ui_mi; 7512395Swnj register struct upst *st; 7522395Swnj struct uba_regs *ubp = ui->ui_hd->uh_uba; 753266Sbill register int i; 754264Sbill caddr_t addr; 755266Sbill int reg, bit, byte, npf, mask, o, cmd, ubaddr; 756264Sbill int bn, cn, tn, sn; 757264Sbill 758264Sbill /* 759266Sbill * Npf is the number of sectors transferred before the sector 760266Sbill * containing the ECC error, and reg is the UBA register 761266Sbill * mapping (the first part of) the transfer. 762266Sbill * O is offset within a memory page of the first byte transferred. 763264Sbill */ 7649548Ssam if (flag == CONT) 7659548Ssam npf = bp->b_error; 7669548Ssam else 76710858Ssam npf = btop((up->upwc * sizeof(short)) + bp->b_bcount); 7682571Swnj reg = btop(um->um_ubinfo&0x3ffff) + npf; 769264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 770264Sbill mask = up->upec2; 7713445Sroot #ifdef UPECCDEBUG 7723403Swnj printf("npf %d reg %x o %d mask %o pos %d\n", npf, reg, o, mask, 7733403Swnj up->upec1); 7743445Sroot #endif 7759548Ssam bn = dkblock(bp); 7769548Ssam st = &upst[ui->ui_type]; 7779548Ssam cn = bp->b_cylin; 7789548Ssam sn = bn%st->nspc + npf; 7799548Ssam tn = sn/st->nsect; 7809548Ssam sn %= st->nsect; 7819548Ssam cn += tn/st->ntrak; 7829548Ssam tn %= st->ntrak; 7832725Swnj ubapurge(um); 7849548Ssam um->um_tab.b_active=2; 785266Sbill /* 7869548Ssam * action taken depends on the flag 787266Sbill */ 7889548Ssam switch(flag){ 7899548Ssam case ECC: 7909548Ssam npf--; 7919548Ssam reg--; 7929548Ssam mask = up->upec2; 7939548Ssam printf("up%d%c: soft ecc sn%d\n", dkunit(bp), 7949548Ssam 'a'+(minor(bp->b_dev)&07), bp->b_blkno + npf); 7959548Ssam /* 7969548Ssam * Flush the buffered data path, and compute the 7979548Ssam * byte and bit position of the error. The variable i 7989548Ssam * is the byte offset in the transfer, the variable byte 7999548Ssam * is the offset from a page boundary in main memory. 8009548Ssam */ 8019548Ssam i = up->upec1 - 1; /* -1 makes 0 origin */ 8029548Ssam bit = i&07; 8039548Ssam i = (i&~07)>>3; 8049548Ssam byte = i + o; 8059548Ssam /* 8069548Ssam * Correct while possible bits remain of mask. Since mask 8079548Ssam * contains 11 bits, we continue while the bit offset is > -11. 8089548Ssam * Also watch out for end of this block and the end of the whole 8099548Ssam * transfer. 8109548Ssam */ 8119548Ssam while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 8129548Ssam addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 8139548Ssam (byte & PGOFSET); 8143445Sroot #ifdef UPECCDEBUG 8159548Ssam printf("addr %x map reg %x\n", 8169548Ssam addr, *(int *)(&ubp->uba_map[reg+btop(byte)])); 8179548Ssam printf("old: %x, ", getmemc(addr)); 8183445Sroot #endif 8199548Ssam putmemc(addr, getmemc(addr)^(mask<<bit)); 8203445Sroot #ifdef UPECCDEBUG 8219548Ssam printf("new: %x\n", getmemc(addr)); 8223445Sroot #endif 8239548Ssam byte++; 8249548Ssam i++; 8259580Shelge bit -= 8; 8269548Ssam } 8279548Ssam if (up->upwc == 0) 8289548Ssam return (0); 8299548Ssam npf++; 8309548Ssam reg++; 8319548Ssam break; 8329548Ssam case BSE: 8339548Ssam /* 8349548Ssam * if not in bad sector table, return 0 8359548Ssam */ 8369548Ssam if ((bn = isbad(&upbad[ui->ui_unit], cn, tn, sn)) < 0) 8379548Ssam return(0); 8389548Ssam /* 8399548Ssam * flag this one as bad 8409548Ssam */ 8419548Ssam bp->b_flags |= B_BAD; 8429548Ssam bp->b_error = npf + 1; 8439548Ssam #ifdef UPECCDEBUG 8449548Ssam printf("BSE: restart at %d\n",npf+1); 8459548Ssam #endif 8469548Ssam bn = st->ncyl * st->nspc -st->nsect - 1 - bn; 8479548Ssam cn = bn / st->nspc; 8489548Ssam sn = bn % st->nspc; 8499548Ssam tn = sn / st->nsect; 8509548Ssam sn %= st->nsect; 8519548Ssam up->upwc = -(512 / sizeof (short)); 8529548Ssam #ifdef UPECCDEBUG 8539548Ssam printf("revector to cn %d tn %d sn %d\n", cn, tn, sn); 8549548Ssam #endif 8559548Ssam break; 8569548Ssam case CONT: 8579548Ssam #ifdef UPECCDEBUG 8589548Ssam printf("upecc, CONT: bn %d cn %d tn %d sn %d\n", bn, cn, tn, sn); 8599548Ssam #endif 8609548Ssam bp->b_flags &= ~B_BAD; 8619548Ssam up->upwc = -((bp->b_bcount - (int)ptob(npf)) / sizeof(short)); 8629548Ssam if (up->upwc == 0) 8639548Ssam return(0); 8649548Ssam break; 865264Sbill } 8667183Sroot if (up->upwc == 0) { 8677183Sroot um->um_tab.b_active = 0; 868264Sbill return (0); 8697183Sroot } 870266Sbill /* 871266Sbill * Have to continue the transfer... clear the drive, 872266Sbill * and compute the position where the transfer is to continue. 873266Sbill * We have completed npf+1 sectors of the transfer already; 874266Sbill * restart at offset o of next sector (i.e. in UBA register reg+1). 875266Sbill */ 8762629Swnj #ifdef notdef 8772629Swnj up->uper1 = 0; 8782629Swnj up->upcs1 |= UP_GO; 8792629Swnj #else 8802629Swnj up->upcs1 = UP_TRE|UP_IE|UP_DCLR|UP_GO; 881264Sbill up->updc = cn; 882266Sbill up->upda = (tn << 8) | sn; 8839548Ssam ubaddr = (int)ptob(reg) + o; 884266Sbill up->upba = ubaddr; 885266Sbill cmd = (ubaddr >> 8) & 0x300; 8869548Ssam cmd |= ((bp->b_flags&B_READ)?UP_RCOM:UP_WCOM)|UP_IE|UP_GO; 8879548Ssam um->um_tab.b_errcnt = 0; 888266Sbill up->upcs1 = cmd; 8892629Swnj #endif 890264Sbill return (1); 891264Sbill } 892286Sbill 893286Sbill /* 894286Sbill * Reset driver after UBA init. 895286Sbill * Cancel software state of all pending transfers 896286Sbill * and restart all units and the controller. 897286Sbill */ 8982395Swnj upreset(uban) 8992931Swnj int uban; 900286Sbill { 9012983Swnj register struct uba_ctlr *um; 9022983Swnj register struct uba_device *ui; 9032395Swnj register sc21, unit; 904286Sbill 9052646Swnj for (sc21 = 0; sc21 < NSC; sc21++) { 9062470Swnj if ((um = upminfo[sc21]) == 0 || um->um_ubanum != uban || 9072470Swnj um->um_alive == 0) 9082395Swnj continue; 9092931Swnj printf(" sc%d", sc21); 9102395Swnj um->um_tab.b_active = 0; 9112395Swnj um->um_tab.b_actf = um->um_tab.b_actl = 0; 9122931Swnj up_softc[sc21].sc_recal = 0; 9136346Swnj up_softc[sc21].sc_wticks = 0; 9142571Swnj if (um->um_ubinfo) { 9152571Swnj printf("<%d>", (um->um_ubinfo>>28)&0xf); 9169357Ssam um->um_ubinfo = 0; 9172395Swnj } 9183445Sroot ((struct updevice *)(um->um_addr))->upcs2 = UPCS2_CLR; 9192395Swnj for (unit = 0; unit < NUP; unit++) { 9202395Swnj if ((ui = updinfo[unit]) == 0) 9212395Swnj continue; 9222931Swnj if (ui->ui_alive == 0 || ui->ui_mi != um) 9232395Swnj continue; 9242395Swnj uputab[unit].b_active = 0; 9252395Swnj (void) upustart(ui); 9262395Swnj } 9272395Swnj (void) upstart(um); 928286Sbill } 929286Sbill } 930313Sbill 931313Sbill /* 932313Sbill * Wake up every second and if an interrupt is pending 933313Sbill * but nothing has happened increment a counter. 9342931Swnj * If nothing happens for 20 seconds, reset the UNIBUS 935313Sbill * and begin anew. 936313Sbill */ 937313Sbill upwatch() 938313Sbill { 9392983Swnj register struct uba_ctlr *um; 9402395Swnj register sc21, unit; 9412470Swnj register struct up_softc *sc; 942313Sbill 9432759Swnj timeout(upwatch, (caddr_t)0, hz); 9442646Swnj for (sc21 = 0; sc21 < NSC; sc21++) { 9452395Swnj um = upminfo[sc21]; 9462470Swnj if (um == 0 || um->um_alive == 0) 9472470Swnj continue; 9482470Swnj sc = &up_softc[sc21]; 9492395Swnj if (um->um_tab.b_active == 0) { 9502395Swnj for (unit = 0; unit < NUP; unit++) 9512629Swnj if (uputab[unit].b_active && 9522629Swnj updinfo[unit]->ui_mi == um) 9532395Swnj goto active; 9542470Swnj sc->sc_wticks = 0; 9552395Swnj continue; 9562395Swnj } 9572931Swnj active: 9582470Swnj sc->sc_wticks++; 9592470Swnj if (sc->sc_wticks >= 20) { 9602470Swnj sc->sc_wticks = 0; 9612931Swnj printf("sc%d: lost interrupt\n", sc21); 9622646Swnj ubareset(um->um_ubanum); 9632395Swnj } 964313Sbill } 965313Sbill } 9662379Swnj 9672379Swnj #define DBSIZE 20 9682379Swnj 9692379Swnj updump(dev) 9702379Swnj dev_t dev; 9712379Swnj { 9722629Swnj struct updevice *upaddr; 9732379Swnj char *start; 9743107Swnj int num, blk, unit; 9752379Swnj struct size *sizes; 9762395Swnj register struct uba_regs *uba; 9772983Swnj register struct uba_device *ui; 9782379Swnj register short *rp; 9792395Swnj struct upst *st; 9806848Ssam register int retry; 9812379Swnj 9822395Swnj unit = minor(dev) >> 3; 9832889Swnj if (unit >= NUP) 9842889Swnj return (ENXIO); 9852470Swnj #define phys(cast, addr) ((cast)((int)addr & 0x7fffffff)) 9862983Swnj ui = phys(struct uba_device *, updinfo[unit]); 9872889Swnj if (ui->ui_alive == 0) 9882889Swnj return (ENXIO); 9892395Swnj uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba; 9902983Swnj ubainit(uba); 9912629Swnj upaddr = (struct updevice *)ui->ui_physaddr; 9926848Ssam DELAY(5000000); 9932379Swnj num = maxfree; 9942379Swnj upaddr->upcs2 = unit; 9952983Swnj DELAY(100); 9966848Ssam upaddr->upcs1 = UP_DCLR|UP_GO; 9976848Ssam upaddr->upcs1 = UP_PRESET|UP_GO; 9986848Ssam upaddr->upof = UPOF_FMT22; 9996848Ssam retry = 0; 10006848Ssam do { 10016848Ssam DELAY(25); 10026848Ssam if (++retry > 527) 10036848Ssam break; 10046861Ssam } while ((upaddr->upds & UP_RDY) == 0); 10053445Sroot if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) 10062889Swnj return (EFAULT); 10079357Ssam start = 0; 10088489Sroot st = &upst[ui->ui_type]; 10092395Swnj sizes = phys(struct size *, st->sizes); 10102889Swnj if (dumplo < 0 || dumplo + num >= sizes[minor(dev)&07].nblocks) 10112889Swnj return (EINVAL); 10122379Swnj while (num > 0) { 10132379Swnj register struct pte *io; 10142379Swnj register int i; 10152379Swnj int cn, sn, tn; 10162379Swnj daddr_t bn; 10172379Swnj 10182379Swnj blk = num > DBSIZE ? DBSIZE : num; 10192395Swnj io = uba->uba_map; 10202379Swnj for (i = 0; i < blk; i++) 10212983Swnj *(int *)io++ = (btop(start)+i) | (1<<21) | UBAMR_MRV; 10222379Swnj *(int *)io = 0; 10232379Swnj bn = dumplo + btop(start); 10242607Swnj cn = bn/st->nspc + sizes[minor(dev)&07].cyloff; 10252607Swnj sn = bn%st->nspc; 10262607Swnj tn = sn/st->nsect; 10272607Swnj sn = sn%st->nsect; 10282379Swnj upaddr->updc = cn; 10292379Swnj rp = (short *) &upaddr->upda; 10302379Swnj *rp = (tn << 8) + sn; 10312379Swnj *--rp = 0; 10322379Swnj *--rp = -blk*NBPG / sizeof (short); 10332629Swnj *--rp = UP_GO|UP_WCOM; 10346848Ssam retry = 0; 10352379Swnj do { 10362379Swnj DELAY(25); 10376848Ssam if (++retry > 527) 10386848Ssam break; 10392629Swnj } while ((upaddr->upcs1 & UP_RDY) == 0); 10406848Ssam if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { 10416861Ssam printf("up%d: not ready", unit); 10426848Ssam if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { 10436848Ssam printf("\n"); 10446848Ssam return (EIO); 10456848Ssam } 10466848Ssam printf(" (flakey)\n"); 10476848Ssam } 10483445Sroot if (upaddr->upds&UPDS_ERR) 10492889Swnj return (EIO); 10502379Swnj start += blk*NBPG; 10512379Swnj num -= blk; 10522379Swnj } 10532379Swnj return (0); 10542379Swnj } 10551902Swnj #endif 1056