1 /* rk.c 4.27 81/03/09 */ 2 3 #include "rk.h" 4 #if NHK > 0 5 int rkpip; /* DEBUG */ 6 int rknosval; /* DEBUG */ 7 /* 8 * RK11/RK07 disk driver 9 * 10 * This driver mimics up.c; see it for an explanation of common code. 11 * 12 * TODO: 13 * Add reading of bad sector information and disk layout from sector 1 14 * Add bad sector forwarding code 15 * Why do we lose an interrupt sometime when spinning drives down? 16 */ 17 #include "../h/param.h" 18 #include "../h/systm.h" 19 #include "../h/buf.h" 20 #include "../h/conf.h" 21 #include "../h/dir.h" 22 #include "../h/user.h" 23 #include "../h/pte.h" 24 #include "../h/map.h" 25 #include "../h/vm.h" 26 #include "../h/ubareg.h" 27 #include "../h/ubavar.h" 28 #include "../h/dk.h" 29 #include "../h/cpu.h" 30 #include "../h/cmap.h" 31 32 #include "../h/rkreg.h" 33 34 struct rk_softc { 35 int sc_softas; 36 int sc_ndrive; 37 int sc_wticks; 38 int sc_recal; 39 } rk_softc[NHK]; 40 41 /* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */ 42 struct size { 43 daddr_t nblocks; 44 int cyloff; 45 } rk7_sizes[] ={ 46 15884, 0, /* A=cyl 0 thru 240 */ 47 10032, 241, /* B=cyl 241 thru 392 */ 48 53790, 0, /* C=cyl 0 thru 814 */ 49 0, 0, 50 0, 0, 51 0, 0, 52 27786, 393, /* G=cyl 393 thru 813 */ 53 0, 0, 54 }; 55 /* END OF STUFF WHICH SHOULD BE READ IN PER DISK */ 56 57 int rkprobe(), rkslave(), rkattach(), rkdgo(), rkintr(); 58 struct uba_ctlr *rkminfo[NHK]; 59 struct uba_device *rkdinfo[NRK]; 60 struct uba_device *rkip[NHK][4]; 61 62 u_short rkstd[] = { 0777440, 0 }; 63 struct uba_driver hkdriver = 64 { rkprobe, rkslave, rkattach, rkdgo, rkstd, "rk", rkdinfo, "hk", rkminfo, 1 }; 65 struct buf rkutab[NRK]; 66 short rkcyl[NRK]; 67 68 struct rkst { 69 short nsect; 70 short ntrak; 71 short nspc; 72 short ncyl; 73 struct size *sizes; 74 } rkst[] = { 75 NRKSECT, NRKTRK, NRKSECT*NRKTRK, NRK7CYL, rk7_sizes, 76 }; 77 78 u_char rk_offset[16] = 79 { P400,M400,P400,M400,P800,M800,P800,M800,P1200,M1200,P1200,M1200,0,0,0,0 }; 80 81 struct buf rrkbuf[NRK]; 82 83 #define b_cylin b_resid 84 85 #ifdef INTRLVE 86 daddr_t dkblock(); 87 #endif 88 89 int rkwstart, rkwatch(); 90 91 rkprobe(reg) 92 caddr_t reg; 93 { 94 register int br, cvec; 95 96 #ifdef lint 97 br = 0; cvec = br; br = cvec; 98 #endif 99 ((struct rkdevice *)reg)->rkcs1 = RK_CDT|RK_IE|RK_CRDY; 100 DELAY(10); 101 ((struct rkdevice *)reg)->rkcs1 = RK_CDT; 102 return (1); 103 } 104 105 rkslave(ui, reg) 106 struct uba_device *ui; 107 caddr_t reg; 108 { 109 register struct rkdevice *rkaddr = (struct rkdevice *)reg; 110 111 rkaddr->rkcs1 = RK_CDT|RK_CCLR; 112 rkaddr->rkcs2 = ui->ui_slave; 113 rkaddr->rkcs1 = RK_CDT|RK_DCLR|RK_GO; 114 rkwait(rkaddr); 115 DELAY(50); 116 if (rkaddr->rkcs2&RK_NED || (rkaddr->rkds&RK_SVAL) == 0) { 117 rkaddr->rkcs1 = RK_CDT|RK_CCLR; 118 return (0); 119 } 120 return (1); 121 } 122 123 rkattach(ui) 124 register struct uba_device *ui; 125 { 126 127 if (rkwstart == 0) { 128 timeout(rkwatch, (caddr_t)0, hz); 129 rkwstart++; 130 } 131 if (ui->ui_dk >= 0) 132 dk_mspw[ui->ui_dk] = 1.0 / (60 * NRKSECT * 256); 133 rkip[ui->ui_ctlr][ui->ui_slave] = ui; 134 rk_softc[ui->ui_ctlr].sc_ndrive++; 135 rkcyl[ui->ui_unit] = -1; 136 } 137 138 rkstrategy(bp) 139 register struct buf *bp; 140 { 141 register struct uba_device *ui; 142 register struct rkst *st; 143 register int unit; 144 register struct buf *dp; 145 int xunit = minor(bp->b_dev) & 07; 146 long bn, sz; 147 148 sz = (bp->b_bcount+511) >> 9; 149 unit = dkunit(bp); 150 if (unit >= NRK) 151 goto bad; 152 ui = rkdinfo[unit]; 153 if (ui == 0 || ui->ui_alive == 0) 154 goto bad; 155 st = &rkst[ui->ui_type]; 156 if (bp->b_blkno < 0 || 157 (bn = dkblock(bp))+sz > st->sizes[xunit].nblocks) 158 goto bad; 159 bp->b_cylin = bn/st->nspc + st->sizes[xunit].cyloff; 160 (void) spl5(); 161 dp = &rkutab[ui->ui_unit]; 162 disksort(dp, bp); 163 if (dp->b_active == 0) { 164 (void) rkustart(ui); 165 bp = &ui->ui_mi->um_tab; 166 if (bp->b_actf && bp->b_active == 0) 167 (void) rkstart(ui->ui_mi); 168 } 169 (void) spl0(); 170 return; 171 172 bad: 173 bp->b_flags |= B_ERROR; 174 iodone(bp); 175 return; 176 } 177 178 rkustart(ui) 179 register struct uba_device *ui; 180 { 181 register struct buf *bp, *dp; 182 register struct uba_ctlr *um; 183 register struct rkdevice *rkaddr; 184 int didie = 0; 185 186 if (ui == 0) 187 return (0); 188 dk_busy &= ~(1<<ui->ui_dk); 189 dp = &rkutab[ui->ui_unit]; 190 um = ui->ui_mi; 191 rkaddr = (struct rkdevice *)um->um_addr; 192 if (um->um_tab.b_active) { 193 rk_softc[um->um_ctlr].sc_softas |= 1<<ui->ui_slave; 194 return (0); 195 } 196 rkaddr->rkcs1 = RK_CDT|RK_CERR; 197 rkaddr->rkcs2 = ui->ui_slave; 198 rkaddr->rkcs1 = RK_CDT|RK_DCLR|RK_GO; 199 rkwait(rkaddr); 200 if ((bp = dp->b_actf) == NULL) { 201 rkaddr->rkcs1 = RK_CDT|RK_DCLR|RK_GO; 202 rkwait(rkaddr); 203 return (0); 204 } 205 if ((rkaddr->rkds & RK_VV) == 0) { 206 /* SHOULD WARN SYSTEM THAT THIS HAPPENED */ 207 rkaddr->rkcs1 = RK_CDT|RK_PACK|RK_GO; 208 rkwait(rkaddr); 209 } 210 if (dp->b_active) 211 goto done; 212 dp->b_active = 1; 213 if ((rkaddr->rkds & RK_DREADY) != RK_DREADY) 214 goto done; 215 if (rk_softc[um->um_ctlr].sc_ndrive == 1) 216 goto done; 217 if (bp->b_cylin == rkcyl[ui->ui_unit]) 218 goto done; 219 rkaddr->rkcyl = bp->b_cylin; 220 rkcyl[ui->ui_unit] = bp->b_cylin; 221 rkaddr->rkcs1 = RK_CDT|RK_IE|RK_SEEK|RK_GO; 222 didie = 1; 223 if (ui->ui_dk >= 0) { 224 dk_busy |= 1<<ui->ui_dk; 225 dk_seek[ui->ui_dk]++; 226 } 227 goto out; 228 done: 229 if (dp->b_active != 2) { 230 dp->b_forw = NULL; 231 if (um->um_tab.b_actf == NULL) 232 um->um_tab.b_actf = dp; 233 else 234 um->um_tab.b_actl->b_forw = dp; 235 um->um_tab.b_actl = dp; 236 dp->b_active = 2; 237 } 238 out: 239 return (didie); 240 } 241 242 rkstart(um) 243 register struct uba_ctlr *um; 244 { 245 register struct buf *bp, *dp; 246 register struct uba_device *ui; 247 register struct rkdevice *rkaddr; 248 struct rkst *st; 249 daddr_t bn; 250 int sn, tn, cmd; 251 252 loop: 253 if ((dp = um->um_tab.b_actf) == NULL) 254 return (0); 255 if ((bp = dp->b_actf) == NULL) { 256 um->um_tab.b_actf = dp->b_forw; 257 goto loop; 258 } 259 um->um_tab.b_active++; 260 ui = rkdinfo[dkunit(bp)]; 261 bn = dkblock(bp); 262 st = &rkst[ui->ui_type]; 263 sn = bn%st->nspc; 264 tn = sn/st->nsect; 265 sn %= st->nsect; 266 rkaddr = (struct rkdevice *)ui->ui_addr; 267 retry: 268 rkaddr->rkcs1 = RK_CDT|RK_CERR; 269 rkaddr->rkcs2 = ui->ui_slave; 270 rkaddr->rkcs1 = RK_CDT|RK_DCLR|RK_GO; 271 rkwait(rkaddr); 272 if ((rkaddr->rkds&RK_SVAL) == 0) { 273 rknosval++; 274 goto nosval; 275 } 276 if (rkaddr->rkds&RK_PIP) { 277 rkpip++; 278 goto retry; 279 } 280 if ((rkaddr->rkds&RK_DREADY) != RK_DREADY) { 281 printf("rk%d: not ready", dkunit(bp)); 282 if ((rkaddr->rkds&RK_DREADY) != RK_DREADY) { 283 printf("\n"); 284 rkaddr->rkcs1 = RK_CDT|RK_DCLR|RK_GO; 285 rkwait(rkaddr); 286 rkaddr->rkcs1 = RK_CDT|RK_CERR; 287 rkwait(rkaddr); 288 um->um_tab.b_active = 0; 289 um->um_tab.b_errcnt = 0; 290 dp->b_actf = bp->av_forw; 291 dp->b_active = 0; 292 bp->b_flags |= B_ERROR; 293 iodone(bp); 294 goto loop; 295 } 296 printf(" (came back!)\n"); 297 } 298 nosval: 299 rkaddr->rkcyl = bp->b_cylin; 300 rkcyl[ui->ui_unit] = bp->b_cylin; 301 rkaddr->rkda = (tn << 8) + sn; 302 rkaddr->rkwc = -bp->b_bcount / sizeof (short); 303 if (bp->b_flags & B_READ) 304 cmd = RK_CDT|RK_IE|RK_READ|RK_GO; 305 else 306 cmd = RK_CDT|RK_IE|RK_WRITE|RK_GO; 307 um->um_cmd = cmd; 308 (void) ubago(ui); 309 return (1); 310 } 311 312 rkdgo(um) 313 register struct uba_ctlr *um; 314 { 315 register struct rkdevice *rkaddr = (struct rkdevice *)um->um_addr; 316 317 rkaddr->rkba = um->um_ubinfo; 318 rkaddr->rkcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x300); 319 } 320 321 rkintr(rk11) 322 int rk11; 323 { 324 register struct uba_ctlr *um = rkminfo[rk11]; 325 register struct uba_device *ui; 326 register struct rkdevice *rkaddr = (struct rkdevice *)um->um_addr; 327 register struct buf *bp, *dp; 328 int unit; 329 struct rk_softc *sc = &rk_softc[um->um_ctlr]; 330 int as = (rkaddr->rkatt >> 8) | sc->sc_softas; 331 int needie = 1; 332 333 sc->sc_wticks = 0; 334 sc->sc_softas = 0; 335 if (um->um_tab.b_active) { 336 ubadone(um); 337 dp = um->um_tab.b_actf; 338 bp = dp->b_actf; 339 ui = rkdinfo[dkunit(bp)]; 340 dk_busy &= ~(1 << ui->ui_dk); 341 if (rkaddr->rkcs1 & RK_CERR) { 342 int recal; 343 u_short ds = rkaddr->rkds; 344 u_short cs2 = rkaddr->rkcs2; 345 u_short er = rkaddr->rker; 346 if (ds & RK_WLE) { 347 printf("rk%d: write locked\n", dkunit(bp)); 348 bp->b_flags |= B_ERROR; 349 } else if (++um->um_tab.b_errcnt > 28 || 350 ds&RKDS_HARD || er&RKER_HARD || cs2&RKCS2_HARD) { 351 harderr(bp, "rk"); 352 printf("cs2=%b ds=%b er=%b\n", 353 cs2, RKCS2_BITS, ds, 354 RKDS_BITS, er, RKER_BITS); 355 bp->b_flags |= B_ERROR; 356 sc->sc_recal = 0; 357 } else 358 um->um_tab.b_active = 0; 359 if (cs2&RK_MDS) { 360 rkaddr->rkcs2 = RK_SCLR; 361 goto retry; 362 } 363 recal = 0; 364 if (ds&RK_DROT || er&(RK_OPI|RK_SKI|RK_UNS) || 365 (um->um_tab.b_errcnt&07) == 4) 366 recal = 1; 367 if ((er & (RK_DCK|RK_ECH)) == RK_DCK) 368 if (rkecc(ui)) 369 return; 370 rkaddr->rkcs1 = RK_CDT|RK_CCLR; 371 rkaddr->rkcs2 = ui->ui_slave; 372 rkaddr->rkcs1 = RK_CDT|RK_DCLR|RK_GO; 373 rkwait(rkaddr); 374 if (recal && um->um_tab.b_active == 0) { 375 rkaddr->rkcs1 = RK_CDT|RK_IE|RK_RECAL|RK_GO; 376 rkcyl[ui->ui_unit] = -1; 377 sc->sc_recal = 0; 378 goto nextrecal; 379 } 380 } 381 retry: 382 switch (sc->sc_recal) { 383 384 case 1: 385 rkaddr->rkcyl = bp->b_cylin; 386 rkcyl[ui->ui_unit] = bp->b_cylin; 387 rkaddr->rkcs1 = RK_CDT|RK_IE|RK_SEEK|RK_GO; 388 goto nextrecal; 389 390 case 2: 391 if (um->um_tab.b_errcnt < 16 || 392 (bp->b_flags&B_READ) != 0) 393 break; 394 rkaddr->rkatt = rk_offset[um->um_tab.b_errcnt & 017]; 395 rkaddr->rkcs1 = RK_CDT|RK_IE|RK_OFFSET|RK_GO; 396 /* fall into ... */ 397 nextrecal: 398 sc->sc_recal++; 399 rkwait(rkaddr); 400 um->um_tab.b_active = 1; 401 return; 402 403 case 3: 404 sc->sc_recal = 0; 405 um->um_tab.b_active = 0; 406 break; 407 } 408 if (um->um_tab.b_active) { 409 um->um_tab.b_active = 0; 410 um->um_tab.b_errcnt = 0; 411 um->um_tab.b_actf = dp->b_forw; 412 dp->b_active = 0; 413 dp->b_errcnt = 0; 414 dp->b_actf = bp->av_forw; 415 bp->b_resid = -rkaddr->rkwc * sizeof(short); 416 iodone(bp); 417 if (dp->b_actf) 418 if (rkustart(ui)) 419 needie = 0; 420 } 421 as &= ~(1<<ui->ui_slave); 422 } 423 for (unit = 0; as; as >>= 1, unit++) 424 if (as & 1) { 425 ui = rkip[rk11][unit]; 426 if (ui) { 427 if (rkustart(rkip[rk11][unit])) 428 needie = 0; 429 } else { 430 rkaddr->rkcs1 = RK_CERR|RK_CDT; 431 rkaddr->rkcs2 = unit; 432 rkaddr->rkcs1 = RK_CDT|RK_DCLR|RK_GO; 433 rkwait(rkaddr); 434 } 435 } 436 if (um->um_tab.b_actf && um->um_tab.b_active == 0) 437 if (rkstart(um)) 438 needie = 0; 439 if (needie) 440 rkaddr->rkcs1 = RK_CDT|RK_IE; 441 } 442 443 rkwait(addr) 444 register struct rkdevice *addr; 445 { 446 447 while ((addr->rkcs1 & RK_CRDY) == 0) 448 ; 449 } 450 451 rkread(dev) 452 dev_t dev; 453 { 454 register int unit = minor(dev) >> 3; 455 456 if (unit >= NRK) 457 u.u_error = ENXIO; 458 else 459 physio(rkstrategy, &rrkbuf[unit], dev, B_READ, minphys); 460 } 461 462 rkwrite(dev) 463 dev_t dev; 464 { 465 register int unit = minor(dev) >> 3; 466 467 if (unit >= NRK) 468 u.u_error = ENXIO; 469 else 470 physio(rkstrategy, &rrkbuf[unit], dev, B_WRITE, minphys); 471 } 472 473 rkecc(ui) 474 register struct uba_device *ui; 475 { 476 register struct rkdevice *rk = (struct rkdevice *)ui->ui_addr; 477 register struct buf *bp = rkutab[ui->ui_unit].b_actf; 478 register struct uba_ctlr *um = ui->ui_mi; 479 register struct rkst *st; 480 struct uba_regs *ubp = ui->ui_hd->uh_uba; 481 register int i; 482 caddr_t addr; 483 int reg, bit, byte, npf, mask, o, cmd, ubaddr; 484 int bn, cn, tn, sn; 485 486 npf = btop((rk->rkwc * sizeof(short)) + bp->b_bcount) - 1; 487 reg = btop(um->um_ubinfo&0x3ffff) + npf; 488 o = (int)bp->b_un.b_addr & PGOFSET; 489 printf("rk%d%c: soft ecc sn%d\n", dkunit(bp), 490 'a'+(minor(bp->b_dev)&07), bp->b_blkno + npf); 491 mask = rk->rkec2; 492 ubapurge(um); 493 i = rk->rkec1 - 1; /* -1 makes 0 origin */ 494 bit = i&07; 495 i = (i&~07)>>3; 496 byte = i + o; 497 while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 498 addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 499 (byte & PGOFSET); 500 putmemc(addr, getmemc(addr)^(mask<<bit)); 501 byte++; 502 i++; 503 bit -= 8; 504 } 505 um->um_tab.b_active++; /* Either complete or continuing... */ 506 if (rk->rkwc == 0) 507 return (0); 508 #ifdef notdef 509 rk->rkcs1 |= RK_GO; 510 #else 511 rk->rkcs1 = RK_CDT|RK_CCLR; 512 rk->rkcs2 = ui->ui_slave; 513 rk->rkcs1 = RK_CDT|RK_DCLR|RK_GO; 514 rkwait(rk); 515 bn = dkblock(bp); 516 st = &rkst[ui->ui_type]; 517 cn = bp->b_cylin; 518 sn = bn%st->nspc + npf + 1; 519 tn = sn/st->nsect; 520 sn %= st->nsect; 521 cn += tn/st->ntrak; 522 tn %= st->ntrak; 523 rk->rkcyl = cn; 524 rk->rkda = (tn << 8) | sn; 525 ubaddr = (int)ptob(reg+1) + o; 526 rk->rkba = ubaddr; 527 cmd = (ubaddr >> 8) & 0x300; 528 cmd |= RK_CDT|RK_IE|RK_GO|RK_READ; 529 rk->rkcs1 = cmd; 530 #endif 531 return (1); 532 } 533 534 rkreset(uban) 535 int uban; 536 { 537 register struct uba_ctlr *um; 538 register struct uba_device *ui; 539 register rk11, unit; 540 541 for (rk11 = 0; rk11 < NHK; rk11++) { 542 if ((um = rkminfo[rk11]) == 0 || um->um_ubanum != uban || 543 um->um_alive == 0) 544 continue; 545 printf(" hk%d", rk11); 546 um->um_tab.b_active = 0; 547 um->um_tab.b_actf = um->um_tab.b_actl = 0; 548 rk_softc[um->um_ctlr].sc_recal = 0; 549 if (um->um_ubinfo) { 550 printf("<%d>", (um->um_ubinfo>>28)&0xf); 551 ubadone(um); 552 } 553 for (unit = 0; unit < NHK; unit++) { 554 if ((ui = rkdinfo[unit]) == 0) 555 continue; 556 if (ui->ui_alive == 0) 557 continue; 558 rkutab[unit].b_active = 0; 559 (void) rkustart(ui); 560 } 561 (void) rkstart(um); 562 } 563 } 564 565 rkwatch() 566 { 567 register struct uba_ctlr *um; 568 register rk11, unit; 569 register struct rk_softc *sc; 570 571 timeout(rkwatch, (caddr_t)0, hz); 572 for (rk11 = 0; rk11 < NHK; rk11++) { 573 um = rkminfo[rk11]; 574 if (um == 0 || um->um_alive == 0) 575 continue; 576 sc = &rk_softc[rk11]; 577 if (um->um_tab.b_active == 0) { 578 for (unit = 0; unit < NRK; unit++) 579 if (rkutab[unit].b_active && 580 rkdinfo[unit]->ui_mi == um) 581 goto active; 582 sc->sc_wticks = 0; 583 continue; 584 } 585 active: 586 sc->sc_wticks++; 587 if (sc->sc_wticks >= 20) { 588 sc->sc_wticks = 0; 589 printf("hk%d: lost interrupt\n", rk11); 590 ubareset(um->um_ubanum); 591 } 592 } 593 } 594 595 #define DBSIZE 20 596 597 rkdump(dev) 598 dev_t dev; 599 { 600 struct rkdevice *rkaddr; 601 char *start; 602 int num, blk, unit; 603 struct size *sizes; 604 register struct uba_regs *uba; 605 register struct uba_device *ui; 606 register short *rp; 607 struct rkst *st; 608 609 unit = minor(dev) >> 3; 610 if (unit >= NRK) 611 return (ENXIO); 612 #define phys(cast, addr) ((cast)((int)addr & 0x7fffffff)) 613 ui = phys(struct uba_device *, rkdinfo[unit]); 614 if (ui->ui_alive == 0) 615 return (ENXIO); 616 uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba; 617 #if VAX780 618 if (cpu == VAX_780) 619 ubainit(uba); 620 #endif 621 rkaddr = (struct rkdevice *)ui->ui_physaddr; 622 num = maxfree; 623 start = 0; 624 rkaddr->rkcs1 = RK_CDT|RK_CERR; 625 rkaddr->rkcs2 = unit; 626 rkaddr->rkcs1 = RK_CDT|RK_DCLR|RK_GO; 627 rkwait(rkaddr); 628 if ((rkaddr->rkds & RK_VV) == 0) { 629 rkaddr->rkcs1 = RK_CDT|RK_IE|RK_PACK|RK_GO; 630 rkwait(rkaddr); 631 } 632 st = &rkst[ui->ui_type]; 633 sizes = phys(struct size *, st->sizes); 634 if (dumplo < 0 || dumplo + num >= sizes[minor(dev)&07].nblocks) 635 return (EINVAL); 636 while (num > 0) { 637 register struct pte *io; 638 register int i; 639 int cn, sn, tn; 640 daddr_t bn; 641 642 blk = num > DBSIZE ? DBSIZE : num; 643 io = uba->uba_map; 644 for (i = 0; i < blk; i++) 645 *(int *)io++ = (btop(start)+i) | (1<<21) | UBAMR_MRV; 646 *(int *)io = 0; 647 bn = dumplo + btop(start); 648 cn = bn/st->nspc + sizes[minor(dev)&07].cyloff; 649 sn = bn%st->nspc; 650 tn = sn/st->nsect; 651 sn = sn%st->nsect; 652 rkaddr->rkcyl = cn; 653 rp = (short *) &rkaddr->rkda; 654 *rp = (tn << 8) + sn; 655 *--rp = 0; 656 *--rp = -blk*NBPG / sizeof (short); 657 *--rp = RK_CDT|RK_GO|RK_WRITE; 658 rkwait(rkaddr); 659 if (rkaddr->rkcs1 & RK_CERR) 660 return (EIO); 661 start += blk*NBPG; 662 num -= blk; 663 } 664 return (0); 665 } 666 #endif 667