1 /*- 2 * Copyright (c) 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * William Jolitz. 7 * 8 * %sccs.include.386.c% 9 * 10 * @(#)wd.c 5.8 (Berkeley) 11/18/90 11 */ 12 13 #include "wd.h" 14 #if NWD > 0 15 16 #include "param.h" 17 #include "dkbad.h" 18 #include "systm.h" 19 #include "conf.h" 20 #include "file.h" 21 #include "dir.h" 22 #include "user.h" 23 #include "ioctl.h" 24 #include "disk.h" 25 #include "buf.h" 26 #include "vm.h" 27 #include "uio.h" 28 #include "machine/pte.h" 29 #include "machine/isa/isa_device.h" 30 #include "icu.h" 31 #include "wdreg.h" 32 #include "syslog.h" 33 34 #define RETRIES 5 /* number of retries before giving up */ 35 #define MAXTRANSFER 256 /* max size of transfer in page clusters */ 36 37 #define WDUNIT(dev) ((minor(dev) & 070) >> 3) 38 39 #define b_cylin b_resid /* cylinder number for doing IO to */ 40 /* shares an entry in the buf struct */ 41 42 /* 43 * Drive states. Used for open and format operations. 44 * States < OPEN (> 0) are transient, during an open operation. 45 * OPENRAW is used for unlabeled disks, and for floppies, to inhibit 46 * bad-sector forwarding. 47 */ 48 #define RAWDISK 8 /* raw disk operation, no translation*/ 49 #define ISRAWSTATE(s) (RAWDISK&(s)) /* are we in a raw state? */ 50 #define DISKSTATE(s) (~RAWDISK&(s)) /* are we in a given state regardless 51 of raw or cooked mode? */ 52 53 #define CLOSED 0 /* disk is closed. */ 54 /* "cooked" disk states */ 55 #define WANTOPEN 1 /* open requested, not started */ 56 #define RECAL 2 /* doing restore */ 57 #define RDLABEL 3 /* reading pack label */ 58 #define RDBADTBL 4 /* reading bad-sector table */ 59 #define OPEN 5 /* done with open */ 60 61 #define WANTOPENRAW (WANTOPEN|RAWDISK) /* raw WANTOPEN */ 62 #define RECALRAW (RECAL|RAWDISK) /* raw open, doing restore */ 63 #define OPENRAW (OPEN|RAWDISK) /* open, but unlabeled disk or floppy */ 64 65 66 /* 67 * The structure of a disk drive. 68 */ 69 struct disk { 70 struct disklabel dk_dd; /* device configuration data */ 71 long dk_bc; /* byte count left */ 72 short dk_skip; /* blocks already transferred */ 73 char dk_unit; /* physical unit number */ 74 char dk_sdh; /* sdh prototype */ 75 char dk_state; /* control state */ 76 u_char dk_status; /* copy of status reg. */ 77 u_char dk_error; /* copy of error reg. */ 78 short dk_open; /* open/closed refcnt */ 79 }; 80 81 /* 82 * This label is used as a default when initializing a new or raw disk. 83 * It really only lets us access the first track until we know more. 84 */ 85 struct disklabel dflt_sizes = { 86 DISKMAGIC, DTYPE_ST506, 87 { 88 512, /* sector size */ 89 36, /* # of sectors per track */ 90 15, /* # of tracks per cylinder */ 91 1224, /* # of cylinders per unit */ 92 36*15, /* # of sectors per cylinder */ 93 1224*15*36, /* # of sectors per unit */ 94 0 /* write precomp cylinder (none) */ 95 }, 96 21600, 0, /* A=root filesystem */ 97 21600, 40, 98 660890, 0, /* C=whole disk */ 99 216000, 80, 100 0, 0, 101 0, 0, 102 0, 0, 103 399600, 480 104 }; 105 106 static struct dkbad dkbad[NWD]; 107 struct disk wddrives[NWD] = {0}; /* table of units */ 108 struct buf wdtab = {0}; 109 struct buf wdutab[NWD] = {0}; /* head of queue per drive */ 110 struct buf rwdbuf[NWD] = {0}; /* buffers for raw IO */ 111 long wdxfer[NWD] = {0}; /* count of transfers */ 112 int writeprotected[NWD] = { 0 }; 113 int wdprobe(), wdattach(), wdintr(); 114 struct isa_driver wddriver = { 115 wdprobe, wdattach, "wd", 116 }; 117 #include "dbg.h" 118 119 static wdc; 120 /* 121 * Probe routine 122 */ 123 wdprobe(dvp) 124 struct isa_device *dvp; 125 { 126 wdc = dvp->id_iobase; 127 128 #ifdef lint 129 wdintr(0); 130 #endif 131 outb(wdc+wd_error, 0x5a) ; /* error register not writable */ 132 /*wdp->wd_cyl_hi = 0xff ;/* only two bits of cylhi are implemented */ 133 outb(wdc+wd_cyl_lo, 0xa5) ; /* but all of cyllo are implemented */ 134 if(inb(wdc+wd_error) != 0x5a /*&& wdp->wd_cyl_hi == 3*/ 135 && inb(wdc+wd_cyl_lo) == 0xa5) 136 return(1) ; 137 return (0); 138 } 139 140 /* 141 * attach each drive if possible. 142 */ 143 wdattach(dvp) 144 struct isa_device *dvp; 145 { 146 int unit = dvp->id_unit; 147 148 outb(wdc+wd_ctlr,12); 149 DELAY(1000); 150 outb(wdc+wd_ctlr,8); 151 } 152 153 /* Read/write routine for a buffer. Finds the proper unit, range checks 154 * arguments, and schedules the transfer. Does not wait for the transfer 155 * to complete. Multi-page transfers are supported. All I/O requests must 156 * be a multiple of a sector in length. 157 */ 158 wdstrategy(bp) 159 register struct buf *bp; /* IO operation to perform */ 160 { 161 register struct buf *dp; 162 register struct disk *du; /* Disk unit to do the IO. */ 163 long nblocks, cyloff, blknum; 164 int unit = WDUNIT(bp->b_dev), xunit = minor(bp->b_dev) & 7; 165 int s; 166 167 if ((unit >= NWD) || (bp->b_blkno < 0)) { 168 printf("wdstrat: unit = %d, blkno = %d, bcount = %d\n", 169 unit, bp->b_blkno, bp->b_bcount); 170 pg("wd:error in wdstrategy"); 171 bp->b_flags |= B_ERROR; 172 goto bad; 173 } 174 if (writeprotected[unit] && (bp->b_flags & B_READ) == 0) { 175 printf("wd%d: write protected\n", unit); 176 goto bad; 177 } 178 du = &wddrives[unit]; 179 if (DISKSTATE(du->dk_state) != OPEN) 180 goto q; 181 /* 182 * Convert DEV_BSIZE "blocks" to sectors. 183 * Note: doing the conversions this way limits the partition size 184 * to about 8 million sectors (1-8 Gb). 185 */ 186 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.dk_secsize; 187 if (((u_long) bp->b_blkno * DEV_BSIZE % du->dk_dd.dk_secsize != 0) || 188 bp->b_bcount >= MAXTRANSFER * CLBYTES) { 189 bp->b_flags |= B_ERROR; 190 goto bad; 191 } 192 nblocks = du->dk_dd.dk_partition[xunit].nblocks; 193 cyloff = du->dk_dd.dk_partition[xunit].cyloff; 194 if (blknum + (bp->b_bcount / du->dk_dd.dk_secsize) > nblocks) { 195 if (blknum == nblocks) 196 bp->b_resid = bp->b_bcount; 197 else 198 bp->b_flags |= B_ERROR; 199 goto bad; 200 } 201 bp->b_cylin = blknum / du->dk_dd.dk_secpercyl + cyloff; 202 q: 203 dp = &wdutab[unit]; 204 s = splhigh(); 205 disksort(dp, bp); 206 if (dp->b_active == 0) 207 wdustart(du); /* start drive if idle */ 208 if (wdtab.b_active == 0) 209 wdstart(s); /* start IO if controller idle */ 210 splx(s); 211 return; 212 213 bad: 214 bp->b_error = EINVAL; 215 biodone(bp); 216 } 217 218 /* Routine to queue a read or write command to the controller. The request is 219 * linked into the active list for the controller. If the controller is idle, 220 * the transfer is started. 221 */ 222 wdustart(du) 223 register struct disk *du; 224 { 225 register struct buf *bp, *dp; 226 227 dp = &wdutab[du->dk_unit]; 228 if (dp->b_active) 229 return; 230 bp = dp->b_actf; 231 if (bp == NULL) 232 return; 233 dp->b_forw = NULL; 234 if (wdtab.b_actf == NULL) /* link unit into active list */ 235 wdtab.b_actf = dp; 236 else 237 wdtab.b_actl->b_forw = dp; 238 wdtab.b_actl = dp; 239 dp->b_active = 1; /* mark the drive as busy */ 240 } 241 242 /* 243 * Controller startup routine. This does the calculation, and starts 244 * a single-sector read or write operation. Called to start a transfer, 245 * or from the interrupt routine to continue a multi-sector transfer. 246 * RESTRICTIONS: 247 * 1. The transfer length must be an exact multiple of the sector size. 248 */ 249 250 static wd_sebyse; 251 252 wdstart() 253 { 254 register struct disk *du; /* disk unit for IO */ 255 register struct buf *bp; 256 struct buf *dp; 257 register struct bt_bad *bt_ptr; 258 long blknum, pagcnt, cylin, head, sector; 259 long secpertrk, secpercyl, addr, i; 260 int minor_dev, unit, s; 261 262 loop: 263 dp = wdtab.b_actf; 264 if (dp == NULL) 265 return; 266 bp = dp->b_actf; 267 if (bp == NULL) { 268 wdtab.b_actf = dp->b_forw; 269 goto loop; 270 } 271 unit = WDUNIT(bp->b_dev); 272 du = &wddrives[unit]; 273 if (DISKSTATE(du->dk_state) <= RDLABEL) { 274 if (wdcontrol(bp)) { 275 dp->b_actf = bp->av_forw; 276 goto loop; /* done */ 277 } 278 return; 279 } 280 minor_dev = minor(bp->b_dev) & 7; 281 secpertrk = du->dk_dd.dk_nsectors; 282 secpercyl = du->dk_dd.dk_secpercyl; 283 /* 284 * Convert DEV_BSIZE "blocks" to sectors. 285 */ 286 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.dk_secsize 287 + du->dk_skip; 288 #ifdef WDDEBUG 289 if (du->dk_skip == 0) { 290 dprintf(DDSK,"\nwdstart %d: %s %d@%d; map ", unit, 291 (bp->b_flags & B_READ) ? "read" : "write", 292 bp->b_bcount, blknum); 293 } else { 294 dprintf(DDSK," %d)%x", du->dk_skip, inb(wdc+wd_altsts)); 295 } 296 #endif 297 298 addr = (int) bp->b_un.b_addr; 299 if(du->dk_skip==0) du->dk_bc = bp->b_bcount; 300 cylin = blknum / secpercyl; 301 head = (blknum % secpercyl) / secpertrk; 302 sector = blknum % secpertrk; 303 if (DISKSTATE(du->dk_state) == OPEN) 304 cylin += du->dk_dd.dk_partition[minor_dev].cyloff; 305 306 /* 307 * See if the current block is in the bad block list. 308 * (If we have one, and not formatting.) 309 */ 310 if (DISKSTATE(du->dk_state) == OPEN && wd_sebyse) 311 for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) { 312 if (bt_ptr->bt_cyl > cylin) 313 /* Sorted list, and we passed our cylinder. quit. */ 314 break; 315 if (bt_ptr->bt_cyl == cylin && 316 bt_ptr->bt_trksec == (head << 8) + sector) { 317 /* 318 * Found bad block. Calculate new block addr. 319 * This starts at the end of the disk (skip the 320 * last track which is used for the bad block list), 321 * and works backwards to the front of the disk. 322 */ 323 #ifdef WDDEBUG 324 dprintf(DDSK,"--- badblock code -> Old = %d; ", 325 blknum); 326 #endif 327 blknum = du->dk_dd.dk_secperunit - du->dk_dd.dk_nsectors 328 - (bt_ptr - dkbad[unit].bt_bad) - 1; 329 cylin = blknum / secpercyl; 330 head = (blknum % secpercyl) / secpertrk; 331 sector = blknum % secpertrk; 332 #ifdef WDDEBUG 333 dprintf(DDSK, "new = %d\n", blknum); 334 #endif 335 break; 336 } 337 } 338 sector += 1; /* sectors begin with 1, not 0 */ 339 340 wdtab.b_active = 1; /* mark controller active */ 341 342 if(du->dk_skip==0 || wd_sebyse) { 343 if(wdtab.b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += 512; 344 while ((inb(wdc+wd_status) & WDCS_BUSY) != 0) ; 345 /*while ((inb(wdc+wd_status) & WDCS_DRQ)) inb(wdc+wd_data);*/ 346 outb(wdc+wd_precomp, 0xff); 347 /*wr(wdc+wd_precomp, du->dk_dd.dk_precompcyl / 4);*/ 348 /*if (bp->b_flags & B_FORMAT) { 349 wr(wdc+wd_sector, du->dk_dd.dk_gap3); 350 wr(wdc+wd_seccnt, du->dk_dd.dk_nsectors); 351 } else {*/ 352 if(wd_sebyse) 353 outb(wdc+wd_seccnt, 1); 354 else 355 outb(wdc+wd_seccnt, ((du->dk_bc +511) / 512)); 356 outb(wdc+wd_sector, sector); 357 358 outb(wdc+wd_cyl_lo, cylin); 359 outb(wdc+wd_cyl_hi, cylin >> 8); 360 361 /* Set up the SDH register (select drive). */ 362 outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); 363 while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; 364 365 /*if (bp->b_flags & B_FORMAT) 366 wr(wdc+wd_command, WDCC_FORMAT); 367 else*/ 368 outb(wdc+wd_command, 369 (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE); 370 #ifdef WDDEBUG 371 dprintf(DDSK,"sector %d cylin %d head %d addr %x sts %x\n", 372 sector, cylin, head, addr, inb(wdc+wd_altsts)); 373 #endif 374 } 375 376 /* If this is a read operation, just go away until it's done. */ 377 if (bp->b_flags & B_READ) return; 378 379 /* Ready to send data? */ 380 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) 381 nulldev(); /* So compiler won't optimize out */ 382 383 /* ASSUMES CONTIGUOUS MEMORY */ 384 outsw (wdc+wd_data, addr+du->dk_skip*512, 256); 385 du->dk_bc -= 512; 386 } 387 388 /* 389 * these are globally defined so they can be found 390 * by the debugger easily in the case of a system crash 391 */ 392 daddr_t wd_errsector; 393 daddr_t wd_errbn; 394 unsigned char wd_errstat; 395 396 /* Interrupt routine for the controller. Acknowledge the interrupt, check for 397 * errors on the current operation, mark it done if necessary, and start 398 * the next request. Also check for a partially done transfer, and 399 * continue with the next chunk if so. 400 */ 401 wdintr() 402 { 403 register struct disk *du; 404 register struct buf *bp, *dp; 405 int status; 406 char partch ; 407 static shit[32]; 408 static wd_haderror; 409 410 /* Shouldn't need this, but it may be a slow controller. */ 411 while ((status = inb(wdc+wd_status)) & WDCS_BUSY) 412 nulldev(); 413 if (!wdtab.b_active) { 414 printf("wd: extra interrupt\n"); 415 return; 416 } 417 418 #ifdef WDDEBUG 419 dprintf(DDSK,"I "); 420 #endif 421 dp = wdtab.b_actf; 422 bp = dp->b_actf; 423 du = &wddrives[WDUNIT(bp->b_dev)]; 424 partch = "abcdefgh"[minor(bp->b_dev)&7] ; 425 if (DISKSTATE(du->dk_state) <= RDLABEL) { 426 if (wdcontrol(bp)) 427 goto done; 428 return; 429 } 430 if (status & (WDCS_ERR | WDCS_ECCCOR)) { 431 wd_errstat = inb(wdc+wd_error); /* save error status */ 432 #ifdef WDDEBUG 433 printf("status %x error %x\n", status, wd_errstat); 434 #endif 435 if(wd_sebyse == 0) { 436 wd_haderror = 1; 437 goto outt; 438 } 439 /*if (bp->b_flags & B_FORMAT) { 440 du->dk_status = status; 441 du->dk_error = wdp->wd_error; 442 bp->b_flags |= B_ERROR; 443 goto done; 444 }*/ 445 446 wd_errsector = (bp->b_cylin * du->dk_dd.dk_secpercyl) + 447 (((unsigned long) bp->b_blkno * DEV_BSIZE / 448 du->dk_dd.dk_secsize) % du->dk_dd.dk_secpercyl) + 449 du->dk_skip; 450 wd_errbn = bp->b_blkno 451 + du->dk_skip * du->dk_dd.dk_secsize / DEV_BSIZE ; 452 if (status & WDCS_ERR) { 453 if (++wdtab.b_errcnt < RETRIES) { 454 wdtab.b_active = 0; 455 /*while ((inb(wdc+wd_status) & WDCS_DRQ)) 456 insw(wdc+wd_data, &shit, sizeof(shit)/2);*/ 457 } else { 458 printf("wd%d%c: ", du->dk_unit, partch); 459 printf( 460 "hard %s error, sn %d bn %d status %b error %b\n", 461 (bp->b_flags & B_READ)? "read":"write", 462 wd_errsector, wd_errbn, status, WDCS_BITS, 463 wd_errstat, WDERR_BITS); 464 bp->b_flags |= B_ERROR; /* flag the error */ 465 } 466 } else 467 log(LOG_WARNING,"wd%d%c: soft ecc sn %d bn %d\n", 468 du->dk_unit, partch, wd_errsector, 469 wd_errbn); 470 } 471 outt: 472 473 /* 474 * If this was a successful read operation, fetch the data. 475 */ 476 if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) { 477 int chk, dummy; 478 479 chk = min(256,du->dk_bc/2); 480 /* Ready to receive data? */ 481 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) 482 nulldev(); 483 484 /*dprintf(DDSK,"addr %x\n", (int)bp->b_un.b_addr + du->dk_skip * 512);*/ 485 insw(wdc+wd_data,(int)bp->b_un.b_addr + du->dk_skip * 512 ,chk); 486 du->dk_bc -= 2*chk; 487 while (chk++ < 256) insw (wdc+wd_data,&dummy,1); 488 } 489 490 wdxfer[du->dk_unit]++; 491 if (wdtab.b_active) { 492 if ((bp->b_flags & B_ERROR) == 0) { 493 du->dk_skip++; /* Add to successful sectors. */ 494 if (wdtab.b_errcnt) { 495 log(LOG_WARNING, "wd%d%c: ", 496 du->dk_unit, partch); 497 log(LOG_WARNING, 498 "soft %s error, sn %d bn %d error %b retries %d\n", 499 (bp->b_flags & B_READ) ? "read" : "write", 500 wd_errsector, wd_errbn, wd_errstat, 501 WDERR_BITS, wdtab.b_errcnt); 502 } 503 wdtab.b_errcnt = 0; 504 505 /* see if more to transfer */ 506 /*if (du->dk_skip < (bp->b_bcount + 511) / 512) {*/ 507 if (du->dk_bc > 0 && wd_haderror == 0) { 508 wdstart(); 509 return; /* next chunk is started */ 510 } else if (wd_haderror && wd_sebyse == 0) { 511 du->dk_skip = 0; 512 wd_haderror = 0; 513 wd_sebyse = 1; 514 wdstart(); 515 return; /* redo xfer sector by sector */ 516 } 517 } 518 519 done: 520 wd_sebyse = 0; 521 /* done with this transfer, with or without error */ 522 wdtab.b_actf = dp->b_forw; 523 wdtab.b_errcnt = 0; 524 du->dk_skip = 0; 525 dp->b_active = 0; 526 dp->b_actf = bp->av_forw; 527 dp->b_errcnt = 0; 528 bp->b_resid = 0; 529 biodone(bp); 530 } 531 wdtab.b_active = 0; 532 if (dp->b_actf) 533 wdustart(du); /* requeue disk if more io to do */ 534 if (wdtab.b_actf) 535 wdstart(); /* start IO on next drive */ 536 } 537 538 /* 539 * Initialize a drive. 540 */ 541 wdopen(dev, flags) 542 dev_t dev; 543 int flags; 544 { 545 register unsigned int unit; 546 register struct buf *bp; 547 register struct disk *du; 548 struct dkbad *db; 549 int i, error = 0; 550 551 unit = WDUNIT(dev); 552 if (unit >= NWD) return (ENXIO) ; 553 du = &wddrives[unit]; 554 if (du->dk_open){ 555 du->dk_open++ ; 556 return(0); /* already is open, don't mess with it */ 557 } 558 #ifdef THE_BUG 559 if (du->dk_state && DISKSTATE(du->dk_state) <= OPEN) 560 return(0); 561 #endif 562 du->dk_unit = unit; 563 wdutab[unit].b_actf = NULL; 564 /*if (flags & O_NDELAY) 565 du->dk_state = WANTOPENRAW; 566 else*/ 567 du->dk_state = WANTOPEN; 568 /* 569 * Use the default sizes until we've read the label, 570 * or longer if there isn't one there. 571 */ 572 du->dk_dd = dflt_sizes; 573 574 /* 575 * Recal, read of disk label will be done in wdcontrol 576 * during first read operation. 577 */ 578 bp = geteblk(512); 579 bp->b_dev = dev & 0xff00; 580 bp->b_blkno = bp->b_bcount = 0; 581 bp->b_flags = B_READ; 582 wdstrategy(bp); 583 biowait(bp); 584 if (bp->b_flags & B_ERROR) { 585 u.u_error = 0; /* XXX */ 586 error = ENXIO; 587 du->dk_state = CLOSED; 588 goto done; 589 } 590 if (du->dk_state == OPENRAW) { 591 du->dk_state = OPENRAW; 592 goto done; 593 } 594 /* 595 * Read bad sector table into memory. 596 */ 597 i = 0; 598 do { 599 u.u_error = 0; /* XXX */ 600 bp->b_flags = B_BUSY | B_READ; 601 bp->b_blkno = du->dk_dd.dk_secperunit - du->dk_dd.dk_nsectors 602 + i; 603 if (du->dk_dd.dk_secsize > DEV_BSIZE) 604 bp->b_blkno *= du->dk_dd.dk_secsize / DEV_BSIZE; 605 else 606 bp->b_blkno /= DEV_BSIZE / du->dk_dd.dk_secsize; 607 bp->b_bcount = du->dk_dd.dk_secsize; 608 bp->b_cylin = du->dk_dd.dk_ncylinders - 1; 609 wdstrategy(bp); 610 biowait(bp); 611 } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 && 612 i < du->dk_dd.dk_nsectors); 613 db = (struct dkbad *)(bp->b_un.b_addr); 614 #define DKBAD_MAGIC 0x4321 615 if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 && 616 db->bt_flag == DKBAD_MAGIC) { 617 dkbad[unit] = *db; 618 du->dk_state = OPEN; 619 } else { 620 printf("wd%d: %s bad-sector file\n", unit, 621 (bp->b_flags & B_ERROR) ? "can't read" : "format error in"); 622 u.u_error = 0; /* XXX */ 623 /*error = ENXIO ;*/ 624 du->dk_state = OPENRAW; 625 } 626 done: 627 bp->b_flags = B_INVAL | B_AGE; 628 brelse(bp); 629 if (error == 0) 630 du->dk_open = 1; 631 return (error); 632 } 633 634 /* 635 * Implement operations other than read/write. 636 * Called from wdstart or wdintr during opens and formats. 637 * Uses finite-state-machine to track progress of operation in progress. 638 * Returns 0 if operation still in progress, 1 if completed. 639 */ 640 wdcontrol(bp) 641 register struct buf *bp; 642 { 643 register struct disk *du; 644 register unit; 645 unsigned char stat; 646 int s, cnt; 647 extern int bootdev, cyloffset; 648 649 du = &wddrives[WDUNIT(bp->b_dev)]; 650 unit = du->dk_unit; 651 switch (DISKSTATE(du->dk_state)) { 652 653 tryagainrecal: 654 case WANTOPEN: /* set SDH, step rate, do restore */ 655 #ifdef WDDEBUG 656 dprintf(DDSK,"wd%d: recal ", unit); 657 #endif 658 s = splbio(); /* not called from intr level ... */ 659 660 #ifdef notdef 661 /* some compaq controllers require this ... */ 662 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) 663 + du->dk_dd.dk_ntracks-1); 664 outb(wdc+wd_seccnt, du->dk_dd.dk_nsectors); 665 outb(wdc+wd_command, 0x91); 666 while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) nulldev(); 667 #endif 668 669 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 670 wdtab.b_active = 1; 671 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); 672 du->dk_state++; 673 splx(s); 674 return(0); 675 676 case RECAL: 677 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { 678 printf("wd%d: recal", du->dk_unit); 679 if (unit == 0) { 680 printf(": status %b error %b\n", 681 stat, WDCS_BITS, 682 inb(wdc+wd_error), WDERR_BITS); 683 if (++wdtab.b_errcnt < RETRIES) 684 goto tryagainrecal; 685 } 686 goto badopen; 687 } 688 wdtab.b_errcnt = 0; 689 if (ISRAWSTATE(du->dk_state)) { 690 du->dk_state = OPENRAW; 691 return(1); 692 } 693 retry: 694 #ifdef WDDEBUG 695 dprintf(DDSK,"rdlabel "); 696 #endif 697 if( cyloffset < 0 || cyloffset > 2048) cyloffset=0; 698 /* 699 * Read in sector 0 to get the pack label and geometry. 700 */ 701 outb(wdc+wd_precomp, 0xff);/* sometimes this is head bit 3 */ 702 outb(wdc+wd_seccnt, 1); 703 outb(wdc+wd_sector, 1); 704 /*if (bp->b_dev == bootdev) { 705 (wdc+wd_cyl_lo = cyloffset & 0xff; 706 (wdc+wd_cyl_hi = cyloffset >> 8; 707 } else { 708 (wdc+wd_cyl_lo = 0; 709 (wdc+wd_cyl_hi = 0; 710 }*/ 711 outb(wdc+wd_cyl_lo, (cyloffset & 0xff)); 712 outb(wdc+wd_cyl_hi, (cyloffset >> 8)); 713 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 714 outb(wdc+wd_command, WDCC_READ); 715 du->dk_state = RDLABEL; 716 return(0); 717 718 case RDLABEL: 719 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { 720 if (++wdtab.b_errcnt < RETRIES) 721 goto retry; 722 printf("wd%d: read label", unit); 723 goto badopen; 724 } 725 726 insw(wdc+wd_data, bp->b_un.b_addr, 256); 727 728 if (((struct disklabel *) 729 (bp->b_un.b_addr + LABELOFFSET))->dk_magic == DISKMAGIC) { 730 du->dk_dd = 731 * (struct disklabel *) (bp->b_un.b_addr + LABELOFFSET); 732 } else { 733 printf("wd%d: bad disk label\n", du->dk_unit); 734 du->dk_state = OPENRAW; 735 } 736 737 s = splbio(); /* not called from intr level ... */ 738 while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) nulldev(); 739 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) 740 + du->dk_dd.dk_ntracks-1); 741 outb(wdc+wd_cyl_lo, du->dk_dd.dk_ncylinders); 742 outb(wdc+wd_cyl_hi, du->dk_dd.dk_ncylinders>>8); 743 outb(wdc+wd_seccnt, du->dk_dd.dk_nsectors); 744 outb(wdc+wd_command, 0x91); 745 while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) nulldev(); 746 outb(wdc+wd_seccnt, 0); 747 splx(s); 748 749 if (du->dk_state == RDLABEL) 750 du->dk_state = RDBADTBL; 751 /* 752 * The rest of the initialization can be done 753 * by normal means. 754 */ 755 return(1); 756 757 default: 758 panic("wdcontrol %x", du->dk_state ); 759 } 760 /* NOTREACHED */ 761 762 badopen: 763 printf(": status %b error %b\n", 764 stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS); 765 du->dk_state = OPENRAW; 766 return(1); 767 } 768 769 wdclose(dev) 770 dev_t dev; 771 { struct disk *du; 772 773 du = &wddrives[WDUNIT(dev)]; 774 du->dk_open-- ; 775 /*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */ 776 } 777 778 wdioctl(dev,cmd,addr,flag) 779 dev_t dev; 780 caddr_t addr; 781 { 782 int unit = WDUNIT(dev); 783 register struct disk *du; 784 int error = 0; 785 struct uio auio; 786 struct iovec aiov; 787 /*int wdformat();*/ 788 789 du = &wddrives[unit]; 790 791 switch (cmd) { 792 793 case DIOCGDINFO: 794 *(struct disklabel *)addr = du->dk_dd; 795 break; 796 797 case DIOCGDINFOP: 798 *(struct disklabel **)addr = &(du->dk_dd); 799 break; 800 801 #ifdef notyet 802 case DIOCWFORMAT: 803 if ((flag & FWRITE) == 0) 804 error = EBADF; 805 else { 806 register struct format_op *fop; 807 808 fop = (struct format_op *)addr; 809 aiov.iov_base = fop->df_buf; 810 aiov.iov_len = fop->df_count; 811 auio.uio_iov = &aiov; 812 auio.uio_iovcnt = 1; 813 auio.uio_resid = fop->df_count; 814 auio.uio_segflg = 0; 815 auio.uio_offset = 816 fop->df_startblk * du->dk_dd.dk_secsize; 817 error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE, 818 minphys, &auio); 819 fop->df_count -= auio.uio_resid; 820 fop->df_reg[0] = du->dk_status; 821 fop->df_reg[1] = du->dk_error; 822 } 823 break; 824 #endif 825 826 default: 827 error = ENOTTY; 828 break; 829 } 830 return (error); 831 } 832 833 /*wdformat(bp) 834 struct buf *bp; 835 { 836 837 bp->b_flags |= B_FORMAT; 838 return (wdstrategy(bp)); 839 }*/ 840 841 /* 842 * Routines to do raw IO for a unit. 843 */ 844 wdread(dev, uio) /* character read routine */ 845 dev_t dev; 846 struct uio *uio; 847 { 848 int unit = WDUNIT(dev) ; 849 850 if (unit >= NWD) return(ENXIO); 851 return(physio(wdstrategy, &rwdbuf[unit], dev, B_READ, minphys, uio)); 852 } 853 854 855 wdwrite(dev, uio) /* character write routine */ 856 dev_t dev; 857 struct uio *uio; 858 { 859 int unit = WDUNIT(dev) ; 860 861 if (unit >= NWD) return(ENXIO); 862 return(physio(wdstrategy, &rwdbuf[unit], dev, B_WRITE, minphys, uio)); 863 } 864 865 wdsize(dev) 866 dev_t dev; 867 { 868 register unit = WDUNIT(dev) ; 869 register xunit = minor(dev) & 07; 870 register struct disk *du; 871 register val ; 872 873 return(21600); 874 #ifdef notdef 875 if (unit >= NWD) return(-1); 876 if (wddrives[unit].dk_state == 0) /*{ 877 val = wdopen (dev, 0) ; 878 if (val < 0) return (val) ; 879 }*/ return (-1) ; 880 du = &wddrives[unit]; 881 return((int)((u_long)du->dk_dd.dk_partition[xunit].nblocks * 882 du->dk_dd.dk_secsize / 512)); 883 #endif 884 } 885 886 wddump(dev) /* dump core after a system crash */ 887 dev_t dev; 888 { 889 #ifdef notyet 890 register struct disk *du; /* disk unit to do the IO */ 891 register struct wd1010 *wdp = (struct wd1010 *) VA_WD; 892 register struct bt_bad *bt_ptr; 893 long num; /* number of sectors to write */ 894 int unit, xunit; 895 long cyloff, blknum, blkcnt; 896 long cylin, head, sector; 897 long secpertrk, secpercyl, nblocks, i; 898 register char *addr; 899 char *end; 900 extern int dumplo, totalclusters; 901 static wddoingadump = 0 ; 902 903 addr = (char *) PA_RAM; /* starting address */ 904 /* size of memory to dump */ 905 num = totalclusters * CLSIZE - PA_RAM / PGSIZE; 906 unit = WDUNIT(dev) ; /* eventually support floppies? */ 907 xunit = minor(dev) & 7; /* file system */ 908 /* check for acceptable drive number */ 909 if (unit >= NWD) return(ENXIO); 910 911 du = &wddrives[unit]; 912 /* was it ever initialized ? */ 913 if (du->dk_state < OPEN) return (ENXIO) ; 914 915 /* Convert to disk sectors */ 916 num = (u_long) num * PGSIZE / du->dk_dd.dk_secsize; 917 918 /* check if controller active */ 919 /*if (wdtab.b_active) return(EFAULT); */ 920 if (wddoingadump) return(EFAULT); 921 922 secpertrk = du->dk_dd.dk_nsectors; 923 secpercyl = du->dk_dd.dk_secpercyl; 924 nblocks = du->dk_dd.dk_partition[xunit].nblocks; 925 cyloff = du->dk_dd.dk_partition[xunit].cyloff; 926 927 /* check transfer bounds against partition size */ 928 if ((dumplo < 0) || ((dumplo + num) >= nblocks)) 929 return(EINVAL); 930 931 /*wdtab.b_active = 1; /* mark controller active for if we 932 panic during the dump */ 933 wddoingadump = 1 ; i = 100000 ; 934 while ((wdp->wd_status & WDCS_BUSY) && (i-- > 0)) nulldev() ; 935 inb(wdc+wd_sdh = du->dk_sdh ; 936 inb(wdc+wd_command = WDCC_RESTORE | WD_STEP; 937 while (inb(wdc+wd_status & WDCS_BUSY) nulldev() ; 938 939 blknum = dumplo; 940 while (num > 0) { 941 #ifdef notdef 942 if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER; 943 if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl) 944 blkcnt = secpercyl - (blknum % secpercyl); 945 /* keep transfer within current cylinder */ 946 #endif 947 948 /* compute disk address */ 949 cylin = blknum / secpercyl; 950 head = (blknum % secpercyl) / secpertrk; 951 sector = blknum % secpertrk; 952 sector++; /* origin 1 */ 953 cylin += cyloff; 954 955 /* 956 * See if the current block is in the bad block list. 957 * (If we have one.) 958 */ 959 for (bt_ptr = dkbad[unit].bt_bad; 960 bt_ptr->bt_cyl != -1; bt_ptr++) { 961 if (bt_ptr->bt_cyl > cylin) 962 /* Sorted list, and we passed our cylinder. 963 quit. */ 964 break; 965 if (bt_ptr->bt_cyl == cylin && 966 bt_ptr->bt_trksec == (head << 8) + sector) { 967 /* 968 * Found bad block. Calculate new block addr. 969 * This starts at the end of the disk (skip the 970 * last track which is used for the bad block list), 971 * and works backwards to the front of the disk. 972 */ 973 blknum = (du->dk_dd.dk_secperunit) 974 - du->dk_dd.dk_nsectors 975 - (bt_ptr - dkbad[unit].bt_bad) - 1; 976 cylin = blknum / secpercyl; 977 head = (blknum % secpercyl) / secpertrk; 978 sector = blknum % secpertrk; 979 break; 980 } 981 982 /* select drive. */ 983 inb(wdc+wd_sdh = du->dk_sdh | (head&07); 984 while ((inb(wdc+wd_status & WDCS_READY) == 0) nulldev(); 985 986 /* transfer some blocks */ 987 inb(wdc+wd_sector = sector; 988 inb(wdc+wd_seccnt = 1; 989 inb(wdc+wd_cyl_lo = cylin; 990 if (du->dk_dd.dk_ntracks > 8) { 991 if (head > 7) 992 inb(wdc+wd_precomp = 0; /* set 3rd head bit */ 993 else 994 inb(wdc+wd_precomp = 0xff; /* set 3rd head bit */ 995 } 996 inb(wdc+wd_cyl_hi = cylin >> 8; 997 #ifdef notdef 998 /* lets just talk about this first...*/ 999 printf ("sdh 0%o sector %d cyl %d addr 0x%x\n", 1000 wdp->wd_sdh, wdp->wd_sector, 1001 wdp->wd_cyl_hi*256+wdp->wd_cyl_lo, addr) ; 1002 for (i=10000; i > 0 ; i--) 1003 ; 1004 continue; 1005 #endif 1006 inb(wdc+wd_command = WDCC_WRITE; 1007 1008 /* Ready to send data? */ 1009 while ((inb(wdc+wd_status & WDCS_DRQ) == 0) nulldev(); 1010 if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ; 1011 1012 end = (char *)addr + du->dk_dd.dk_secsize; 1013 for (; addr < end; addr += 8) { 1014 wdp->wd_data = addr[0]; 1015 wdp->wd_data = addr[1]; 1016 wdp->wd_data = addr[2]; 1017 wdp->wd_data = addr[3]; 1018 wdp->wd_data = addr[4]; 1019 wdp->wd_data = addr[5]; 1020 wdp->wd_data = addr[6]; 1021 wdp->wd_data = addr[7]; 1022 } 1023 if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ; 1024 /* Check data request (should be done). */ 1025 if (inb(wdc+wd_status & WDCS_DRQ) return(EIO) ; 1026 1027 /* wait for completion */ 1028 for ( i = 1000000 ; inb(wdc+wd_status & WDCS_BUSY ; i--) { 1029 if (i < 0) return (EIO) ; 1030 nulldev () ; 1031 } 1032 /* error check the xfer */ 1033 if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ; 1034 /* update block count */ 1035 num--; 1036 blknum++ ; 1037 #ifdef WDDEBUG 1038 if (num % 100 == 0) printf(".") ; 1039 #endif 1040 } 1041 return(0); 1042 #endif 1043 } 1044 #endif 1045