1 /* vd.c 1.16 87/03/01 */ 2 3 #include "dk.h" 4 #if NVD > 0 5 /* 6 * Versabus VDDC/SMDE driver. 7 */ 8 #include "param.h" 9 #include "buf.h" 10 #include "cmap.h" 11 #include "conf.h" 12 #include "dir.h" 13 #include "dkstat.h" 14 #include "disklabel.h" 15 #include "map.h" 16 #include "file.h" 17 #include "systm.h" 18 #include "user.h" 19 #include "vmmac.h" 20 #include "proc.h" 21 #include "uio.h" 22 #include "syslog.h" 23 #include "kernel.h" 24 #include "ioctl.h" 25 26 #include "../tahoe/cpu.h" 27 #include "../tahoe/mtpr.h" 28 #include "../tahoe/pte.h" 29 30 #include "../tahoevba/vbavar.h" 31 #include "../tahoevba/vdreg.h" 32 33 #define COMPAT_42 34 35 #define VDMAXIO (MAXBPTE*NBPG) 36 37 #define vdunit(dev) (minor(dev) >> 3) 38 #define vdpart(dev) (minor(dev) & 0x07) 39 #define vdminor(unit,part) (((unit) << 3) | (part)) 40 41 struct vba_ctlr *vdminfo[NVD]; 42 struct vba_device *vddinfo[NDK]; 43 int vdprobe(), vdslave(), vdattach(), vddgo(); 44 long vdaddr[] = { 0xffff2000, 0xffff2100, 0xffff2200, 0xffff2300, 0 }; 45 struct vba_driver vddriver = 46 { vdprobe, vdslave, vdattach, vddgo, vdaddr, "dk", vddinfo, "vd", vdminfo }; 47 48 /* 49 * Per-controller state. 50 */ 51 struct vdsoftc { 52 u_short vd_flags; 53 #define VD_INIT 0x1 /* controller initialized */ 54 #define VD_STARTED 0x2 /* start command issued */ 55 #define VD_DOSEEKS 0x4 /* should overlap seeks */ 56 u_short vd_type; /* controller type */ 57 u_short vd_wticks; /* timeout */ 58 struct mdcb vd_mdcb; /* master command block */ 59 u_long vd_mdcbphys; /* physical address of vd_mdcb */ 60 struct dcb vd_dcb; /* i/o command block */ 61 u_long vd_dcbphys; /* physical address of vd_dcb */ 62 struct pte *vd_map; /* i/o page map */ 63 caddr_t vd_utl; /* mapped i/o space */ 64 caddr_t vd_rawbuf; /* buffer for raw+swap i/o */ 65 } vdsoftc[NVD]; 66 67 /* 68 * Per-drive state. 69 */ 70 struct dksoftc { 71 u_short dk_state; /* open fsm */ 72 u_short dk_openpart; /* units open on this drive */ 73 u_int dk_curcyl; /* last selected cylinder */ 74 struct dcb dk_dcb; /* seek command block */ 75 u_long dk_dcbphys; /* physical address of dk_dcb */ 76 } dksoftc[NDK]; 77 78 /* 79 * Drive states. Used during steps of open/initialization. 80 * States < OPEN (> 0) are transient, during an open operation. 81 * OPENRAW is used for unabeled disks, to allow format operations. 82 */ 83 #define CLOSED 0 /* disk is closed */ 84 #define WANTOPEN 1 /* open requested, not started */ 85 #define WANTOPENRAW 2 /* open requested, no label */ 86 #define RDLABEL 3 /* reading pack label */ 87 #define OPEN 4 /* intialized and ready */ 88 #define OPENRAW 5 /* open, no label */ 89 90 struct buf rdkbuf[NDK]; /* raw i/o buffer headers */ 91 struct buf dkutab[NDK]; /* i/o queue headers */ 92 struct disklabel dklabel[NDK]; /* pack labels */ 93 94 #define b_cylin b_resid 95 #define b_track b_error /* used for seek commands */ 96 #define b_seekf b_forw /* second queue on um_tab */ 97 #define b_seekl b_back /* second queue on um_tab */ 98 99 int vdwstart, vdwatch(); 100 101 /* 102 * See if the controller is really there; if so, initialize it. 103 */ 104 vdprobe(reg, vm) 105 caddr_t reg; 106 struct vba_ctlr *vm; 107 { 108 register br, cvec; /* must be r12, r11 */ 109 register struct vddevice *vdaddr = (struct vddevice *)reg; 110 struct vdsoftc *vd; 111 int s; 112 113 #ifdef lint 114 br = 0; cvec = br; br = cvec; 115 vdintr(0); 116 #endif 117 if (badaddr((caddr_t)reg, 2)) 118 return (0); 119 vd = &vdsoftc[vm->um_ctlr]; 120 vdaddr->vdreset = 0xffffffff; 121 DELAY(1000000); 122 if (vdaddr->vdreset != (unsigned)0xffffffff) { 123 vd->vd_type = VDTYPE_VDDC; 124 vd->vd_flags &= ~VD_DOSEEKS; 125 DELAY(1000000); 126 } else { 127 vd->vd_type = VDTYPE_SMDE; 128 vd->vd_flags |= VD_DOSEEKS; 129 vdaddr->vdrstclr = 0; 130 DELAY(3000000); 131 vdaddr->vdcsr = 0; 132 vdaddr->vdtcf_mdcb = AM_ENPDA; 133 vdaddr->vdtcf_dcb = AM_ENPDA; 134 vdaddr->vdtcf_trail = AM_ENPDA; 135 vdaddr->vdtcf_data = AM_ENPDA; 136 vdaddr->vdccf = CCF_SEN | CCF_DER | CCF_STS | 137 XMD_32BIT | BSZ_16WRD | 138 CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; 139 } 140 vd->vd_mdcbphys = vtoph((struct proc *)0, (unsigned)&vd->vd_mdcb); 141 vd->vd_dcbphys = vtoph((struct proc *)0, (unsigned)&vd->vd_dcb); 142 vm->um_addr = reg; /* XXX */ 143 s = spl7(); 144 if (!vdcmd(vm, VDOP_INIT, 10) || !vdcmd(vm, VDOP_DIAG, 10)) { 145 printf("vd%d: %s cmd failed\n", vm->um_ctlr, 146 vd->vd_dcb.opcode == VDOP_INIT ? "init" : "diag"); 147 splx(s); 148 return (0); 149 } 150 splx(s); 151 /* 152 * Allocate page tables and i/o buffer. 153 */ 154 vbmapalloc(btoc(VDMAXIO)+1, &vd->vd_map, &vd->vd_utl); 155 vd->vd_rawbuf = calloc(VDMAXIO); 156 br = 0x17, cvec = 0xe0 + vm->um_ctlr; /* XXX */ 157 return (sizeof (struct vddevice)); 158 } 159 160 /* 161 * See if a drive is really there. 162 * 163 * Can't read pack label here as various data structures 164 * aren't setup for doing a read in a straightforward 165 * manner. Instead just probe for the drive and leave 166 * the pack label stuff to the attach routine. 167 */ 168 vdslave(vi, addr) 169 register struct vba_device *vi; 170 struct vddevice *vdaddr; 171 { 172 register struct disklabel *lp = &dklabel[vi->ui_unit]; 173 struct vdsoftc *vd = &vdsoftc[vi->ui_ctlr]; 174 175 if ((vd->vd_flags&VD_INIT) == 0) { 176 printf("vd%d: %s controller\n", vi->ui_ctlr, 177 vd->vd_type == VDTYPE_VDDC ? "VDDC" : "SMDE"); 178 vd->vd_flags |= VD_INIT; 179 } 180 181 /* 182 * Initialize label enough to do a reset on 183 * the drive. The remainder of the default 184 * label values will be filled in in vdinit 185 * at attach time. 186 */ 187 lp->d_secsize = DEV_BSIZE / 2; /* XXX */ 188 lp->d_nsectors = 32; 189 lp->d_ntracks = 24; 190 lp->d_ncylinders = 711; 191 lp->d_secpercyl = 32*24; 192 return (vdreset_drive(vi)); 193 } 194 195 /* 196 * Read pack label. 197 */ 198 vdattach(vi) 199 register struct vba_device *vi; 200 { 201 register int unit = vi->ui_unit; 202 register struct dksoftc *dk = &dksoftc[unit]; 203 register struct disklabel *lp; 204 205 /* 206 * Try to initialize device and read pack label. 207 */ 208 if (vdinit(vdminor(unit, 0), 0) != 0) { 209 printf(": unknown drive type"); 210 return; 211 } 212 /* 213 * Initialize invariant portion of 214 * dcb used for overlapped seeks. 215 */ 216 dk->dk_dcb.opcode = VDOP_SEEK; 217 dk->dk_dcb.intflg = DCBINT_NONE | DCBINT_PBA; 218 dk->dk_dcb.devselect = vi->ui_slave; 219 dk->dk_dcb.trailcnt = sizeof (trseek) / sizeof (long); 220 dk->dk_dcb.trail.sktrail.skaddr.sector = 0; 221 dk->dk_dcbphys = vtoph((struct proc *)0, (unsigned)&dk->dk_dcb); 222 lp = &dklabel[unit]; 223 printf(": %s <ntrak %d, ncyl %d, nsec %d>", 224 lp->d_typename, lp->d_ntracks, lp->d_ncylinders, lp->d_nsectors); 225 /* 226 * (60 / rpm) / (sectors per track * (bytes per sector / 2)) 227 */ 228 if (vi->ui_dk >= 0) 229 dk_mspw[vi->ui_dk] = 120.0 / 230 (lp->d_rpm * lp->d_nsectors * lp->d_secsize); 231 #ifdef notyet 232 addswap(makedev(VDMAJOR, vdminor(unit, 0)), lp); 233 #endif 234 } 235 236 /*ARGSUSED*/ 237 vdopen(dev, flags) 238 dev_t dev; 239 int flags; 240 { 241 register unit = vdunit(dev); 242 register struct disklabel *lp; 243 register struct dksoftc *dk; 244 register struct partition *pp; 245 struct vba_device *vi; 246 int s, error, part = vdpart(dev); 247 daddr_t start, end; 248 249 if (unit >= NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0) 250 return (ENXIO); 251 lp = &dklabel[unit]; 252 dk = &dksoftc[unit]; 253 254 s = spl7(); 255 while (dk->dk_state != OPEN && dk->dk_state != OPENRAW && 256 dk->dk_state != CLOSED) 257 sleep((caddr_t)dk, PZERO+1); 258 splx(s); 259 if (dk->dk_state != OPEN && dk->dk_state != OPENRAW) 260 if (error = vdinit(dev, flags)) 261 return (error); 262 263 if (vdwstart == 0) { 264 timeout(vdwatch, (caddr_t)0, hz); 265 vdwstart++; 266 } 267 /* 268 * Warn if a partion is opened 269 * that overlaps another partition which is open 270 * unless one is the "raw" partition (whole disk). 271 */ 272 #define RAWPART 2 /* 'c' partition */ /* XXX */ 273 if ((dk->dk_openpart & (1 << part)) == 0 && 274 part != RAWPART) { 275 pp = &lp->d_partitions[part]; 276 start = pp->p_offset; 277 end = pp->p_offset + pp->p_size; 278 for (pp = lp->d_partitions; 279 pp < &lp->d_partitions[lp->d_npartitions]; pp++) { 280 if (pp->p_offset + pp->p_size <= start || 281 pp->p_offset >= end) 282 continue; 283 if (pp - lp->d_partitions == RAWPART) 284 continue; 285 if (dk->dk_openpart & (1 << (pp - lp->d_partitions))) 286 log(LOG_WARNING, 287 "dk%d%c: overlaps open partition (%c)\n", 288 unit, part + 'a', 289 pp - lp->d_partitions + 'a'); 290 } 291 } 292 if (part >= lp->d_npartitions) 293 return (ENXIO); 294 dk->dk_openpart |= 1 << part; 295 return (0); 296 } 297 298 vdclose(dev, flags) 299 dev_t dev; 300 int flags; 301 { 302 register int unit = vdunit(dev); 303 register struct dksoftc *dk = &dksoftc[unit]; 304 305 dk->dk_openpart &= ~(1 << vdpart(dev)); 306 /* 307 * Should wait for i/o to complete on this partition 308 * even if others are open, but wait for work on blkflush(). 309 */ 310 if (dk->dk_openpart == 0) { 311 int s = spl7(); 312 while (dkutab[unit].b_actf) 313 sleep((caddr_t)dk, PZERO-1); 314 splx(s); 315 dk->dk_state = CLOSED; 316 } 317 } 318 319 vdinit(dev, flags) 320 dev_t dev; 321 int flags; 322 { 323 register struct buf *bp = NULL; 324 register struct disklabel *lp; 325 register struct dksoftc *dk; 326 struct vba_device *vi; 327 struct disklabel *dlp; 328 int unit = vdunit(dev), error = 0; 329 extern int cold; 330 331 dk = &dksoftc[unit]; 332 if (flags & O_NDELAY) { 333 dk->dk_state = OPENRAW; 334 goto done; 335 } 336 337 /* 338 * Initialize portion of the label 339 * not set up in the slave routine. 340 */ 341 dk->dk_state = RDLABEL; 342 lp = &dklabel[unit]; 343 lp->d_secperunit = 0x1fffffff; 344 lp->d_npartitions = 1; 345 lp->d_partitions[0].p_size = 0x1fffffff; 346 lp->d_partitions[0].p_offset = 0; 347 348 bp = geteblk(DEV_BSIZE); /* max sector size */ 349 bp->b_dev = dev; 350 bp->b_blkno = LABELSECTOR; 351 bp->b_bcount = DEV_BSIZE; 352 bp->b_flags = B_BUSY | B_READ; 353 bp->b_cylin = LABELSECTOR / lp->d_secpercyl; 354 vdstrategy(bp); 355 biowait(bp); 356 if (bp->b_flags & B_ERROR) { 357 error = u.u_error; /* XXX */ 358 u.u_error = 0; 359 dk->dk_state = CLOSED; 360 goto done; 361 } 362 vi = vddinfo[unit]; 363 dlp = (struct disklabel *)(bp->b_un.b_addr + LABELOFFSET); 364 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC && 365 dkcksum(dlp) == 0) { 366 *lp = *dlp; 367 /* 368 * Now that we have the label, configure 369 * the correct drive parameters. 370 */ 371 if (!vdreset_drive(vi)) 372 dk->dk_state = CLOSED; 373 else 374 dk->dk_state = OPEN; 375 } else { 376 if (cold) 377 printf(": no disk label"); 378 else 379 log(LOG_ERR, "dk%d: no disk label\n", vi->ui_unit); 380 #ifdef COMPAT_42 381 if (!vdmaptype(vi, lp)) { 382 error = ENXIO; 383 dk->dk_state = CLOSED; 384 } else 385 dk->dk_state = OPEN; 386 #else 387 dk->dk_state = OPENRAW; 388 #endif 389 } 390 done: 391 if (bp) { 392 bp->b_flags = B_INVAL | B_AGE; 393 brelse(bp); 394 } 395 wakeup((caddr_t)dk); 396 return (error); 397 } 398 399 /*ARGSUSED*/ 400 vddgo(vm) 401 struct vba_device *vm; 402 { 403 404 } 405 406 vdstrategy(bp) 407 register struct buf *bp; 408 { 409 register struct vba_device *vi; 410 register struct disklabel *lp; 411 register struct dksoftc *dk; 412 register int unit; 413 register daddr_t sn; 414 struct buf *dp; 415 daddr_t sz, maxsz; 416 int part, s; 417 418 sz = bp->b_bcount; 419 sz = (sz + DEV_BSIZE - 1) >> DEV_BSHIFT; 420 unit = vdunit(bp->b_dev); 421 if (unit > NDK) { 422 bp->b_error = ENXIO; 423 goto bad; 424 } 425 vi = vddinfo[unit]; 426 lp = &dklabel[unit]; 427 if (vi == 0 || vi->ui_alive == 0) { 428 bp->b_error = ENXIO; 429 goto bad; 430 } 431 dk = &dksoftc[unit]; 432 if (dk->dk_state < OPEN) 433 goto q; 434 part = vdpart(bp->b_dev); 435 if ((dk->dk_openpart & (1 << part)) == 0) { 436 bp->b_error = ENODEV; 437 goto bad; 438 } 439 maxsz = lp->d_partitions[part].p_size; 440 sn = bp->b_blkno; 441 if (sn < 0 || sn + sz > maxsz) { 442 if (sn == maxsz) { 443 bp->b_resid = bp->b_bcount; 444 goto done; 445 } 446 sz = maxsz - bp->b_blkno; 447 if (sz <= 0) { 448 bp->b_error = EINVAL; 449 goto bad; 450 } 451 bp->b_bcount = sz * lp->d_secsize; 452 } 453 bp->b_cylin = (sn + lp->d_partitions[part].p_offset) / lp->d_secpercyl; 454 q: 455 vbasetup(bp, lp->d_secsize); 456 s = spl7(); 457 dp = &dkutab[vi->ui_unit]; 458 disksort(dp, bp); 459 if (!dp->b_active) { 460 (void) vdustart(vi); 461 if (!vi->ui_mi->um_tab.b_active) 462 vdstart(vi->ui_mi); 463 } 464 splx(s); 465 return; 466 bad: 467 bp->b_flags |= B_ERROR; 468 done: 469 biodone(bp); 470 return; 471 } 472 473 vdustart(vi) 474 register struct vba_device *vi; 475 { 476 register struct buf *bp, *dp; 477 register struct vba_ctlr *vm; 478 register int unit = vi->ui_unit; 479 register struct dksoftc *dk; 480 register struct vdsoftc *vd; 481 struct disklabel *lp; 482 483 dp = &dkutab[unit]; 484 /* 485 * If queue empty, nothing to do. 486 */ 487 if ((bp = dp->b_actf) == NULL) 488 return; 489 /* 490 * If drive is off-cylinder and controller supports seeks, 491 * place drive on seek queue for controller. 492 * Otherwise, place on transfer queue. 493 */ 494 vd = &vdsoftc[vi->ui_ctlr]; 495 dk = &dksoftc[unit]; 496 vm = vi->ui_mi; 497 if (bp->b_cylin != dk->dk_curcyl && vd->vd_flags&VD_DOSEEKS) { 498 lp = &dklabel[unit]; 499 bp->b_track = (bp->b_blkno % lp->d_secpercyl) / lp->d_nsectors; 500 if (vm->um_tab.b_seekf == NULL) 501 vm->um_tab.b_seekf = dp; 502 else 503 vm->um_tab.b_seekl->b_forw = dp; 504 vm->um_tab.b_seekl = dp; 505 } else { 506 if (vm->um_tab.b_actf == NULL) 507 vm->um_tab.b_actf = dp; 508 else 509 vm->um_tab.b_actl->b_forw = dp; 510 vm->um_tab.b_actl = dp; 511 } 512 dp->b_forw = NULL; 513 dp->b_active++; 514 } 515 516 /* 517 * Start next transfer on a controller. 518 * There are two queues of drives, the first on-cylinder 519 * and the second off-cylinder from their next transfers. 520 * Perform the first transfer for the first drive on the on-cylinder 521 * queue, if any, otherwise the first transfer for the first drive 522 * on the second queue. Initiate seeks on remaining drives on the 523 * off-cylinder queue, then move them all to the on-cylinder queue. 524 */ 525 vdstart(vm) 526 register struct vba_ctlr *vm; 527 { 528 register struct buf *bp; 529 register struct vba_device *vi; 530 register struct vdsoftc *vd; 531 register struct dksoftc *dk; 532 register struct disklabel *lp; 533 register int slave; 534 register struct dcb **dcbp; 535 struct mdcb *mdcb; 536 struct buf *dp; 537 int sn, tn; 538 539 loop: 540 /* 541 * Pull a request off the controller queue. 542 */ 543 if ((dp = vm->um_tab.b_actf) == NULL && 544 (dp = vm->um_tab.b_seekf) == NULL) 545 return; 546 if ((bp = dp->b_actf) == NULL) { 547 vm->um_tab.b_actf = dp->b_forw; 548 goto loop; 549 } 550 551 /* 552 * Mark controller busy, and determine 553 * destination of this request. 554 */ 555 vm->um_tab.b_active++; 556 vi = vddinfo[vdunit(bp->b_dev)]; 557 dk = &dksoftc[vi->ui_unit]; 558 sn = bp->b_blkno; 559 lp = &dklabel[vi->ui_unit]; 560 sn %= lp->d_secpercyl; 561 tn = sn / lp->d_nsectors; 562 sn %= lp->d_nsectors; 563 564 /* 565 * Construct dcb for read/write command. 566 */ 567 vd = &vdsoftc[vm->um_ctlr]; 568 slave = vi->ui_slave; 569 vd->vd_dcb.opcode = (bp->b_flags & B_READ) ? VDOP_RD : VDOP_WD; 570 vd->vd_dcb.intflg = DCBINT_DONE; 571 vd->vd_dcb.devselect = slave; 572 vd->vd_dcb.operrsta = 0; 573 vd->vd_dcb.nxtdcb = (struct dcb *)0; /* end of chain */ 574 vd->vd_dcb.trailcnt = sizeof (trrw) / sizeof (long); 575 vd->vd_dcb.trail.rwtrail.memadr = (char *) 576 vbastart(bp, vd->vd_rawbuf, (long *)vd->vd_map, vd->vd_utl); 577 vd->vd_dcb.trail.rwtrail.wcount = (bp->b_bcount+1) >> 1; 578 vd->vd_dcb.trail.rwtrail.disk.cylinder = bp->b_cylin; 579 vd->vd_dcb.trail.rwtrail.disk.track = tn; 580 vd->vd_dcb.trail.rwtrail.disk.sector = sn; 581 dk->dk_curcyl = bp->b_cylin; 582 bp->b_track = 0; /* init overloaded field */ 583 if (vi->ui_dk >= 0) { 584 dk_busy |= 1<<vi->ui_dk; 585 dk_xfer[vi->ui_dk]++; 586 dk_wds[vi->ui_dk] += bp->b_bcount>>6; 587 } 588 589 /* 590 * Look for any seeks to be performed on other drives on this 591 * controller. If overlapped seeks exist, insert seek commands 592 * on the controller's command queue before the transfer. 593 */ 594 dcbp = &vd->vd_mdcb.mdcb_head; 595 596 if (dp == vm->um_tab.b_seekf) 597 dp = dp->b_forw; 598 else 599 dp = vm->um_tab.b_seekf; 600 for (; dp != NULL; dp = dp->b_forw) { 601 if ((bp = dp->b_actf) == NULL) 602 continue; 603 vi = vddinfo[vdunit(bp->b_dev)]; 604 dk = &dksoftc[vi->ui_unit]; 605 dk->dk_curcyl = bp->b_cylin; 606 if (vi->ui_dk >= 0) 607 dk_seek[vi->ui_dk]++; 608 dk->dk_dcb.operrsta = 0; 609 dk->dk_dcb.trail.sktrail.skaddr.cylinder = bp->b_cylin; 610 #ifdef notdef 611 dk->dk_dcb.trail.sktrail.skaddr.track = bp->b_daddr>>8; 612 dk->dk_dcb.trail.sktrail.skaddr.sector = 613 bp->b_daddr & 0xff; 614 #else 615 dk->dk_dcb.trail.sktrail.skaddr.track = bp->b_track; 616 #endif 617 *dcbp = (struct dcb *)dk->dk_dcbphys; 618 dcbp = &dk->dk_dcb.nxtdcb; 619 } 620 *dcbp = (struct dcb *)vd->vd_dcbphys; 621 if (vm->um_tab.b_actf) 622 vm->um_tab.b_actl->b_forw = vm->um_tab.b_seekf; 623 else 624 vm->um_tab.b_actf = vm->um_tab.b_seekf; 625 vm->um_tab.b_actl = vm->um_tab.b_seekl; 626 vm->um_tab.b_seekf = 0; 627 vm->um_tab.b_seekl = 0; 628 629 /* 630 * Initiate operation. 631 */ 632 vd->vd_mdcb.mdcb_status = 0; 633 VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type); 634 } 635 636 #define DONTCARE (DCBS_DSE|DCBS_DSL|DCBS_TOP|DCBS_TOM|DCBS_FAIL|DCBS_DONE) 637 /* 638 * Handle a disk interrupt. 639 */ 640 vdintr(ctlr) 641 register ctlr; 642 { 643 register struct buf *bp, *dp; 644 register struct vba_ctlr *vm = vdminfo[ctlr]; 645 register struct vba_device *vi; 646 register struct vdsoftc *vd = &vdsoftc[ctlr]; 647 register status; 648 struct dksoftc *dk; 649 650 vd->vd_wticks = 0; 651 if (!vm->um_tab.b_active) { 652 printf("vd%d: stray interrupt\n", ctlr); 653 return; 654 } 655 /* 656 * Get device and block structures, and a pointer 657 * to the vba_device for the drive. 658 */ 659 dp = vm->um_tab.b_actf; 660 bp = dp->b_actf; 661 vi = vddinfo[vdunit(bp->b_dev)]; 662 if (vi->ui_dk >= 0) 663 dk_busy &= ~(1<<vi->ui_dk); 664 /* 665 * Check for and process errors on 666 * either the drive or the controller. 667 */ 668 uncache(&vd->vd_dcb.operrsta); 669 status = vd->vd_dcb.operrsta; 670 if (status & VDERR_HARD) { 671 if (status & DCBS_WPT) { 672 /* 673 * Give up on write locked devices immediately. 674 */ 675 printf("dk%d: write locked\n", vi->ui_unit); 676 bp->b_flags |= B_ERROR; 677 } else if (status & VDERR_RETRY) { 678 if (status & VDERR_DRIVE) { 679 if (!vdreset_drive(vi)) 680 vi->ui_alive = 0; 681 } else if (status & VDERR_CTLR) 682 vdreset_ctlr(vm); 683 /* 684 * Retry transfer once, unless reset failed. 685 */ 686 if (!vi->ui_alive || bp->b_errcnt++ >= 2) 687 goto hard; 688 vm->um_tab.b_active = 0; /* force retry */ 689 } else { 690 hard: 691 bp->b_flags |= B_ERROR; 692 /* NEED TO ADJUST b_blkno to failed sector */ 693 harderr(bp, "dk"); 694 printf("status %x (%b)", status, 695 status &~ DONTCARE, VDERRBITS); 696 if (vd->vd_type == VDTYPE_SMDE) { 697 uncache(&vd->vd_dcb.err_code); 698 printf(" ecode %x", vd->vd_dcb.err_code); 699 } 700 printf("\n"); 701 } 702 } else if (status & DCBS_SOFT) 703 vdsofterr(vd, bp, &vd->vd_dcb); 704 if (vm->um_tab.b_active) { 705 vm->um_tab.b_active = 0; 706 vm->um_tab.b_errcnt = 0; 707 vm->um_tab.b_actf = dp->b_forw; 708 dp->b_active = 0; 709 dp->b_errcnt = 0; 710 dp->b_actf = bp->av_forw; 711 bp->b_resid = 0; 712 vbadone(bp, vd->vd_rawbuf, (long *)vd->vd_map, vd->vd_utl); 713 biodone(bp); 714 /* 715 * If this unit has more work to do, 716 * then start it up right away. 717 */ 718 if (dp->b_actf) 719 vdustart(vi); 720 else if ((dk = &dksoftc[vi->ui_unit])->dk_openpart == 0) 721 wakeup((caddr_t)dk); 722 } 723 /* 724 * If there are devices ready to 725 * transfer, start the controller. 726 */ 727 if (vm->um_tab.b_actf) 728 vdstart(vm); 729 } 730 731 vdsofterr(vd, bp, dcb) 732 struct vdsoftc *vd; 733 register struct buf *bp; 734 register struct dcb *dcb; 735 { 736 int unit = vdunit(bp->b_dev), status = dcb->operrsta; 737 char part = 'a' + vdpart(bp->b_dev); 738 739 if (status != (DCBS_DCE|DCBS_CCD|DCBS_SOFT|DCBS_ERR)) { 740 if (vd->vd_type == VDTYPE_SMDE) 741 uncache(&dcb->err_code); 742 log(LOG_WARNING, "dk%d%c: soft error sn%d status %b ecode %x\n", 743 unit, part, bp->b_blkno, status, VDERRBITS, dcb->err_code); 744 } else 745 log(LOG_WARNING, "dk%d%c: soft ecc sn%d\n", 746 unit, part, bp->b_blkno); 747 } 748 749 vdread(dev, uio) 750 dev_t dev; 751 struct uio *uio; 752 { 753 register int unit = vdunit(dev); 754 755 if (unit >= NDK) 756 return (ENXIO); 757 return (physio(vdstrategy, &rdkbuf[unit], dev, B_READ, minphys, uio)); 758 } 759 760 vdwrite(dev, uio) 761 dev_t dev; 762 struct uio *uio; 763 { 764 register int unit = vdunit(dev); 765 766 if (unit >= NDK) 767 return (ENXIO); 768 return (physio(vdstrategy, &rdkbuf[unit], dev, B_WRITE, minphys, uio)); 769 } 770 771 vdioctl(dev, cmd, data, flag) 772 dev_t dev; 773 int cmd; 774 caddr_t data; 775 int flag; 776 { 777 int unit = vdunit(dev); 778 register struct disklabel *lp = &dklabel[unit]; 779 int error = 0; 780 781 switch (cmd) { 782 783 case DIOCGDINFO: 784 *(struct disklabel *)data = *lp; 785 break; 786 787 case DIOCGPART: 788 ((struct partinfo *)data)->disklab = lp; 789 ((struct partinfo *)data)->part = 790 &lp->d_partitions[vdpart(dev)]; 791 break; 792 793 case DIOCSDINFO: 794 if ((flag & FWRITE) == 0) 795 error = EBADF; 796 else 797 *lp = *(struct disklabel *)data; 798 break; 799 800 case DIOCWDINFO: { 801 struct buf *bp; 802 struct disklabel *dlp; 803 804 if ((flag & FWRITE) == 0) { 805 error = EBADF; 806 break; 807 } 808 *lp = *(struct disklabel *)data; 809 bp = geteblk(lp->d_secsize); 810 bp->b_dev = dev; 811 bp->b_blkno = LABELSECTOR; 812 bp->b_bcount = lp->d_secsize; 813 bp->b_flags = B_READ; 814 dlp = (struct disklabel *)(bp->b_un.b_addr + LABELOFFSET); 815 vdstrategy(bp); 816 biowait(bp); 817 if (bp->b_flags & B_ERROR) { 818 error = u.u_error; /* XXX */ 819 u.u_error = 0; 820 goto bad; 821 } 822 *dlp = *lp; 823 bp->b_flags = B_WRITE; 824 vdstrategy(bp); 825 biowait(bp); 826 if (bp->b_flags & B_ERROR) { 827 error = u.u_error; /* XXX */ 828 u.u_error = 0; 829 } 830 bad: 831 brelse(bp); 832 break; 833 } 834 835 default: 836 error = ENOTTY; 837 break; 838 } 839 return (0); 840 } 841 842 /* 843 * Watch for lost interrupts. 844 */ 845 vdwatch() 846 { 847 register struct vdsoftc *vd; 848 register struct vba_ctlr *vm; 849 register int ctlr, unit; 850 851 timeout(vdwatch, (caddr_t)0, hz); 852 for (ctlr = 0; ctlr < NVD; ctlr++) { 853 vm = vdminfo[ctlr]; 854 if (vm == 0 || vm->um_alive == 0) 855 continue; 856 vd = &vdsoftc[ctlr]; 857 if (!vm->um_tab.b_active) { 858 for (unit = 0; unit < NDK; unit++) 859 if (dkutab[unit].b_active && 860 vddinfo[unit]->ui_mi == vm) 861 goto active; 862 vd->vd_wticks = 0; 863 continue; 864 } 865 active: 866 vd->vd_wticks++; 867 if (vd->vd_wticks >= 20) { 868 vd->vd_wticks = 0; 869 printf("vd%d: lost interrupt\n", ctlr); 870 /* abort pending dcb's and restart controller */ 871 } 872 } 873 } 874 875 #define DBSIZE 64 /* controller limit with 1K sectors */ 876 /* 877 * Crash dump. 878 */ 879 vddump(dev) 880 dev_t dev; 881 { 882 register struct vba_device *vi; 883 register struct vba_ctlr *vm; 884 register struct disklabel *lp; 885 register struct vdsoftc *vd; 886 struct dksoftc *dk; 887 int part, unit, num; 888 caddr_t start; 889 890 start = 0; 891 unit = vdunit(dev); 892 if (unit > NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0) 893 return (ENXIO); 894 dk = &dksoftc[unit]; 895 if (dk->dk_state != OPEN && dk->dk_state != OPENRAW) 896 return (ENXIO); 897 lp = &dklabel[unit]; 898 part = vdpart(dev); 899 if (part >= lp->d_npartitions) 900 return (ENXIO); 901 vm = vdminfo[vi->ui_ctlr]; 902 vdreset_ctlr(vm); 903 if (dumplo < 0) 904 return (EINVAL); 905 /* 906 * Dumplo and maxfree are in pages. 907 */ 908 num = maxfree * (NBPG / lp->d_secsize); 909 dumplo *= NBPG / lp->d_secsize; 910 if (dumplo + num >= lp->d_partitions[vdpart(dev)].p_size) 911 num = lp->d_partitions[vdpart(dev)].p_size - dumplo; 912 vd = &vdsoftc[vm->um_ctlr]; 913 vd->vd_dcb.intflg = DCBINT_NONE; 914 vd->vd_dcb.opcode = VDOP_WD; 915 vd->vd_dcb.devselect = vi->ui_slave; 916 vd->vd_dcb.trailcnt = sizeof (trrw) / sizeof (long); 917 while (num > 0) { 918 int nsec, cn, sn, tn; 919 920 nsec = MIN(num, DBSIZE); 921 sn = dumplo + (unsigned)start / lp->d_secsize; 922 cn = (sn + lp->d_partitions[vdpart(dev)].p_offset) / 923 lp->d_secpercyl; 924 sn %= lp->d_secpercyl; 925 tn = sn / lp->d_nsectors; 926 sn %= lp->d_nsectors; 927 vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys; 928 vd->vd_dcb.trail.rwtrail.memadr = start; 929 vd->vd_dcb.trail.rwtrail.wcount = (nsec * lp->d_secsize) >> 1; 930 vd->vd_dcb.trail.rwtrail.disk.cylinder = cn; 931 vd->vd_dcb.trail.rwtrail.disk.track = tn; 932 vd->vd_dcb.trail.rwtrail.disk.sector = sn; 933 vd->vd_dcb.operrsta = 0; 934 VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type); 935 if (!vdpoll(vm, 5)) { 936 printf(" during dump\n"); 937 return (EIO); 938 } 939 if (vd->vd_dcb.operrsta & VDERR_HARD) { 940 printf("dk%d: hard error, status=%b\n", unit, 941 vd->vd_dcb.operrsta, VDERRBITS); 942 return (EIO); 943 } 944 start += nsec * lp->d_secsize; 945 num -= nsec; 946 } 947 return (0); 948 } 949 950 vdsize(dev) 951 dev_t dev; 952 { 953 register int unit = vdunit(dev); 954 register struct dksoftc *dk; 955 struct vba_device *vi; 956 struct disklabel *lp; 957 958 if (unit >= NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0 || 959 (dk = &dksoftc[unit])->dk_state != OPEN) 960 return (-1); 961 lp = &dklabel[unit]; 962 return ((int)lp->d_partitions[vdpart(dev)].p_size); 963 } 964 965 /* 966 * Perform a controller reset. 967 */ 968 vdreset_ctlr(vm) 969 register struct vba_ctlr *vm; 970 { 971 register struct vddevice *vdaddr = (struct vddevice *)vm->um_addr; 972 register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr]; 973 register int unit; 974 struct vba_device *vi; 975 976 VDRESET(vdaddr, vd->vd_type); 977 if (vd->vd_type == VDTYPE_SMDE) { 978 vdaddr->vdcsr = 0; 979 vdaddr->vdtcf_mdcb = AM_ENPDA; 980 vdaddr->vdtcf_dcb = AM_ENPDA; 981 vdaddr->vdtcf_trail = AM_ENPDA; 982 vdaddr->vdtcf_data = AM_ENPDA; 983 vdaddr->vdccf = CCF_STS | XMD_32BIT | BSZ_16WRD | 984 CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR; 985 } 986 if (!vdcmd(vm, VDOP_INIT, 10) || !vdcmd(vm, VDOP_DIAG, 10)) { 987 printf("%s cmd failed\n", 988 vd->vd_dcb.opcode == VDOP_INIT ? "init" : "diag"); 989 return; 990 } 991 for (unit = 0; unit < NDK; unit++) 992 if ((vi = vddinfo[unit])->ui_mi == vm && vi->ui_alive) 993 (void) vdreset_drive(vi); 994 } 995 996 vdreset_drive(vi) 997 register struct vba_device *vi; 998 { 999 register struct disklabel *lp = &dklabel[vi->ui_unit]; 1000 struct vba_ctlr *vm = vdminfo[vi->ui_ctlr]; 1001 struct vddevice *vdaddr = (struct vddevice *)vm->um_addr; 1002 struct vdsoftc *vd = &vdsoftc[vi->ui_ctlr]; 1003 1004 top: 1005 vd->vd_dcb.opcode = VDOP_CONFIG; /* command */ 1006 vd->vd_dcb.intflg = DCBINT_NONE; 1007 vd->vd_dcb.nxtdcb = (struct dcb *)0; /* end of chain */ 1008 vd->vd_dcb.operrsta = 0; 1009 vd->vd_dcb.devselect = vi->ui_slave; 1010 vd->vd_dcb.trail.rstrail.ncyl = lp->d_ncylinders; 1011 vd->vd_dcb.trail.rstrail.nsurfaces = lp->d_ntracks; 1012 if (vd->vd_type == VDTYPE_SMDE) { 1013 vd->vd_dcb.trailcnt = sizeof (treset) / sizeof (long); 1014 vd->vd_dcb.trail.rstrail.nsectors = lp->d_nsectors; 1015 vd->vd_dcb.trail.rstrail.slip_sec = lp->d_trackskew; 1016 vd->vd_dcb.trail.rstrail.recovery = 0x18f; 1017 } else 1018 vd->vd_dcb.trailcnt = 2; /* XXX */ 1019 vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys; 1020 vd->vd_mdcb.mdcb_status = 0; 1021 VDGO(vdaddr, vd->vd_mdcbphys, vd->vd_type); 1022 if (!vdpoll(vm, 5)) { 1023 printf(" during config\n"); 1024 return (0); 1025 } 1026 if (vd->vd_dcb.operrsta & VDERR_HARD) { 1027 if (vd->vd_type == VDTYPE_SMDE && 1028 (vdaddr->vdstatus[vi->ui_slave]&STA_US) == 0) 1029 return (0); 1030 if ((vd->vd_dcb.operrsta & (DCBS_OCYL|DCBS_NRDY)) == 0) 1031 printf("dk%d: config error\n", vi->ui_unit); 1032 else if ((vd->vd_flags&VD_STARTED) == 0) { 1033 int started; 1034 1035 printf("vd%d: starting drives, wait ... ", vm->um_ctlr); 1036 vd->vd_flags |= VD_STARTED; 1037 started = (vdcmd(vm, VDOP_START, 10) == 1); 1038 DELAY(62000000); 1039 printf("\n"); 1040 if (started) 1041 goto top; 1042 } 1043 return (0); 1044 } 1045 return (1); 1046 } 1047 1048 /* 1049 * Perform a command w/o trailer. 1050 */ 1051 vdcmd(vm, cmd, t) 1052 register struct vba_ctlr *vm; 1053 { 1054 register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr]; 1055 1056 vd->vd_dcb.opcode = cmd; /* command */ 1057 vd->vd_dcb.intflg = DCBINT_NONE; 1058 vd->vd_dcb.nxtdcb = (struct dcb *)0; /* end of chain */ 1059 vd->vd_dcb.operrsta = 0; 1060 vd->vd_dcb.devselect = 0; 1061 vd->vd_dcb.trailcnt = 0; 1062 vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys; 1063 vd->vd_mdcb.mdcb_status = 0; 1064 VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type); 1065 if (!vdpoll(vm, t)) { 1066 printf(" during init\n"); 1067 return (0); 1068 } 1069 return ((vd->vd_dcb.operrsta&VDERR_HARD) == 0); 1070 } 1071 1072 /* 1073 * Poll controller until operation 1074 * completes or timeout expires. 1075 */ 1076 vdpoll(vm, t) 1077 register struct vba_ctlr *vm; 1078 register int t; 1079 { 1080 register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr]; 1081 register struct vddevice *vdaddr = (struct vddevice *)vm->um_addr; 1082 1083 t *= 1000; 1084 for (;;) { 1085 uncache(&vd->vd_dcb.operrsta); 1086 if (vd->vd_dcb.operrsta & (DCBS_DONE|DCBS_ABORT)) 1087 break; 1088 if (--t <= 0) { 1089 printf("vd%d: controller timeout", vm->um_ctlr); 1090 VDABORT(vdaddr, vd->vd_type); 1091 DELAY(30000); 1092 return (0); 1093 } 1094 DELAY(1000); 1095 } 1096 if (vd->vd_type == VDTYPE_SMDE) { 1097 do { 1098 DELAY(50); 1099 uncache(&vdaddr->vdcsr); 1100 } while (vdaddr->vdcsr & CS_GO); 1101 DELAY(300); 1102 } 1103 DELAY(200); 1104 uncache(&vd->vd_dcb.operrsta); 1105 return (1); 1106 } 1107 1108 #ifdef COMPAT_42 1109 struct vdst { 1110 int nsec; /* sectors/track */ 1111 int ntrack; /* tracks/cylinder */ 1112 int ncyl; /* cylinders */ 1113 char *name; /* type name */ 1114 struct { 1115 int off; /* partition offset in sectors */ 1116 int size; /* partition size in sectors */ 1117 } parts[8]; 1118 } vdst[] = { 1119 { 48, 24, 711, "xsd", 1120 {0, 30528}, /* a cyl 0 - 52 */ 1121 {30528, 30528}, /* b cyl 53 - 105 */ 1122 {61056, 345600}, /* c cyl 106 - 705 */ 1123 {118656, 288000}, /* d cyl 206 - 705 */ 1124 {176256, 230400}, /* e cyl 306 - 705 */ 1125 {233856, 172800}, /* f cyl 406 - 705 */ 1126 {291456, 115200}, /* g cyl 506 - 705 */ 1127 {349056, 57600} /* h cyl 606 - 705 */ 1128 }, 1129 { 44, 20, 842, "egl", 1130 {0, 26400}, /* egl0a cyl 0 - 59 */ 1131 {26400, 33000}, /* egl0b cyl 60 - 134 */ 1132 {59400, 308880}, /* egl0c cyl 135 - 836 */ 1133 {368280, 2640}, /* egl0d cyl 837 - 842 */ 1134 {0, 368280}, /* egl0e cyl 0 - 836 */ 1135 {0, 370920}, /* egl0f cyl 0 - 842 */ 1136 {59400, 155320}, /* egl0g cyl 135 - 487 */ 1137 {214720, 153560} /* egl0h cyl 488 - 836 */ 1138 }, 1139 { 64, 10, 823, "fuj", 1140 {0, 19200}, /* fuj0a cyl 0 - 59 */ 1141 {19200, 24000}, /* fuj0b cyl 60 - 134 */ 1142 {43200, 218560}, /* fuj0c cyl 135 - 817 */ 1143 {79680, 182080}, /* fuj0d cyl 249 - 817 */ 1144 {116160, 145600}, /* fuj0e cyl 363 - 817 */ 1145 {152640, 109120}, /* fuj0f cyl 477 - 817 */ 1146 {189120, 72640}, /* fuj0g cyl 591 - 817 */ 1147 {225600, 36160} /* fug0h cyl 705 - 817 */ 1148 }, 1149 { 32, 24, 711, "xfd", 1150 { 0, 20352 }, /* a cyl 0 - 52 */ 1151 { 20352, 20352 }, /* b cyl 53 - 105 */ 1152 { 40704, 230400 }, /* c cyl 106 - 705 */ 1153 { 0, 40704 }, /* d cyl 709 - 710 (a & b) */ 1154 { 0, 271104 }, /* e cyl 0 - 705 */ 1155 { 20352, 250752 }, /* f cyl 53 - 705 (b & c) */ 1156 { 40704, 115200 }, /* g cyl 106 - 405 (1/2 of c) */ 1157 { 155904,115200 } /* h cyl 406 - 705 (1/2 of c) */ 1158 }, 1159 { 32, 19, 823, "smd", 1160 {0, 20064}, /* a cyl 0-65 */ 1161 {20064, 13680}, /* b cyl 66-110 */ 1162 {33744, 214928}, /* c cyl 111-817 */ 1163 {69616, 179056}, /* d cyl 229 - 817 */ 1164 {105488, 143184}, /* e cyl 347 - 817 */ 1165 {141360, 107312}, /* f cyl 465 - 817 */ 1166 {177232, 71440}, /* g cyl 583 - 817 */ 1167 {213104, 35568} /* h cyl 701 - 817 */ 1168 }, 1169 { 32, 10, 823, "fsd", 1170 {0, 9600}, /* a cyl 0 - 59 */ 1171 {9600, 12000}, /* b cyl 60 - 134 */ 1172 {21600, 109280}, /* c cyl 135 - 817 */ 1173 {39840, 91040}, /* d cyl 249 - 817 */ 1174 {58080, 72800}, /* e cyl 363 - 817 */ 1175 {76320, 54560}, /* f cyl 477 - 817 */ 1176 {94560, 36320}, /* g cyl 591 - 817 */ 1177 {112800, 18080} /* h cyl 705 - 817 */ 1178 } 1179 }; 1180 #define NVDST (sizeof (vdst) / sizeof (vdst[0])) 1181 1182 /* 1183 * Construct a label for an unlabeled pack. We 1184 * deduce the drive type by reading from the last 1185 * track on successively smaller drives until we 1186 * don't get an error. 1187 */ 1188 vdmaptype(vi, lp) 1189 register struct vba_device *vi; 1190 register struct disklabel *lp; 1191 { 1192 register struct vdsoftc *vd; 1193 register struct vdst *p; 1194 struct vba_ctlr *vm = vdminfo[vi->ui_ctlr]; 1195 int i; 1196 1197 vd = &vdsoftc[vi->ui_ctlr]; 1198 for (p = vdst; p < &vdst[NVDST]; p++) { 1199 if (vd->vd_type == VDTYPE_VDDC && p->nsec != 32) 1200 continue; 1201 lp->d_nsectors = p->nsec; 1202 lp->d_ntracks = p->ntrack; 1203 lp->d_ncylinders = p->ncyl; 1204 if (!vdreset_drive(vi)) 1205 return (0); 1206 vd->vd_dcb.opcode = VDOP_RD; 1207 vd->vd_dcb.intflg = DCBINT_NONE; 1208 vd->vd_dcb.nxtdcb = (struct dcb *)0; /* end of chain */ 1209 vd->vd_dcb.devselect = vi->ui_slave; 1210 vd->vd_dcb.trailcnt = sizeof (trrw) / sizeof (long); 1211 vd->vd_dcb.trail.rwtrail.memadr = (char *) 1212 vtoph((struct proc *)0, (unsigned)vd->vd_rawbuf); 1213 vd->vd_dcb.trail.rwtrail.wcount = 512 / sizeof(short); 1214 vd->vd_dcb.operrsta = 0; 1215 vd->vd_dcb.trail.rwtrail.disk.cylinder = p->ncyl - 2; 1216 vd->vd_dcb.trail.rwtrail.disk.track = p->ntrack - 1; 1217 vd->vd_dcb.trail.rwtrail.disk.sector = p->nsec - 1; 1218 vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys; 1219 vd->vd_mdcb.mdcb_status = 0; 1220 VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type); 1221 if (!vdpoll(vm, 60)) 1222 printf(" during probe\n"); 1223 if ((vd->vd_dcb.operrsta & VDERR_HARD) == 0) 1224 break; 1225 } 1226 if (p >= &vdst[NVDST]) { 1227 printf("dk%d: unknown drive type\n", vi->ui_unit); 1228 return (0); 1229 } 1230 for (i = 0; i < 8; i++) { 1231 lp->d_partitions[i].p_offset = p->parts[i].off; 1232 lp->d_partitions[i].p_size = p->parts[i].size; 1233 } 1234 lp->d_npartitions = 8; 1235 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; 1236 lp->d_rpm = 3600; 1237 lp->d_secsize = 512; 1238 bcopy(p->name, lp->d_typename, 4); 1239 return (1); 1240 } 1241 #endif COMPAT_42 1242 #endif 1243