1 /* $NetBSD: wd.c,v 1.166 1997/10/13 00:47:33 explorer Exp $ */ 2 3 /* 4 * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved. 5 * 6 * DMA and multi-sector PIO handling are derived from code contributed by 7 * Onno van der Linden. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by Charles M. Hannum. 20 * 4. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include "rnd.h" 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/conf.h> 41 #include <sys/file.h> 42 #include <sys/stat.h> 43 #include <sys/ioctl.h> 44 #include <sys/buf.h> 45 #include <sys/uio.h> 46 #include <sys/malloc.h> 47 #include <sys/device.h> 48 #include <sys/disklabel.h> 49 #include <sys/disk.h> 50 #include <sys/syslog.h> 51 #include <sys/proc.h> 52 #if NRND > 0 53 #include <sys/rnd.h> 54 #endif 55 56 #include <vm/vm.h> 57 58 #include <machine/cpu.h> 59 #include <machine/intr.h> 60 #include <machine/pio.h> 61 62 #include <dev/isa/isavar.h> 63 #include <dev/isa/wdreg.h> 64 #include <dev/isa/wdlink.h> 65 #include "locators.h" 66 67 #define WAITTIME (4 * hz) /* time to wait for a completion */ 68 69 #define WDIORETRIES 5 /* number of retries before giving up */ 70 71 #define WDUNIT(dev) DISKUNIT(dev) 72 #define WDPART(dev) DISKPART(dev) 73 #define MAKEWDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part) 74 75 #define WDLABELDEV(dev) (MAKEWDDEV(major(dev), WDUNIT(dev), RAW_PART)) 76 77 #ifdef WDDEBUG 78 #define WDDEBUG_PRINT(args) printf args 79 #else 80 #define WDDEBUG_PRINT(args) 81 #endif 82 83 struct wd_softc { 84 struct device sc_dev; 85 struct disk sc_dk; 86 struct wd_link *d_link; 87 struct buf sc_q; 88 #if NRND > 0 89 rndsource_element_t rnd_source; 90 #endif 91 }; 92 93 int wdprobe __P((struct device *, void *, void *)); 94 void wdattach __P((struct device *, struct device *, void *)); 95 int wdprint __P((void *, char *)); 96 97 struct cfattach wd_ca = { 98 sizeof(struct wd_softc), wdprobe, wdattach 99 }; 100 101 struct cfdriver wd_cd = { 102 NULL, "wd", DV_DISK 103 }; 104 105 void wdgetdefaultlabel __P((struct wd_softc *, struct disklabel *)); 106 void wdgetdisklabel __P((struct wd_softc *)); 107 int wd_get_parms __P((struct wd_softc *)); 108 void wdstrategy __P((struct buf *)); 109 void wdstart __P((void *)); 110 111 struct dkdriver wddkdriver = { wdstrategy }; 112 113 /* XXX: these should go elsewhere */ 114 cdev_decl(wd); 115 bdev_decl(wd); 116 117 void wdfinish __P((struct wd_softc *, struct buf *)); 118 int wdsetctlr __P((struct wd_link *)); 119 static void bad144intern __P((struct wd_softc *)); 120 int wdlock __P((struct wd_link *)); 121 void wdunlock __P((struct wd_link *)); 122 123 int 124 wdprobe(parent, match, aux) 125 struct device *parent; 126 void *match, *aux; 127 { 128 struct cfdata *cf = match; 129 struct wd_link *d_link = aux; 130 int drive; 131 132 if (d_link == NULL) 133 return 0; 134 if (d_link->type != ATA) 135 return 0; 136 137 drive = d_link->drive; 138 if (cf->cf_loc[ATACF_DRIVE] != ATACF_DRIVE_DEFAULT && 139 cf->cf_loc[ATACF_DRIVE] != drive) 140 return 0; 141 142 return 1; 143 } 144 145 void 146 wdattach(parent, self, aux) 147 struct device *parent, *self; 148 void *aux; 149 { 150 struct wd_softc *wd = (void *)self; 151 struct wd_link *d_link= aux; 152 int i, blank; 153 char buf[41], c, *p, *q; 154 155 wd->d_link = d_link; 156 d_link->openings = 1; 157 d_link->wd_softc = (caddr_t)wd; 158 159 /* 160 * Initialize and attach the disk structure. 161 */ 162 wd->sc_dk.dk_driver = &wddkdriver; 163 wd->sc_dk.dk_name = wd->sc_dev.dv_xname; 164 disk_attach(&wd->sc_dk); 165 166 d_link->sc_lp = wd->sc_dk.dk_label; 167 168 wdc_get_parms((struct wdc_softc *)d_link->wdc_softc, d_link); 169 for (blank = 0, p = d_link->sc_params.wdp_model, q = buf, i = 0; 170 i < sizeof(d_link->sc_params.wdp_model); i++) { 171 c = *p++; 172 if (c == '\0') 173 break; 174 if (c != ' ') { 175 if (blank) { 176 *q++ = ' '; 177 blank = 0; 178 } 179 *q++ = c; 180 } else 181 blank = 1; 182 } 183 *q++ = '\0'; 184 185 printf(": <%s>\n%s: %dMB, %d cyl, %d head, %d sec, %d bytes/sec\n", 186 buf, self->dv_xname, 187 d_link->sc_params.wdp_cylinders * 188 (d_link->sc_params.wdp_heads * d_link->sc_params.wdp_sectors) / 189 (1048576 / DEV_BSIZE), 190 d_link->sc_params.wdp_cylinders, 191 d_link->sc_params.wdp_heads, 192 d_link->sc_params.wdp_sectors, 193 DEV_BSIZE); 194 195 if ((d_link->sc_params.wdp_capabilities & WD_CAP_DMA) != 0 && 196 d_link->sc_mode == WDM_DMA) { 197 d_link->sc_mode = WDM_DMA; 198 } else if (d_link->sc_params.wdp_maxmulti > 1) { 199 d_link->sc_mode = WDM_PIOMULTI; 200 d_link->sc_multiple = min(d_link->sc_params.wdp_maxmulti, 16); 201 } else { 202 d_link->sc_mode = WDM_PIOSINGLE; 203 d_link->sc_multiple = 1; 204 } 205 206 printf("%s: using", wd->sc_dev.dv_xname); 207 if (d_link->sc_mode == WDM_DMA) 208 printf(" dma transfers,"); 209 else 210 printf(" %d-sector %d-bit pio transfers,", 211 d_link->sc_multiple, 212 (d_link->sc_flags & WDF_32BIT) == 0 ? 16 : 32); 213 if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) 214 printf(" lba addressing\n"); 215 else 216 printf(" chs addressing\n"); 217 218 #if NRND > 0 219 rnd_attach_source(&wd->rnd_source, wd->sc_dev.dv_xname, RND_TYPE_DISK); 220 #endif 221 } 222 223 /* 224 * Read/write routine for a buffer. Validates the arguments and schedules the 225 * transfer. Does not wait for the transfer to complete. 226 */ 227 void 228 wdstrategy(bp) 229 struct buf *bp; 230 { 231 struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(bp->b_dev)]; 232 struct wd_link *d_link= wd->d_link; 233 int s; 234 235 /* Valid request? */ 236 if (bp->b_blkno < 0 || 237 (bp->b_bcount % wd->sc_dk.dk_label->d_secsize) != 0 || 238 (bp->b_bcount / wd->sc_dk.dk_label->d_secsize) >= (1 << NBBY)) { 239 bp->b_error = EINVAL; 240 goto bad; 241 } 242 243 /* If device invalidated (e.g. media change, door open), error. */ 244 if ((d_link->sc_flags & WDF_LOADED) == 0) { 245 bp->b_error = EIO; 246 goto bad; 247 } 248 249 /* If it's a null transfer, return immediately. */ 250 if (bp->b_bcount == 0) 251 goto done; 252 253 /* 254 * Do bounds checking, adjust transfer. if error, process. 255 * If end of partition, just return. 256 */ 257 if (WDPART(bp->b_dev) != RAW_PART && 258 bounds_check_with_label(bp, wd->sc_dk.dk_label, 259 (d_link->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0) 260 goto done; 261 262 /* Queue transfer on drive, activate drive and controller if idle. */ 263 s = splbio(); 264 disksort(&wd->sc_q, bp); 265 wdstart(wd); 266 splx(s); 267 return; 268 269 bad: 270 bp->b_flags |= B_ERROR; 271 done: 272 /* Toss transfer; we're done early. */ 273 bp->b_resid = bp->b_bcount; 274 biodone(bp); 275 } 276 277 /* 278 * Queue a drive for I/O. 279 */ 280 void 281 wdstart(arg) 282 void *arg; 283 { 284 struct wd_softc *wd = arg; 285 struct buf *dp, *bp=0; 286 struct wd_link *d_link = wd->d_link; 287 struct wdc_xfer *xfer; 288 u_long p_offset; 289 290 while (d_link->openings > 0) { 291 292 /* Is there a buf for us ? */ 293 dp = &wd->sc_q; 294 if ((bp = dp->b_actf) == NULL) /* yes, an assign */ 295 return; 296 dp->b_actf = bp->b_actf; 297 298 /* 299 * Make the command. First lock the device 300 */ 301 d_link->openings--; 302 if (WDPART(bp->b_dev) != RAW_PART) 303 p_offset = 304 wd->sc_dk.dk_label->d_partitions[WDPART(bp->b_dev)].p_offset; 305 else 306 p_offset = 0; 307 308 xfer = wdc_get_xfer(0); 309 if (xfer == NULL) 310 panic("wdc_xfer"); 311 312 xfer->d_link = d_link; 313 xfer->c_bp = bp; 314 xfer->c_p_offset = p_offset; 315 xfer->databuf = bp->b_data; 316 xfer->c_bcount = bp->b_bcount; 317 xfer->c_flags |= bp->b_flags & (B_READ|B_WRITE); 318 xfer->c_blkno = bp->b_blkno; 319 320 /* Instrumentation. */ 321 disk_busy(&wd->sc_dk); 322 wdc_exec_xfer((struct wdc_softc *)wd->d_link->wdc_softc, 323 wd->d_link, xfer); 324 } 325 } 326 327 int 328 wdread(dev, uio, flags) 329 dev_t dev; 330 struct uio *uio; 331 int flags; 332 { 333 334 WDDEBUG_PRINT(("wdread\n")); 335 return (physio(wdstrategy, NULL, dev, B_READ, minphys, uio)); 336 } 337 338 int 339 wdwrite(dev, uio, flags) 340 dev_t dev; 341 struct uio *uio; 342 int flags; 343 { 344 345 WDDEBUG_PRINT(("wdwrite\n")); 346 return (physio(wdstrategy, NULL, dev, B_WRITE, minphys, uio)); 347 } 348 349 /* 350 * Wait interruptibly for an exclusive lock. 351 * 352 * XXX 353 * Several drivers do this; it should be abstracted and made MP-safe. 354 */ 355 int 356 wdlock(d_link) 357 struct wd_link *d_link; 358 { 359 int error; 360 int s; 361 362 WDDEBUG_PRINT(("wdlock\n")); 363 364 s = splbio(); 365 366 while ((d_link->sc_flags & WDF_LOCKED) != 0) { 367 d_link->sc_flags |= WDF_WANTED; 368 if ((error = tsleep(d_link, PRIBIO | PCATCH, 369 "wdlck", 0)) != 0) { 370 splx(s); 371 return error; 372 } 373 } 374 d_link->sc_flags |= WDF_LOCKED; 375 splx(s); 376 return 0; 377 } 378 379 /* 380 * Unlock and wake up any waiters. 381 */ 382 void 383 wdunlock(d_link) 384 struct wd_link *d_link; 385 { 386 387 WDDEBUG_PRINT(("wdunlock")); 388 389 d_link->sc_flags &= ~WDF_LOCKED; 390 if ((d_link->sc_flags & WDF_WANTED) != 0) { 391 d_link->sc_flags &= ~WDF_WANTED; 392 wakeup(d_link); 393 } 394 } 395 396 int 397 wdopen(dev, flag, fmt, p) 398 dev_t dev; 399 int flag, fmt; 400 struct proc *p; 401 { 402 struct wd_softc *wd; 403 struct wd_link *d_link; 404 int unit, part; 405 int error; 406 407 WDDEBUG_PRINT(("wdopen\n")); 408 409 unit = WDUNIT(dev); 410 if (unit >= wd_cd.cd_ndevs) 411 return ENXIO; 412 wd = wd_cd.cd_devs[unit]; 413 if (wd == NULL) 414 return ENXIO; 415 416 d_link = wd->d_link; 417 if ((error = wdlock(d_link)) != 0) 418 return error; 419 420 if (wd->sc_dk.dk_openmask != 0) { 421 /* 422 * If any partition is open, but the disk has been invalidated, 423 * disallow further opens. 424 */ 425 if ((d_link->sc_flags & WDF_LOADED) == 0) { 426 error = EIO; 427 goto bad3; 428 } 429 } else { 430 if ((d_link->sc_flags & WDF_LOADED) == 0) { 431 d_link->sc_flags |= WDF_LOADED; 432 433 /* Load the physical device parameters. */ 434 if (wdc_get_parms((struct wdc_softc *)d_link->wdc_softc, 435 d_link) != 0) { 436 error = ENXIO; 437 goto bad2; 438 } 439 440 /* Load the partition info if not already loaded. */ 441 wdgetdisklabel(wd); 442 } 443 } 444 445 part = WDPART(dev); 446 447 /* Check that the partition exists. */ 448 if (part != RAW_PART && 449 (part >= wd->sc_dk.dk_label->d_npartitions || 450 wd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { 451 error = ENXIO; 452 goto bad; 453 } 454 455 /* Insure only one open at a time. */ 456 switch (fmt) { 457 case S_IFCHR: 458 wd->sc_dk.dk_copenmask |= (1 << part); 459 break; 460 case S_IFBLK: 461 wd->sc_dk.dk_bopenmask |= (1 << part); 462 break; 463 } 464 wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; 465 466 wdunlock(d_link); 467 return 0; 468 469 bad2: 470 d_link->sc_flags &= ~WDF_LOADED; 471 472 bad: 473 if (wd->sc_dk.dk_openmask == 0) { 474 } 475 476 bad3: 477 wdunlock(d_link); 478 return error; 479 } 480 481 int 482 wdclose(dev, flag, fmt, p) 483 dev_t dev; 484 int flag, fmt; 485 struct proc *p; 486 { 487 struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)]; 488 int part = WDPART(dev); 489 int error; 490 491 if ((error = wdlock(wd->d_link)) != 0) 492 return error; 493 494 switch (fmt) { 495 case S_IFCHR: 496 wd->sc_dk.dk_copenmask &= ~(1 << part); 497 break; 498 case S_IFBLK: 499 wd->sc_dk.dk_bopenmask &= ~(1 << part); 500 break; 501 } 502 wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; 503 504 if (wd->sc_dk.dk_openmask == 0) { 505 /* XXXX Must wait for I/O to complete! */ 506 } 507 508 wdunlock(wd->d_link); 509 return 0; 510 } 511 512 void 513 wdgetdefaultlabel(wd, lp) 514 struct wd_softc *wd; 515 struct disklabel *lp; 516 { 517 struct wd_link *d_link = wd->d_link; 518 519 bzero(lp, sizeof(struct disklabel)); 520 521 lp->d_secsize = DEV_BSIZE; 522 lp->d_ntracks = d_link->sc_params.wdp_heads; 523 lp->d_nsectors = d_link->sc_params.wdp_sectors; 524 lp->d_ncylinders = d_link->sc_params.wdp_cylinders; 525 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 526 527 #if 0 528 strncpy(lp->d_typename, "ST506 disk", 16); 529 lp->d_type = DTYPE_ST506; 530 #endif 531 strncpy(lp->d_packname, d_link->sc_params.wdp_model, 16); 532 lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; 533 lp->d_rpm = 3600; 534 lp->d_interleave = 1; 535 lp->d_flags = 0; 536 537 lp->d_partitions[RAW_PART].p_offset = 0; 538 lp->d_partitions[RAW_PART].p_size = 539 lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 540 lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 541 lp->d_npartitions = RAW_PART + 1; 542 543 lp->d_magic = DISKMAGIC; 544 lp->d_magic2 = DISKMAGIC; 545 lp->d_checksum = dkcksum(lp); 546 } 547 548 /* 549 * Fabricate a default disk label, and try to read the correct one. 550 */ 551 void 552 wdgetdisklabel(wd) 553 struct wd_softc *wd; 554 { 555 struct disklabel *lp = wd->sc_dk.dk_label; 556 struct wd_link *d_link = wd->d_link; 557 char *errstring; 558 559 WDDEBUG_PRINT(("wdgetdisklabel\n")); 560 561 bzero(wd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel)); 562 563 wdgetdefaultlabel(wd, lp); 564 565 d_link->sc_badsect[0] = -1; 566 567 if (d_link->sc_state > RECAL) 568 d_link->sc_state = RECAL; 569 errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART), 570 wdstrategy, lp, wd->sc_dk.dk_cpulabel); 571 if (errstring) { 572 /* 573 * This probably happened because the drive's default 574 * geometry doesn't match the DOS geometry. We 575 * assume the DOS geometry is now in the label and try 576 * again. XXX This is a kluge. 577 */ 578 if (d_link->sc_state > GEOMETRY) 579 d_link->sc_state = GEOMETRY; 580 errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART), 581 wdstrategy, lp, wd->sc_dk.dk_cpulabel); 582 } 583 if (errstring) { 584 printf("%s: %s\n", wd->sc_dev.dv_xname, errstring); 585 return; 586 } 587 588 if (d_link->sc_state > GEOMETRY) 589 d_link->sc_state = GEOMETRY; 590 if ((lp->d_flags & D_BADSECT) != 0) 591 bad144intern(wd); 592 } 593 594 595 /* 596 * Tell the drive what geometry to use. 597 */ 598 int 599 wdsetctlr(d_link) 600 struct wd_link *d_link; 601 { 602 struct wd_softc *wd=(struct wd_softc *)d_link->wd_softc; 603 604 WDDEBUG_PRINT(("wd(%d,%d) C%dH%dS%d\n", wd->sc_dev.dv_unit, 605 d_link->drive, wd->sc_dk.dk_label->d_ncylinders, 606 wd->sc_dk.dk_label->d_ntracks, wd->sc_dk.dk_label->d_nsectors)); 607 608 if (wdccommand((struct wdc_softc *)d_link->wdc_softc, 609 d_link, WDCC_IDP, d_link->drive, 610 wd->sc_dk.dk_label->d_ncylinders, 611 wd->sc_dk.dk_label->d_ntracks - 1, 0, 612 wd->sc_dk.dk_label->d_nsectors) != 0) { 613 wderror(d_link, NULL, "wdsetctlr: geometry upload failed"); 614 return -1; 615 } 616 617 return 0; 618 } 619 620 int 621 wdioctl(dev, xfer, addr, flag, p) 622 dev_t dev; 623 u_long xfer; 624 caddr_t addr; 625 int flag; 626 struct proc *p; 627 { 628 struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)]; 629 struct wd_link *d_link = wd->d_link; 630 int error; 631 632 WDDEBUG_PRINT(("wdioctl\n")); 633 634 if ((d_link->sc_flags & WDF_LOADED) == 0) 635 return EIO; 636 637 switch (xfer) { 638 case DIOCSBAD: 639 if ((flag & FWRITE) == 0) 640 return EBADF; 641 wd->sc_dk.dk_cpulabel->bad = *(struct dkbad *)addr; 642 wd->sc_dk.dk_label->d_flags |= D_BADSECT; 643 bad144intern(wd); 644 return 0; 645 646 case DIOCGDINFO: 647 *(struct disklabel *)addr = *(wd->sc_dk.dk_label); 648 return 0; 649 650 case DIOCGPART: 651 ((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label; 652 ((struct partinfo *)addr)->part = 653 &wd->sc_dk.dk_label->d_partitions[WDPART(dev)]; 654 return 0; 655 656 case DIOCWDINFO: 657 case DIOCSDINFO: 658 if ((flag & FWRITE) == 0) 659 return EBADF; 660 661 if ((error = wdlock(wd->d_link)) != 0) 662 return error; 663 d_link->sc_flags |= WDF_LABELLING; 664 665 error = setdisklabel(wd->sc_dk.dk_label, 666 (struct disklabel *)addr, /*wd->sc_dk.dk_openmask : */0, 667 wd->sc_dk.dk_cpulabel); 668 if (error == 0) { 669 if (d_link->sc_state > GEOMETRY) 670 d_link->sc_state = GEOMETRY; 671 if (xfer == DIOCWDINFO) 672 error = writedisklabel(WDLABELDEV(dev), 673 wdstrategy, wd->sc_dk.dk_label, 674 wd->sc_dk.dk_cpulabel); 675 } 676 677 d_link->sc_flags &= ~WDF_LABELLING; 678 wdunlock(d_link); 679 return error; 680 681 case DIOCWLABEL: 682 if ((flag & FWRITE) == 0) 683 return EBADF; 684 if (*(int *)addr) 685 d_link->sc_flags |= WDF_WLABEL; 686 else 687 d_link->sc_flags &= ~WDF_WLABEL; 688 return 0; 689 690 case DIOCGDEFLABEL: 691 wdgetdefaultlabel(wd, (struct disklabel *)addr); 692 return 0; 693 694 #ifdef notyet 695 case DIOCWFORMAT: 696 if ((flag & FWRITE) == 0) 697 return EBADF; 698 { 699 register struct format_op *fop; 700 struct iovec aiov; 701 struct uio auio; 702 703 fop = (struct format_op *)addr; 704 aiov.iov_base = fop->df_buf; 705 aiov.iov_len = fop->df_count; 706 auio.uio_iov = &aiov; 707 auio.uio_iovcnt = 1; 708 auio.uio_resid = fop->df_count; 709 auio.uio_segflg = 0; 710 auio.uio_offset = 711 fop->df_startblk * wd->sc_dk.dk_label->d_secsize; 712 auio.uio_procp = p; 713 error = physio(wdformat, NULL, dev, B_WRITE, minphys, 714 &auio); 715 fop->df_count -= auio.uio_resid; 716 fop->df_reg[0] = wdc->sc_status; 717 fop->df_reg[1] = wdc->sc_error; 718 return error; 719 } 720 #endif 721 722 default: 723 return ENOTTY; 724 } 725 726 #ifdef DIAGNOSTIC 727 panic("wdioctl: impossible"); 728 #endif 729 } 730 731 #ifdef B_FORMAT 732 int 733 wdformat(struct buf *bp) 734 { 735 736 bp->b_flags |= B_FORMAT; 737 return wdstrategy(bp); 738 } 739 #endif 740 741 int 742 wdsize(dev) 743 dev_t dev; 744 { 745 struct wd_softc *wd; 746 int part, unit, omask; 747 int size; 748 749 WDDEBUG_PRINT(("wdsize\n")); 750 751 unit = WDUNIT(dev); 752 if (unit >= wd_cd.cd_ndevs) 753 return (-1); 754 wd = wd_cd.cd_devs[unit]; 755 if (wd == NULL) 756 return (-1); 757 758 part = WDPART(dev); 759 omask = wd->sc_dk.dk_openmask & (1 << part); 760 761 if (omask == 0 && wdopen(dev, 0, S_IFBLK, NULL) != 0) 762 return (-1); 763 if (wd->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) 764 size = -1; 765 else 766 size = wd->sc_dk.dk_label->d_partitions[part].p_size * 767 (wd->sc_dk.dk_label->d_secsize / DEV_BSIZE); 768 if (omask == 0 && wdclose(dev, 0, S_IFBLK, NULL) != 0) 769 return (-1); 770 return (size); 771 } 772 773 774 #ifndef __BDEVSW_DUMP_OLD_TYPE 775 /* #define WD_DUMP_NOT_TRUSTED if you just want to watch */ 776 static int wddoingadump; 777 static int wddumprecalibrated; 778 779 /* 780 * Dump core after a system crash. 781 * 782 * XXX: This needs work! Currently, it's a major hack: the 783 * use of wdc_softc is very bad and should go away. 784 */ 785 int 786 wddump(dev, blkno, va, size) 787 dev_t dev; 788 daddr_t blkno; 789 caddr_t va; 790 size_t size; 791 { 792 struct wd_softc *wd; /* disk unit to do the I/O */ 793 struct wdc_softc *wdc; /* disk controller to do the I/O */ 794 struct disklabel *lp; /* disk's disklabel */ 795 struct wd_link *d_link; 796 int unit, part; 797 int nblks; /* total number of sectors left to write */ 798 799 /* Check if recursive dump; if so, punt. */ 800 if (wddoingadump) 801 return EFAULT; 802 wddoingadump = 1; 803 804 unit = WDUNIT(dev); 805 if (unit >= wd_cd.cd_ndevs) 806 return ENXIO; 807 wd = wd_cd.cd_devs[unit]; 808 if (wd == (struct wd_softc *)0) 809 return ENXIO; 810 d_link = wd->d_link; 811 812 part = WDPART(dev); 813 814 /* Make sure it was initialized. */ 815 if (d_link->sc_state < READY) 816 return ENXIO; 817 818 wdc = (void *)wd->sc_dev.dv_parent; 819 820 /* Convert to disk sectors. Request must be a multiple of size. */ 821 lp = wd->sc_dk.dk_label; 822 if ((size % lp->d_secsize) != 0) 823 return EFAULT; 824 nblks = size / lp->d_secsize; 825 blkno = blkno / (lp->d_secsize / DEV_BSIZE); 826 827 /* Check transfer bounds against partition size. */ 828 if ((blkno < 0) || ((blkno + nblks) > lp->d_partitions[part].p_size)) 829 return EINVAL; 830 831 /* Offset block number to start of partition. */ 832 blkno += lp->d_partitions[part].p_offset; 833 834 /* Recalibrate, if first dump transfer. */ 835 if (wddumprecalibrated == 0) { 836 wddumprecalibrated = 1; 837 if (wdccommandshort(wdc, d_link->drive, WDCC_RECAL) != 0 || 838 wait_for_ready(wdc) != 0 || wdsetctlr(d_link) != 0 || 839 wait_for_ready(wdc) != 0) { 840 wderror(d_link, NULL, "wddump: recal failed"); 841 return EIO; 842 } 843 } 844 845 while (nblks > 0) { 846 daddr_t xlt_blkno = blkno; 847 long cylin, head, sector; 848 849 if ((lp->d_flags & D_BADSECT) != 0) { 850 long blkdiff; 851 int i; 852 853 for (i = 0; (blkdiff = d_link->sc_badsect[i]) != -1; i++) { 854 blkdiff -= xlt_blkno; 855 if (blkdiff < 0) 856 continue; 857 if (blkdiff == 0) { 858 /* Replace current block of transfer. */ 859 xlt_blkno = lp->d_secperunit - 860 lp->d_nsectors - i - 1; 861 } 862 break; 863 } 864 /* Tranfer is okay now. */ 865 } 866 867 if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) { 868 sector = (xlt_blkno >> 0) & 0xff; 869 cylin = (xlt_blkno >> 8) & 0xffff; 870 head = (xlt_blkno >> 24) & 0xf; 871 head |= WDSD_LBA; 872 } else { 873 sector = xlt_blkno % lp->d_nsectors; 874 sector++; /* Sectors begin with 1, not 0. */ 875 xlt_blkno /= lp->d_nsectors; 876 head = xlt_blkno % lp->d_ntracks; 877 xlt_blkno /= lp->d_ntracks; 878 cylin = xlt_blkno; 879 head |= WDSD_CHS; 880 } 881 882 #ifndef WD_DUMP_NOT_TRUSTED 883 if (wdccommand((struct wdc_softc *)d_link->wdc_softc, d_link, 884 WDCC_WRITE, d_link->drive, cylin, head, sector, 1) != 0 || 885 wait_for_drq(wdc) != 0) { 886 wderror(d_link, NULL, "wddump: write failed"); 887 return EIO; 888 } 889 890 /* XXX XXX XXX */ 891 outsw(wdc->sc_iobase + wd_data, va, lp->d_secsize >> 1); 892 893 /* Check data request (should be done). */ 894 if (wait_for_ready(wdc) != 0) { 895 wderror(d_link, NULL, 896 "wddump: timeout waiting for ready"); 897 return EIO; 898 } 899 #else /* WD_DUMP_NOT_TRUSTED */ 900 /* Let's just talk about this first... */ 901 printf("wd%d: dump addr 0x%x, cylin %d, head %d, sector %d\n", 902 unit, va, cylin, head, sector); 903 delay(500 * 1000); /* half a second */ 904 #endif 905 906 /* update block count */ 907 nblks -= 1; 908 blkno += 1; 909 va += lp->d_secsize; 910 } 911 912 wddoingadump = 0; 913 return 0; 914 } 915 #else /* __BDEVSW_DUMP_NEW_TYPE */ 916 917 918 int 919 wddump(dev, blkno, va, size) 920 dev_t dev; 921 daddr_t blkno; 922 caddr_t va; 923 size_t size; 924 { 925 926 /* Not implemented. */ 927 return ENXIO; 928 } 929 #endif /* __BDEVSW_DUMP_NEW_TYPE */ 930 931 /* 932 * Internalize the bad sector table. 933 */ 934 void 935 bad144intern(wd) 936 struct wd_softc *wd; 937 { 938 struct dkbad *bt = &wd->sc_dk.dk_cpulabel->bad; 939 struct disklabel *lp = wd->sc_dk.dk_label; 940 struct wd_link *d_link = wd->d_link; 941 int i = 0; 942 943 WDDEBUG_PRINT(("bad144intern\n")); 944 945 for (; i < 126; i++) { 946 if (bt->bt_bad[i].bt_cyl == 0xffff) 947 break; 948 d_link->sc_badsect[i] = 949 bt->bt_bad[i].bt_cyl * lp->d_secpercyl + 950 (bt->bt_bad[i].bt_trksec >> 8) * lp->d_nsectors + 951 (bt->bt_bad[i].bt_trksec & 0xff); 952 } 953 for (; i < 127; i++) 954 d_link->sc_badsect[i] = -1; 955 } 956 957 void 958 wderror(d_link, bp, msg) 959 struct wd_link *d_link; 960 struct buf *bp; 961 char *msg; 962 { 963 struct wd_softc *wd = (struct wd_softc *)d_link->wd_softc; 964 965 if (bp) { 966 diskerr(bp, "wd", msg, LOG_PRINTF, bp->b_bcount, 967 wd->sc_dk.dk_label); 968 printf("\n"); 969 } else 970 printf("%s: %s\n", wd->sc_dev.dv_xname, msg); 971 } 972 973 void 974 wddone(d_link, bp) 975 struct wd_link *d_link; 976 struct buf *bp; 977 { 978 struct wd_softc *wd = (void *)d_link->wd_softc; 979 980 disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid)); 981 #if NRND > 0 982 rnd_add_uint32(&wd->rnd_source, bp->b_blkno); 983 #endif 984 } 985