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