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 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * from: @(#)wd.c 7.2 (Berkeley) 5/9/91 37 * $Id: wd.c,v 1.25 1993/07/28 02:21:37 cgd Exp $ 38 */ 39 40 /* Note: This code heavily modified by tih@barsoom.nhh.no; use at own risk! */ 41 /* The following defines represent only a very small part of the mods, most */ 42 /* of them are not marked in any way. -tih */ 43 44 #define TIHMODS /* wdopen() workaround, some splx() calls */ 45 #define QUIETWORKS /* define this when wdopen() can actually set DKFL_QUIET */ 46 #define INSTRUMENT /* Add instrumentation stuff by Brad Parker */ 47 #define TIPCAT /* theo says: whatever it is, it looks important! */ 48 49 /* TODO: peel out buffer at low ipl, speed improvement */ 50 /* TODO: find and fix the timing bugs apparent on some controllers */ 51 52 #include "wd.h" 53 #if NWDC > 0 54 55 #include "param.h" 56 #include "dkbad.h" 57 #include "systm.h" 58 #include "conf.h" 59 #include "file.h" 60 #include "stat.h" 61 #include "ioctl.h" 62 #include "disklabel.h" 63 #include "buf.h" 64 #include "uio.h" 65 #include "malloc.h" 66 #include "machine/cpu.h" 67 #ifdef INSTRUMENT 68 #include "sys/dkstat.h" 69 #endif 70 #include "i386/isa/isa.h" 71 #include "i386/isa/isa_device.h" 72 #include "i386/isa/icu.h" 73 #include "i386/isa/wdreg.h" 74 #include "syslog.h" 75 #include "vm/vm.h" 76 77 #ifndef WDCNDELAY 78 #define WDCNDELAY 400000 /* delay = 25us; so 10s for a controller state change */ 79 #endif 80 #define WDCDELAY 25 81 82 /* if you enable this, it will report any delays more than 25us * N long */ 83 /*#define WDCNDELAY_DEBUG 6 */ 84 85 #define WDIORETRIES 5 /* number of retries before giving up */ 86 87 #define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */ 88 #define wddospart(dev) (minor(dev) & 0x40) /* use dos partitions */ 89 #define wdunit(dev) ((minor(dev) & 0x38) >> 3) 90 #define wdpart(dev) (minor(dev) & 0x7) 91 #define makewddev(maj, unit, part) (makedev(maj,((unit<<3)+part))) 92 #define WDRAW 3 /* 'd' partition isn't a partition! */ 93 94 #define b_cylin b_resid /* cylinder number for doing IO to */ 95 /* shares an entry in the buf struct */ 96 97 /* 98 * Drive states. Used to initialize drive. 99 */ 100 101 #define CLOSED 0 /* disk is closed. */ 102 #define WANTOPEN 1 /* open requested, not started */ 103 #define RECAL 2 /* doing restore */ 104 #define OPEN 3 /* done with open */ 105 106 /* 107 * The structure of a disk drive. 108 */ 109 struct disk { 110 long dk_bc; /* byte count left */ 111 long dk_bct; /* total byte count left */ 112 short dk_skip; /* blocks already transferred */ 113 short dk_skipm; /* blocks already transferred for multi */ 114 char dk_ctrlr; /* physical controller number */ 115 char dk_unit; /* physical unit number */ 116 char dk_lunit; /* logical unit number */ 117 char dk_state; /* control state */ 118 u_char dk_status; /* copy of status reg. */ 119 u_char dk_error; /* copy of error reg. */ 120 short dk_port; /* i/o port base */ 121 122 u_long dk_copenpart; /* character units open on this drive */ 123 u_long dk_bopenpart; /* block units open on this drive */ 124 u_long dk_openpart; /* all units open on this drive */ 125 short dk_wlabel; /* label writable? */ 126 short dk_flags; /* drive characteistics found */ 127 #define DKFL_DOSPART 0x00001 /* has DOS partition table */ 128 #define DKFL_QUIET 0x00002 /* report errors back, but don't complain */ 129 #define DKFL_SINGLE 0x00004 /* sector at a time mode */ 130 #define DKFL_ERROR 0x00008 /* processing a disk error */ 131 #define DKFL_BSDLABEL 0x00010 /* has a BSD disk label */ 132 #define DKFL_BADSECT 0x00020 /* has a bad144 badsector table */ 133 #define DKFL_WRITEPROT 0x00040 /* manual unit write protect */ 134 struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */ 135 struct disklabel dk_dd; /* device configuration data */ 136 struct cpu_disklabel dk_cpd; 137 long dk_badsect[127]; /* 126 plus trailing -1 marker */ 138 }; 139 140 struct board { 141 short dkc_port; 142 }; 143 144 void bad144intern(struct disk *); 145 void wddisksort(); 146 147 struct board wdcontroller[NWDC]; 148 struct disk *wddrives[NWD]; /* table of units */ 149 struct buf wdtab[NWDC]; /* various per-controller info */ 150 struct buf wdutab[NWD]; /* head of queue per drive */ 151 struct buf rwdbuf[NWD]; /* buffers for raw IO */ 152 long wdxfer[NWD]; /* count of transfers */ 153 154 int wdprobe(), wdattach(); 155 156 struct isa_driver wdcdriver = { 157 wdprobe, wdattach, "wdc", 158 }; 159 160 void wdustart(struct disk *); 161 void wdstart(int); 162 int wdcommand(struct disk *, int); 163 int wdcontrol(struct buf *); 164 int wdsetctlr(dev_t, struct disk *); 165 int wdgetctlr(int, struct disk *); 166 167 /* 168 * Probe for controller. 169 */ 170 int 171 wdprobe(struct isa_device *dvp) 172 { 173 struct disk *du; 174 int wdc; 175 176 if (dvp->id_unit >= NWDC) 177 return(0); 178 179 du = (struct disk *) malloc (sizeof(struct disk), M_TEMP, M_NOWAIT); 180 bzero(du, sizeof(struct disk)); 181 182 du->dk_ctrlr = dvp->id_unit; 183 du->dk_unit = 0; 184 du->dk_lunit = 0; 185 wdcontroller[dvp->id_unit].dkc_port = dvp->id_iobase; 186 187 wdc = du->dk_port = dvp->id_iobase; 188 189 /* check if we have registers that work */ 190 outb(wdc+wd_error, 0x5a); /* error register not writable */ 191 outb(wdc+wd_cyl_lo, 0xa5); /* but all of cyllo are implemented */ 192 if(inb(wdc+wd_error) == 0x5a || inb(wdc+wd_cyl_lo) != 0xa5) 193 goto nodevice; 194 195 wdreset(dvp->id_unit, wdc, 0); 196 197 /* execute a controller only command */ 198 if (wdcommand(du, WDCC_DIAGNOSE) < 0) 199 goto nodevice; 200 201 bzero(&wdtab[du->dk_ctrlr], sizeof(struct buf)); 202 203 free(du, M_TEMP); 204 return (8); 205 206 nodevice: 207 free(du, M_TEMP); 208 return (0); 209 } 210 211 /* 212 * Called for the controller too 213 * Attach each drive if possible. 214 */ 215 int 216 wdattach(struct isa_device *dvp) 217 { 218 int unit, lunit; 219 struct disk *du; 220 221 if (dvp->id_masunit == -1) 222 return(0); 223 if (dvp->id_masunit >= NWDC) 224 return(0); 225 226 lunit = dvp->id_unit; 227 if (lunit == -1) { 228 printf("wdc%d: cannot support unit ?\n", dvp->id_masunit); 229 return 0; 230 } 231 if (lunit >= NWD) 232 return(0); 233 unit = dvp->id_physid; 234 235 du = wddrives[lunit] = (struct disk *) 236 malloc(sizeof(struct disk), M_TEMP, M_NOWAIT); 237 bzero(du, sizeof(struct disk)); 238 bzero(&wdutab[lunit], sizeof(struct buf)); 239 bzero(&rwdbuf[lunit], sizeof(struct buf)); 240 wdxfer[lunit] = 0; 241 242 du->dk_ctrlr = dvp->id_masunit; 243 du->dk_unit = unit; 244 du->dk_lunit = lunit; 245 du->dk_port = wdcontroller[dvp->id_masunit].dkc_port; 246 247 if(wdgetctlr(unit, du) == 0) { 248 int i, blank; 249 250 printf("wd%d at wdc%d targ %d: ", 251 dvp->id_unit, dvp->id_masunit, dvp->id_physid); 252 if(du->dk_params.wdp_heads==0) 253 printf("(unknown size) <"); 254 else 255 printf("%dMB %d cyl, %d head, %d sec <", 256 du->dk_dd.d_ncylinders * du->dk_dd.d_secpercyl / 2048, 257 du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, 258 du->dk_dd.d_nsectors); 259 for (i=blank=0; i<sizeof(du->dk_params.wdp_model); i++) { 260 char c = du->dk_params.wdp_model[i]; 261 if (blank && c == ' ') 262 continue; 263 if (blank && c != ' ') { 264 printf(" %c", c); 265 blank = 0; 266 continue; 267 } 268 if (c == ' ') 269 blank = 1; 270 else 271 printf("%c", c); 272 } 273 printf(">\n"); 274 } else { 275 /*printf("wd%d at wdc%d slave %d -- error\n", 276 lunit, dvp->id_masunit, unit);*/ 277 wddrives[lunit] = 0; 278 free(du, M_TEMP); 279 return 0; 280 } 281 return 1; 282 } 283 284 /* Read/write routine for a buffer. Finds the proper unit, range checks 285 * arguments, and schedules the transfer. Does not wait for the transfer 286 * to complete. Multi-page transfers are supported. All I/O requests must 287 * be a multiple of a sector in length. 288 */ 289 int 290 wdstrategy(register struct buf *bp) 291 { 292 register struct buf *dp; 293 struct disk *du; /* Disk unit to do the IO. */ 294 int lunit = wdunit(bp->b_dev); 295 int s; 296 297 /* valid unit, controller, and request? */ 298 if (lunit >= NWD || bp->b_blkno < 0 || 299 howmany(bp->b_bcount, DEV_BSIZE) >= (1<<NBBY) || 300 (du = wddrives[lunit]) == 0) { 301 bp->b_error = EINVAL; 302 bp->b_flags |= B_ERROR; 303 goto done; 304 } 305 306 /* "soft" write protect check */ 307 if ((du->dk_flags & DKFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) { 308 bp->b_error = EROFS; 309 bp->b_flags |= B_ERROR; 310 goto done; 311 } 312 313 /* have partitions and want to use them? */ 314 if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW) { 315 /* 316 * do bounds checking, adjust transfer. if error, process. 317 * if end of partition, just return 318 */ 319 if (bounds_check_with_label(bp, &du->dk_dd, du->dk_wlabel) <= 0) 320 goto done; 321 /* otherwise, process transfer request */ 322 } 323 324 /* queue transfer on drive, activate drive and controller if idle */ 325 dp = &wdutab[lunit]; 326 s = splbio(); 327 wddisksort(dp, bp); 328 if (dp->b_active == 0) 329 wdustart(du); /* start drive */ 330 if (wdtab[du->dk_ctrlr].b_active == 0) 331 wdstart(du->dk_ctrlr); /* start controller */ 332 splx(s); 333 return 0; 334 335 done: 336 /* toss transfer, we're done early */ 337 biodone(bp); 338 return 0; 339 } 340 341 /* 342 * Routine to queue a command to the controller. The unit's 343 * request is linked into the active list for the controller. 344 * If the controller is idle, the transfer is started. 345 */ 346 static void 347 wdustart(register struct disk *du) 348 { 349 register struct buf *bp, *dp = &wdutab[du->dk_lunit]; 350 int ctrlr = du->dk_ctrlr; 351 352 /* unit already active? */ 353 if (dp->b_active) 354 return; 355 356 /* anything to start? */ 357 bp = dp->b_actf; 358 if (bp == NULL) 359 return; 360 361 /* link onto controller queue */ 362 dp->b_forw = NULL; 363 if (wdtab[ctrlr].b_actf == NULL) 364 wdtab[ctrlr].b_actf = dp; 365 else 366 wdtab[ctrlr].b_actl->b_forw = dp; 367 wdtab[ctrlr].b_actl = dp; 368 369 /* mark the drive unit as busy */ 370 dp->b_active = 1; 371 } 372 373 /* 374 * Controller startup routine. This does the calculation, and starts 375 * a single-sector read or write operation. Called to start a transfer, 376 * or from the interrupt routine to continue a multi-sector transfer. 377 * RESTRICTIONS: 378 * 1. The transfer length must be an exact multiple of the sector size. 379 */ 380 static void 381 wdstart(int ctrlr) 382 { 383 register struct disk *du; /* disk unit for IO */ 384 register struct buf *bp; 385 struct disklabel *lp; 386 struct buf *dp; 387 long blknum, cylin, head, sector; 388 long secpertrk, secpercyl, addr, timeout; 389 int lunit, wdc; 390 int xfrblknum; 391 unsigned char status; 392 393 loop: 394 /* is there a drive for the controller to do a transfer with? */ 395 dp = wdtab[ctrlr].b_actf; 396 if (dp == NULL) 397 return; 398 399 /* is there a transfer to this drive ? if so, link it on 400 the controller's queue */ 401 bp = dp->b_actf; 402 if (bp == NULL) { 403 wdtab[ctrlr].b_actf = dp->b_forw; 404 goto loop; 405 } 406 407 /* obtain controller and drive information */ 408 lunit = wdunit(bp->b_dev); 409 du = wddrives[lunit]; 410 411 /* if not really a transfer, do control operations specially */ 412 if (du->dk_state < OPEN) { 413 (void) wdcontrol(bp); 414 return; 415 } 416 417 /* calculate transfer details */ 418 blknum = bp->b_blkno + du->dk_skip; 419 #ifdef WDDEBUG 420 if (du->dk_skip == 0) 421 printf("\nwdstart %d: %s %d@%d; map ", lunit, 422 (bp->b_flags & B_READ) ? "read" : "write", 423 bp->b_bcount, blknum); 424 else 425 printf(" %d)%x", du->dk_skip, inb(du->dk_port+wd_altsts)); 426 #endif 427 addr = (int) bp->b_un.b_addr; 428 if (du->dk_skip == 0) { 429 du->dk_bc = bp->b_bcount; 430 } 431 if (du->dk_skipm == 0) { 432 struct buf *oldbp, *nextbp; 433 oldbp = bp; 434 nextbp = bp->av_forw; 435 du->dk_bct = du->dk_bc; 436 oldbp->b_flags |= B_XXX; 437 while( nextbp 438 && (oldbp->b_flags & DKFL_SINGLE) == 0 439 && oldbp->b_dev == nextbp->b_dev 440 && nextbp->b_blkno == (oldbp->b_blkno + (oldbp->b_bcount/DEV_BSIZE)) 441 && (oldbp->b_flags & B_READ) == (nextbp->b_flags & B_READ)) { 442 if( (du->dk_bct+nextbp->b_bcount)/DEV_BSIZE >= 240) { 443 break; 444 } 445 du->dk_bct += nextbp->b_bcount; 446 oldbp->b_flags |= B_XXX; 447 oldbp = nextbp; 448 nextbp = nextbp->av_forw; 449 } 450 } 451 452 lp = &du->dk_dd; 453 secpertrk = lp->d_nsectors; 454 secpercyl = lp->d_secpercyl; 455 if ((du->dk_flags & DKFL_BSDLABEL) != 0 && wdpart(bp->b_dev) != WDRAW) 456 blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset; 457 cylin = blknum / secpercyl; 458 head = (blknum % secpercyl) / secpertrk; 459 sector = blknum % secpertrk; 460 461 /* Check for bad sectors if we have them, and not formatting */ 462 /* Only do this in single-sector mode, or when starting a */ 463 /* multiple-sector transfer. */ 464 #ifdef B_FORMAT 465 if ((du->dk_flags & DKFL_BADSECT) && !(bp->b_flags & B_FORMAT) && 466 ((du->dk_skipm == 0) || (du->dk_flags & DKFL_SINGLE))) { 467 #else 468 if ((du->dk_flags & DKFL_BADSECT) && 469 ((du->dk_skipm == 0) || (du->dk_flags & DKFL_SINGLE))) { 470 #endif 471 472 long blkchk, blkend, blknew; 473 int i; 474 475 blkend = blknum + howmany(du->dk_bct, DEV_BSIZE) - 1; 476 for (i = 0; (blkchk = du->dk_badsect[i]) != -1; i++) { 477 if (blkchk > blkend) { 478 break; /* transfer is completely OK; done */ 479 } else if (blkchk == blknum) { 480 blknew = lp->d_secperunit - lp->d_nsectors - i - 1; 481 cylin = blknew / secpercyl; 482 head = (blknew % secpercyl) / secpertrk; 483 sector = blknew % secpertrk; 484 du->dk_flags |= DKFL_SINGLE; 485 /* found and replaced first blk of transfer; done */ 486 break; 487 } else if (blkchk > blknum) { 488 du->dk_flags |= DKFL_SINGLE; 489 break; /* bad block inside transfer; done */ 490 } 491 } 492 } 493 if( du->dk_flags & DKFL_SINGLE) { 494 du->dk_bct = du->dk_bc; 495 du->dk_skipm = du->dk_skip; 496 } 497 498 #ifdef WDDEBUG 499 pg("c%d h%d s%d ", cylin, head, sector); 500 #endif 501 502 sector += 1; /* sectors begin with 1, not 0 */ 503 504 wdtab[ctrlr].b_active = 1; /* mark controller active */ 505 wdc = du->dk_port; 506 507 #ifdef INSTRUMENT 508 /* instrumentation */ 509 if (du->dk_unit >= 0 && du->dk_skip == 0) { 510 dk_busy |= 1 << du->dk_lunit; 511 dk_wds[du->dk_lunit] += bp->b_bcount >> 6; 512 } 513 if (du->dk_unit >= 0 && du->dk_skipm == 0) { 514 ++dk_seek[du->dk_lunit]; 515 ++dk_xfer[du->dk_lunit]; 516 } 517 #endif 518 519 retry: 520 /* if starting a multisector transfer, or doing single transfers */ 521 if (du->dk_skipm == 0 || (du->dk_flags & DKFL_SINGLE)) { 522 if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0) { 523 du->dk_bc += DEV_BSIZE; 524 du->dk_bct += DEV_BSIZE; 525 } 526 527 /* controller idle? */ 528 for (timeout=0; inb(wdc+wd_status) & WDCS_BUSY; ) { 529 DELAY(WDCDELAY); 530 if (++timeout < WDCNDELAY) 531 continue; 532 wdreset(ctrlr, wdc, 1); 533 break; 534 } 535 #ifdef WDCNDELAY_DEBUG 536 if(timeout>WDCNDELAY_DEBUG) 537 printf("wdc%d: timeout took %dus\n", ctrlr, WDCDELAY * timeout); 538 #endif 539 540 /* stuff the task file */ 541 outb(wdc+wd_precomp, lp->d_precompcyl / 4); 542 #ifdef B_FORMAT 543 if (bp->b_flags & B_FORMAT) { 544 outb(wdc+wd_sector, lp->d_gap3); 545 outb(wdc+wd_seccnt, lp->d_nsectors); 546 } else { 547 if (du->dk_flags & DKFL_SINGLE) 548 outb(wdc+wd_seccnt, 1); 549 else 550 outb(wdc+wd_seccnt, howmany(du->dk_bct, DEV_BSIZE)); 551 outb(wdc+wd_sector, sector); 552 } 553 #else 554 if (du->dk_flags & DKFL_SINGLE) 555 outb(wdc+wd_seccnt, 1); 556 else 557 outb(wdc+wd_seccnt, howmany(du->dk_bct, DEV_BSIZE)); 558 outb(wdc+wd_sector, sector); 559 #endif 560 outb(wdc+wd_cyl_lo, cylin); 561 outb(wdc+wd_cyl_hi, cylin >> 8); 562 563 /* set up the SDH register (select drive) */ 564 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit<<4) | (head & 0xf)); 565 566 /* wait for drive to become ready */ 567 for (timeout=0; (inb(wdc+wd_status) & WDCS_READY) == 0; ) { 568 DELAY(WDCDELAY); 569 if (++timeout < WDCNDELAY) 570 continue; 571 wdreset(ctrlr, wdc, 1); 572 goto retry; 573 } 574 #ifdef WDCNDELAY_DEBUG 575 if(timeout>WDCNDELAY_DEBUG) 576 printf("wdc%d: timeout took %dus\n", ctrlr, WDCDELAY * timeout); 577 #endif 578 579 /* initiate command! */ 580 #ifdef B_FORMAT 581 if (bp->b_flags & B_FORMAT) 582 outb(wdc+wd_command, WDCC_FORMAT); 583 else 584 outb(wdc+wd_command, 585 (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE); 586 #else 587 outb(wdc+wd_command, (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE); 588 #endif 589 #ifdef WDDEBUG 590 printf("sector %d cylin %d head %d addr %x sts %x\n", 591 sector, cylin, head, addr, inb(wdc+wd_altsts)); 592 #endif 593 } 594 595 /* if this is a read operation, just go away until it's done. */ 596 if (bp->b_flags & B_READ) 597 return; 598 599 /* ready to send data? */ 600 for (timeout=0; (inb(wdc+wd_altsts) & WDCS_DRQ) == 0; ) { 601 DELAY(WDCDELAY); 602 if (++timeout < WDCNDELAY) 603 continue; 604 wdreset(ctrlr, wdc, 1); 605 goto retry; 606 } 607 #ifdef WDCNDELAY_DEBUG 608 if(timeout>WDCNDELAY_DEBUG) 609 printf("wdc%d: timeout took %dus\n", ctrlr, WDCDELAY * timeout); 610 #endif 611 612 /* then send it! */ 613 outagain: 614 outsw (wdc+wd_data, addr+du->dk_skip * DEV_BSIZE, 615 DEV_BSIZE/sizeof(short)); 616 du->dk_bc -= DEV_BSIZE; 617 du->dk_bct -= DEV_BSIZE; 618 } 619 620 /* Interrupt routine for the controller. Acknowledge the interrupt, check for 621 * errors on the current operation, mark it done if necessary, and start 622 * the next request. Also check for a partially done transfer, and 623 * continue with the next chunk if so. 624 */ 625 void 626 wdintr(struct intrframe wdif) 627 { 628 register struct disk *du; 629 register struct buf *bp, *dp; 630 int status, wdc, ctrlr; 631 632 ctrlr = wdif.if_vec; 633 634 if (!wdtab[ctrlr].b_active) { 635 printf("wdc%d: extra interrupt\n", ctrlr); 636 return; 637 } 638 639 dp = wdtab[ctrlr].b_actf; 640 bp = dp->b_actf; 641 du = wddrives[wdunit(bp->b_dev)]; 642 wdc = du->dk_port; 643 644 #ifdef WDDEBUG 645 printf("I%d ", ctrlr); 646 #endif 647 648 while ((status = inb(wdc+wd_status)) & WDCS_BUSY) 649 ; 650 651 /* is it not a transfer, but a control operation? */ 652 if (du->dk_state < OPEN) { 653 if (wdcontrol(bp)) 654 wdstart(ctrlr); 655 return; 656 } 657 658 /* have we an error? */ 659 if (status & (WDCS_ERR | WDCS_ECCCOR)) { 660 du->dk_status = status; 661 du->dk_error = inb(wdc + wd_error); 662 #ifdef WDDEBUG 663 printf("status %x error %x\n", status, du->dk_error); 664 #endif 665 if((du->dk_flags & DKFL_SINGLE) == 0) { 666 du->dk_flags |= DKFL_ERROR; 667 goto outt; 668 } 669 #ifdef B_FORMAT 670 if (bp->b_flags & B_FORMAT) { 671 bp->b_flags |= B_ERROR; 672 goto done; 673 } 674 #endif 675 676 /* error or error correction? */ 677 if (status & WDCS_ERR) { 678 if (++wdtab[ctrlr].b_errcnt < WDIORETRIES) { 679 wdtab[ctrlr].b_active = 0; 680 } else { 681 if((du->dk_flags & DKFL_QUIET) == 0) { 682 diskerr(bp, "wd", "hard error", 683 LOG_PRINTF, du->dk_skip, 684 &du->dk_dd); 685 #ifdef WDDEBUG 686 printf( "status %b error %b\n", 687 status, WDCS_BITS, 688 inb(wdc+wd_error), WDERR_BITS); 689 #endif 690 } 691 bp->b_flags |= B_ERROR; /* flag the error */ 692 } 693 } else if((du->dk_flags & DKFL_QUIET) == 0) { 694 diskerr(bp, "wd", "soft ecc", 0, 695 du->dk_skip, &du->dk_dd); 696 } 697 } 698 outt: 699 700 /* 701 * If this was a successful read operation, fetch the data. 702 */ 703 if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab[ctrlr].b_active) { 704 int chk, dummy; 705 706 chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short)); 707 708 /* ready to receive data? */ 709 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) 710 ; 711 712 /* suck in data */ 713 insw (wdc+wd_data, 714 (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, chk); 715 du->dk_bc -= chk * sizeof(short); 716 du->dk_bct -= chk * sizeof(short); 717 718 /* for obselete fractional sector reads */ 719 while (chk++ < (DEV_BSIZE / sizeof(short))) 720 insw(wdc+wd_data, &dummy, 1); 721 } 722 723 wdxfer[du->dk_lunit]++; 724 if (wdtab[ctrlr].b_active) { 725 #ifdef INSTRUMENT 726 if (du->dk_unit >= 0) 727 dk_busy &=~ (1 << du->dk_unit); 728 #endif 729 if ((bp->b_flags & B_ERROR) == 0) { 730 du->dk_skip++; /* Add to succ. sect */ 731 du->dk_skipm++; /* Add to succ. sect for multitransfer */ 732 if (wdtab[ctrlr].b_errcnt && (du->dk_flags & DKFL_QUIET) == 0) 733 diskerr(bp, "wd", "soft error", 0, 734 du->dk_skip, &du->dk_dd); 735 wdtab[ctrlr].b_errcnt = 0; 736 737 /* see if more to transfer */ 738 if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) { 739 if( (du->dk_flags & DKFL_SINGLE) 740 || (du->dk_flags & B_READ) == 0) { 741 wdstart(ctrlr); 742 return; /* next chunk is started */ 743 } 744 } else if ((du->dk_flags & (DKFL_SINGLE|DKFL_ERROR)) == DKFL_ERROR) { 745 du->dk_skip = 0; 746 du->dk_skipm = 0; 747 du->dk_flags &= ~DKFL_ERROR; 748 du->dk_flags |= DKFL_SINGLE; 749 wdstart(ctrlr); 750 return; /* redo xfer sector by sector */ 751 } 752 } 753 754 done: 755 /* done with this transfer, with or without error */ 756 du->dk_flags &= ~DKFL_SINGLE; 757 wdtab[ctrlr].b_errcnt = 0; 758 du->dk_skip = 0; 759 if( du->dk_bct == 0) { 760 wdtab[ctrlr].b_actf = dp->b_forw; 761 du->dk_skipm = 0; 762 dp->b_active = 0; 763 } 764 dp->b_actf = bp->av_forw; 765 dp->b_errcnt = 0; 766 bp->b_resid = 0; 767 bp->b_flags &= ~B_XXX; 768 biodone(bp); 769 } 770 771 /* anything more on drive queue? */ 772 if (dp->b_actf && du->dk_bct == 0) 773 wdustart(du); 774 775 /* anything more for controller to do? */ 776 if (wdtab[ctrlr].b_actf) 777 wdstart(ctrlr); 778 779 if (!wdtab[ctrlr].b_actf) 780 wdtab[ctrlr].b_active = 0; 781 } 782 783 /* 784 * Initialize a drive. 785 */ 786 int 787 wdopen(dev_t dev, int flags, int fmt, struct proc *p) 788 { 789 register unsigned int lunit; 790 register struct disk *du; 791 int part = wdpart(dev), mask = 1 << part; 792 struct partition *pp; 793 int error = 0; 794 char *msg; 795 796 lunit = wdunit(dev); 797 if (lunit >= NWD) 798 return (ENXIO); 799 800 du = wddrives[lunit]; 801 802 if (du == 0) 803 return (ENXIO); 804 805 #ifdef QUIETWORKS 806 if (part == WDRAW) 807 du->dk_flags |= DKFL_QUIET; 808 else 809 du->dk_flags &= ~DKFL_QUIET; 810 #else 811 du->dk_flags &= ~DKFL_QUIET; 812 #endif 813 814 if ((du->dk_flags & DKFL_BSDLABEL) == 0) { 815 du->dk_flags |= DKFL_WRITEPROT; 816 wdutab[lunit].b_actf = NULL; 817 818 /* 819 * Use the default sizes until we've read the label, 820 * or longer if there isn't one there. 821 */ 822 bzero(&du->dk_dd, sizeof(du->dk_dd)); 823 #undef d_type /* fix goddamn segments.h! XXX */ 824 du->dk_dd.d_type = DTYPE_ST506; 825 du->dk_dd.d_ncylinders = 1024; 826 du->dk_dd.d_secsize = DEV_BSIZE; 827 du->dk_dd.d_ntracks = 8; 828 du->dk_dd.d_nsectors = 17; 829 du->dk_dd.d_secpercyl = 17*8; 830 du->dk_dd.d_secperunit = 17*8*1024; 831 du->dk_state = WANTOPEN; 832 833 /* read label using "raw" partition */ 834 #if defined(TIHMODS) && defined(garbage) 835 /* wdsetctlr(dev, du); */ /* Maybe do this TIH */ 836 msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW), 837 wdstrategy, &du->dk_dd, &du->dk_cpd); 838 wdsetctlr(dev, du); 839 msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW), 840 wdstrategy, &du->dk_dd, &du->dk_cpd); 841 if (msg) { 842 #ifdef QUIETWORKS 843 if((du->dk_flags & DKFL_QUIET) == 0) { 844 log(LOG_WARNING, "wd%d: cannot find label (%s)\n", 845 lunit, msg); 846 error = EINVAL; /* XXX needs translation */ 847 } 848 #else 849 log(LOG_WARNING, "wd%d: cannot find label (%s)\n", lunit, msg); 850 if(part != WDRAW) { 851 error = EINVAL; /* XXX needs translation */ 852 } 853 #endif 854 goto done; 855 } else { 856 wdsetctlr(dev, du); 857 du->dk_flags |= DKFL_BSDLABEL; 858 du->dk_flags &= ~DKFL_WRITEPROT; 859 if (du->dk_dd.d_flags & D_BADSECT) 860 du->dk_flags |= DKFL_BADSECT; 861 } 862 #else 863 if (msg = readdisklabel(makewddev(major(dev), wdunit(dev), WDRAW), 864 wdstrategy, &du->dk_dd, &du->dk_cpd) ) { 865 if((du->dk_flags & DKFL_QUIET) == 0) { 866 log(LOG_WARNING, "wd%d: cannot find label (%s)\n", 867 lunit, msg); 868 error = EINVAL; /* XXX needs translation */ 869 } 870 goto done; 871 } else { 872 wdsetctlr(dev, du); 873 du->dk_flags |= DKFL_BSDLABEL; 874 du->dk_flags &= ~DKFL_WRITEPROT; 875 if (du->dk_dd.d_flags & D_BADSECT) 876 du->dk_flags |= DKFL_BADSECT; 877 } 878 #endif 879 880 done: 881 if (error) { 882 return(error); 883 } 884 } 885 886 if (du->dk_flags & DKFL_BADSECT) 887 bad144intern(du); 888 889 /* 890 * Warn if a partion is opened 891 * that overlaps another partition which is open 892 * unless one is the "raw" partition (whole disk). 893 */ 894 if ((du->dk_openpart & mask) == 0 /*&& part != RAWPART*/ && part != WDRAW) { 895 int start, end; 896 897 pp = &du->dk_dd.d_partitions[part]; 898 start = pp->p_offset; 899 end = pp->p_offset + pp->p_size; 900 for (pp = du->dk_dd.d_partitions; 901 pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions]; pp++) { 902 if (pp->p_offset + pp->p_size <= start || pp->p_offset >= end) 903 continue; 904 /*if (pp - du->dk_dd.d_partitions == RAWPART) 905 continue; */ 906 if (pp - du->dk_dd.d_partitions == WDRAW) 907 continue; 908 if (du->dk_openpart & (1 << (pp - du->dk_dd.d_partitions))) 909 log(LOG_WARNING, 910 "wd%d%c: overlaps open partition (%c)\n", 911 lunit, part + 'a', 912 pp - du->dk_dd.d_partitions + 'a'); 913 } 914 } 915 916 if (part >= du->dk_dd.d_npartitions && part != WDRAW) { 917 return (ENXIO); 918 } 919 920 /* insure only one open at a time */ 921 du->dk_openpart |= mask; 922 switch (fmt) { 923 case S_IFCHR: 924 du->dk_copenpart |= mask; 925 break; 926 case S_IFBLK: 927 du->dk_bopenpart |= mask; 928 break; 929 } 930 return (0); 931 } 932 933 /* 934 * Implement operations other than read/write. 935 * Called from wdstart or wdintr during opens and formats. 936 * Uses finite-state-machine to track progress of operation in progress. 937 * Returns 0 if operation still in progress, 1 if completed. 938 */ 939 static int 940 wdcontrol(register struct buf *bp) 941 { 942 register struct disk *du; 943 register unit, lunit; 944 unsigned char stat; 945 int s, ctrlr, timeout; 946 int wdc; 947 948 du = wddrives[wdunit(bp->b_dev)]; 949 ctrlr = du->dk_ctrlr; 950 unit = du->dk_unit; 951 lunit = du->dk_lunit; 952 wdc = du->dk_port; 953 954 switch (du->dk_state) { 955 tryagainrecal: 956 case WANTOPEN: /* set SDH, step rate, do restore */ 957 #ifdef WDDEBUG 958 printf("wd%d: recal ", lunit); 959 #endif 960 s = splbio(); /* not called from intr level ... */ 961 wdgetctlr(unit, du); 962 #ifdef TIPCAT 963 964 for (timeout=0; (inb(wdc+wd_status) & WDCS_READY) == 0; ) { 965 DELAY(WDCDELAY); 966 if (++timeout < WDCNDELAY) 967 continue; 968 wdreset(ctrlr, wdc, 1); 969 goto tryagainrecal; 970 } 971 #ifdef WDCNDELAY_DEBUG 972 if(timeout>WDCNDELAY_DEBUG) 973 printf("wdc%d: timeout took %dus\n", ctrlr, WDCDELAY * timeout); 974 #endif 975 #endif 976 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 977 wdtab[ctrlr].b_active = 1; 978 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); 979 #ifdef TIPCAT 980 for (timeout=0; (inb(wdc+wd_status) & WDCS_READY) == 0; ) { 981 DELAY(WDCDELAY); 982 if (++timeout < WDCNDELAY) 983 continue; 984 wdreset(ctrlr, wdc, 1); 985 goto tryagainrecal; 986 } 987 #ifdef WDCNDELAY_DEBUG 988 if(timeout>WDCNDELAY_DEBUG) 989 printf("wdc%d: timeout took %dus\n", ctrlr, WDCDELAY * timeout); 990 #endif 991 #endif 992 du->dk_state = RECAL; 993 splx(s); 994 return(0); 995 case RECAL: 996 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { 997 if ((du->dk_flags & DKFL_QUIET) == 0) { 998 printf("wd%d: recal", du->dk_lunit); 999 printf(": status %b error %b\n", 1000 stat, WDCS_BITS, inb(wdc+wd_error), 1001 WDERR_BITS); 1002 } 1003 if (++wdtab[ctrlr].b_errcnt < WDIORETRIES) 1004 goto tryagainrecal; 1005 bp->b_error = ENXIO; /* XXX needs translation */ 1006 goto badopen; 1007 } 1008 1009 /* some controllers require this ... */ 1010 wdsetctlr(bp->b_dev, du); 1011 1012 wdtab[ctrlr].b_errcnt = 0; 1013 du->dk_state = OPEN; 1014 /* 1015 * The rest of the initialization can be done 1016 * by normal means. 1017 */ 1018 return(1); 1019 default: 1020 panic("wdcontrol"); 1021 } 1022 /* NOTREACHED */ 1023 1024 badopen: 1025 if ((du->dk_flags & DKFL_QUIET) == 0) 1026 printf(": status %b error %b\n", 1027 stat, WDCS_BITS, inb(wdc + wd_error), WDERR_BITS); 1028 bp->b_flags |= B_ERROR; 1029 return(1); 1030 } 1031 1032 /* 1033 * send a command and wait uninterruptibly until controller is finished. 1034 * return -1 if controller busy for too long, otherwise 1035 * return status. intended for brief controller commands at critical points. 1036 * assumes interrupts are blocked. 1037 */ 1038 static int 1039 wdcommand(struct disk *du, int cmd) 1040 { 1041 int timeout, stat, wdc; 1042 1043 /*DELAY(2000);*/ 1044 wdc = du->dk_port; 1045 1046 /* controller ready for command? */ 1047 for (timeout=0; (stat=inb(wdc+wd_status)) & WDCS_BUSY; ) { 1048 DELAY(WDCDELAY); 1049 if(++timeout > WDCNDELAY) 1050 return -1; 1051 } 1052 #ifdef WDCNDELAY_DEBUG 1053 if(timeout>WDCNDELAY_DEBUG) 1054 printf("wdc%d: timeout took %dus\n", du->dk_ctrlr, WDCDELAY * timeout); 1055 #endif 1056 1057 /* send command, await results */ 1058 outb(wdc+wd_command, cmd); 1059 for (timeout=0; (stat=inb(wdc+wd_status)) & WDCS_BUSY; ) { 1060 DELAY(WDCDELAY); 1061 if(++timeout > WDCNDELAY) 1062 return -1; 1063 } 1064 #ifdef WDCNDELAY_DEBUG 1065 if(timeout>WDCNDELAY_DEBUG) 1066 printf("wdc%d: timeout took %dus\n", du->dk_ctrlr, WDCDELAY * timeout); 1067 #endif 1068 if (cmd != WDCC_READP) 1069 return (stat); 1070 1071 /* is controller ready to return data? */ 1072 for (timeout=0; ((stat=inb(wdc+wd_status)) & (WDCS_ERR|WDCS_DRQ)) == 0; ) { 1073 DELAY(WDCDELAY); 1074 if(++timeout > WDCNDELAY) 1075 return -1; 1076 } 1077 #ifdef WDCNDELAY_DEBUG 1078 if(timeout>WDCNDELAY_DEBUG) 1079 printf("wdc%d: timeout took %dus\n", du->dk_ctrlr, WDCDELAY * timeout); 1080 #endif 1081 return (stat); 1082 } 1083 1084 /* 1085 * issue IDC to drive to tell it just what geometry it is to be. 1086 */ 1087 static int 1088 wdsetctlr(dev_t dev, struct disk *du) 1089 { 1090 int stat, x, wdc; 1091 1092 /* 1093 printf("wd(%d,%d) C%dH%dS%d\n", du->dk_ctrlr, du->dk_unit, 1094 du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors); 1095 */ 1096 1097 wdc = du->dk_port; 1098 1099 /*DELAY(2000);*/ 1100 1101 x = splbio(); 1102 outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders); /* TIH: was ...ders+1 */ 1103 outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders)>>8); /* TIH: was ...ders+1 */ 1104 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4) + du->dk_dd.d_ntracks-1); 1105 outb(wdc+wd_seccnt, du->dk_dd.d_nsectors); 1106 stat = wdcommand(du, WDCC_IDC); 1107 1108 #ifndef TIHMODS 1109 if (stat < 0) 1110 return(stat); 1111 #endif 1112 if (stat & WDCS_ERR) 1113 printf("wdsetctlr: status %b error %b\n", 1114 stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS); 1115 splx(x); 1116 return(stat); 1117 } 1118 1119 /* 1120 * issue READP to drive to ask it what it is. 1121 */ 1122 static int 1123 wdgetctlr(int u, struct disk *du) 1124 { 1125 int stat, x, i, wdc; 1126 char tb[DEV_BSIZE]; 1127 struct wdparams *wp; 1128 int timeout; 1129 1130 x = splbio(); /* not called from intr level ... */ 1131 wdc = du->dk_port; 1132 #ifdef TIPCAT 1133 for (timeout=0; (inb(wdc+wd_status) & WDCS_READY) == 0; ) { 1134 DELAY(WDCDELAY); 1135 if(++timeout > WDCNDELAY) { 1136 splx(x); 1137 return -1; 1138 } 1139 } 1140 #ifdef WDCNDELAY_DEBUG 1141 if(timeout>WDCNDELAY_DEBUG) 1142 printf("wdc%d: timeout took %dus\n", du->dk_ctrlr, WDCDELAY * timeout); 1143 #endif 1144 #endif 1145 outb(wdc+wd_sdh, WDSD_IBM | (u << 4)); 1146 stat = wdcommand(du, WDCC_READP); 1147 #ifdef TIPCAT 1148 for (timeout=0; (inb(wdc+wd_status) & WDCS_READY) == 0; ) { 1149 DELAY(WDCDELAY); 1150 if(++timeout > WDCNDELAY) { 1151 splx(x); 1152 return -1; 1153 } 1154 } 1155 #ifdef WDCNDELAY_DEBUG 1156 if(timeout>WDCNDELAY_DEBUG) 1157 printf("wdc%d: timeout took %dus\n", du->dk_ctrlr, WDCDELAY * timeout); 1158 #endif 1159 #endif 1160 1161 #ifndef TIHMODS 1162 if (stat < 0) 1163 return(stat); 1164 #else 1165 if (stat < 0) { 1166 splx(x); 1167 return(stat); 1168 } 1169 #endif 1170 1171 if( (stat & WDCS_ERR) == 0) { 1172 /* obtain parameters */ 1173 wp = &du->dk_params; 1174 insw(wdc+wd_data, tb, sizeof(tb)/sizeof(short)); 1175 bcopy(tb, wp, sizeof(struct wdparams)); 1176 1177 /* shuffle string byte order */ 1178 for (i=0; i < sizeof(wp->wdp_model); i+=2) { 1179 u_short *p; 1180 p = (u_short *) (wp->wdp_model + i); 1181 *p = ntohs(*p); 1182 } 1183 1184 strncpy(du->dk_dd.d_typename, "ESDI/IDE", sizeof du->dk_dd.d_typename); 1185 du->dk_dd.d_type = DTYPE_ESDI; 1186 bcopy(wp->wdp_model+20, du->dk_dd.d_packname, 14-1); 1187 1188 /* update disklabel given drive information */ 1189 du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/; 1190 du->dk_dd.d_ntracks = wp->wdp_heads; 1191 du->dk_dd.d_nsectors = wp->wdp_sectors; 1192 du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; 1193 du->dk_dd.d_partitions[1].p_size = du->dk_dd.d_secpercyl * 1194 wp->wdp_sectors; 1195 du->dk_dd.d_partitions[1].p_offset = 0; 1196 } else { 1197 /* 1198 * If WDCC_READP fails then we might have an old drive 1199 * so we try a seek to 0; if that passes then the 1200 * drive is there but it's OLD AND KRUSTY. 1201 */ 1202 stat = wdcommand(du, WDCC_RESTORE | WD_STEP); 1203 if(stat & WDCS_ERR) { 1204 splx(x); 1205 return(inb(wdc+wd_error)); 1206 } 1207 1208 strncpy(du->dk_dd.d_typename, "ST506", sizeof du->dk_dd.d_typename); 1209 strncpy(du->dk_params.wdp_model, "Unknown Type", 1210 sizeof du->dk_params.wdp_model); 1211 du->dk_dd.d_type = DTYPE_ST506; 1212 } 1213 1214 #if 0 1215 printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", wp->wdp_config, 1216 wp->wdp_fixedcyl+wp->wdp_removcyl, wp->wdp_heads, wp->wdp_sectors, 1217 wp->wdp_cntype, wp->wdp_cnsbsz, wp->wdp_model); 1218 #endif 1219 1220 /* better ... */ 1221 du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; 1222 1223 /* XXX sometimes possibly needed */ 1224 (void) inb(wdc+wd_status); 1225 #ifdef TIHMODS 1226 splx(x); 1227 #endif 1228 return (0); 1229 } 1230 1231 1232 /* ARGSUSED */ 1233 int 1234 wdclose(dev_t dev, int flags, int fmt) 1235 { 1236 register struct disk *du; 1237 int part = wdpart(dev), mask = 1 << part; 1238 1239 du = wddrives[wdunit(dev)]; 1240 1241 /* insure only one open at a time */ 1242 du->dk_openpart &= ~mask; 1243 switch (fmt) { 1244 case S_IFCHR: 1245 du->dk_copenpart &= ~mask; 1246 break; 1247 case S_IFBLK: 1248 du->dk_bopenpart &= ~mask; 1249 break; 1250 } 1251 return(0); 1252 } 1253 1254 int 1255 wdioctl(dev_t dev, int cmd, caddr_t addr, int flag) 1256 { 1257 int lunit = wdunit(dev); 1258 register struct disk *du; 1259 int error = 0; 1260 struct uio auio; 1261 struct iovec aiov; 1262 1263 du = wddrives[lunit]; 1264 1265 switch (cmd) { 1266 case DIOCSBAD: 1267 if ((flag & FWRITE) == 0) 1268 error = EBADF; 1269 else { 1270 du->dk_cpd.bad = *(struct dkbad *)addr; 1271 bad144intern(du); 1272 } 1273 break; 1274 1275 case DIOCGDINFO: 1276 *(struct disklabel *)addr = du->dk_dd; 1277 break; 1278 1279 case DIOCGPART: 1280 ((struct partinfo *)addr)->disklab = &du->dk_dd; 1281 ((struct partinfo *)addr)->part = 1282 &du->dk_dd.d_partitions[wdpart(dev)]; 1283 break; 1284 1285 case DIOCSDINFO: 1286 if ((flag & FWRITE) == 0) 1287 error = EBADF; 1288 else { 1289 error = setdisklabel(&du->dk_dd, (struct disklabel *)addr, 1290 /*(du->dk_flags&DKFL_BSDLABEL) ? du->dk_openpart : */0, 1291 &du->dk_cpd); 1292 } 1293 if (error == 0) { 1294 du->dk_flags |= DKFL_BSDLABEL; 1295 wdsetctlr(dev, du); 1296 } 1297 break; 1298 1299 case DIOCWLABEL: 1300 du->dk_flags &= ~DKFL_WRITEPROT; 1301 if ((flag & FWRITE) == 0) 1302 error = EBADF; 1303 else 1304 du->dk_wlabel = *(int *)addr; 1305 break; 1306 1307 case DIOCWDINFO: 1308 du->dk_flags &= ~DKFL_WRITEPROT; 1309 if ((flag & FWRITE) == 0) 1310 error = EBADF; 1311 else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr, 1312 /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart :*/ 0, 1313 &du->dk_cpd)) == 0) { 1314 int wlab; 1315 1316 du->dk_flags |= DKFL_BSDLABEL; 1317 wdsetctlr(dev, du); 1318 1319 /* simulate opening partition 0 so write succeeds */ 1320 du->dk_openpart |= (1 << 0); /* XXX */ 1321 wlab = du->dk_wlabel; 1322 du->dk_wlabel = 1; 1323 error = writedisklabel(dev, wdstrategy, &du->dk_dd, &du->dk_cpd); 1324 du->dk_openpart = du->dk_copenpart | du->dk_bopenpart; 1325 du->dk_wlabel = wlab; 1326 } 1327 break; 1328 1329 #ifdef notyet 1330 case DIOCGDINFOP: 1331 *(struct disklabel **)addr = &(du->dk_dd); 1332 break; 1333 1334 case DIOCWFORMAT: 1335 if ((flag & FWRITE) == 0) 1336 error = EBADF; 1337 else { 1338 register struct format_op *fop; 1339 1340 fop = (struct format_op *)addr; 1341 aiov.iov_base = fop->df_buf; 1342 aiov.iov_len = fop->df_count; 1343 auio.uio_iov = &aiov; 1344 auio.uio_iovcnt = 1; 1345 auio.uio_resid = fop->df_count; 1346 auio.uio_segflg = 0; 1347 auio.uio_offset = fop->df_startblk * du->dk_dd.d_secsize; 1348 error = physio(wdformat, &rwdbuf[lunit], dev, B_WRITE, 1349 minphys, &auio); 1350 fop->df_count -= auio.uio_resid; 1351 fop->df_reg[0] = du->dk_status; 1352 fop->df_reg[1] = du->dk_error; 1353 } 1354 break; 1355 #endif 1356 1357 default: 1358 error = ENOTTY; 1359 break; 1360 } 1361 return (error); 1362 } 1363 1364 #ifdef B_FORMAT 1365 int 1366 wdformat(struct buf *bp) 1367 { 1368 bp->b_flags |= B_FORMAT; 1369 return (wdstrategy(bp)); 1370 } 1371 #endif 1372 1373 int 1374 wdsize(dev_t dev) 1375 { 1376 int lunit = wdunit(dev), part = wdpart(dev); 1377 struct disk *du; 1378 1379 if (lunit >= NWD) 1380 return(-1); 1381 1382 if ((du = wddrives[lunit]) == 0) 1383 return (-1); 1384 1385 if (du->dk_state < OPEN || (du->dk_flags & DKFL_BSDLABEL) == 0) { 1386 int val; 1387 val = wdopen(makewddev(major(dev), lunit, WDRAW), FREAD, S_IFBLK, 0); 1388 if (val != 0) 1389 return (-1); 1390 } 1391 1392 if ((du->dk_flags & (DKFL_WRITEPROT|DKFL_BSDLABEL)) != DKFL_BSDLABEL) 1393 return (-1); 1394 else 1395 return((int)du->dk_dd.d_partitions[part].p_size); 1396 } 1397 1398 extern char *vmmap; /* poor name! */ 1399 1400 /* dump core after a system crash */ 1401 int 1402 wddump(dev_t dev) 1403 { 1404 register struct disk *du; /* disk unit to do the IO */ 1405 long num; /* number of sectors to write */ 1406 int ctrlr, lunit, part, wdc; 1407 long blkoff, blknum; 1408 long cylin, head, sector, stat; 1409 long secpertrk, secpercyl, nblocks, i; 1410 char *addr; 1411 extern int Maxmem; 1412 static wddoingadump = 0; 1413 extern caddr_t CADDR1; 1414 1415 addr = (char *) 0; /* starting address */ 1416 1417 #if DO_NOT_KNOW_HOW 1418 /* toss any characters present prior to dump, ie. non-blocking getc */ 1419 while (cngetc()) 1420 ; 1421 #endif 1422 1423 /* size of memory to dump */ 1424 num = Maxmem; 1425 lunit = wdunit(dev); /* eventually support floppies? */ 1426 part = wdpart(dev); /* file system */ 1427 /* check for acceptable drive number */ 1428 if (lunit >= NWD) 1429 return(ENXIO); 1430 1431 du = wddrives[lunit]; 1432 if (du == 0) 1433 return(ENXIO); 1434 /* was it ever initialized ? */ 1435 if (du->dk_state < OPEN) 1436 return (ENXIO); 1437 if (du->dk_flags & DKFL_WRITEPROT) 1438 return(ENXIO); 1439 wdc = du->dk_port; 1440 ctrlr = du->dk_ctrlr; 1441 1442 /* Convert to disk sectors */ 1443 num = (u_long) num * NBPG / du->dk_dd.d_secsize; 1444 1445 /* check if controller active */ 1446 /*if (wdtab[ctrlr].b_active) 1447 return(EFAULT); */ 1448 if (wddoingadump) 1449 return(EFAULT); 1450 1451 secpertrk = du->dk_dd.d_nsectors; 1452 secpercyl = du->dk_dd.d_secpercyl; 1453 nblocks = du->dk_dd.d_partitions[part].p_size; 1454 blkoff = du->dk_dd.d_partitions[part].p_offset; 1455 1456 /*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/ 1457 /* check transfer bounds against partition size */ 1458 if ((dumplo < 0) || ((dumplo + num) > nblocks)) 1459 return(EINVAL); 1460 1461 /* mark controller active for if we panic during the dump */ 1462 /* wdtab[ctrlr].b_active = 1; */ 1463 wddoingadump = 1; 1464 i = 200000000; 1465 while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) 1466 ; 1467 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4)); 1468 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); 1469 while (inb(wdc+wd_status) & WDCS_BUSY) 1470 ; 1471 1472 /* some compaq controllers require this ... */ 1473 wdsetctlr(dev, du); 1474 1475 blknum = dumplo + blkoff; 1476 while (num > 0) { 1477 pmap_enter(kernel_pmap, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE); 1478 1479 /* compute disk address */ 1480 cylin = blknum / secpercyl; 1481 head = (blknum % secpercyl) / secpertrk; 1482 sector = blknum % secpertrk; 1483 1484 if (du->dk_flags & DKFL_BADSECT) { 1485 long newblk; 1486 int i; 1487 1488 for (i = 0; du->dk_badsect[i] != -1; i++) { 1489 if (blknum < du->dk_badsect[i]) { 1490 break; /* sorted list, passed our block by */ 1491 } else if (blknum == du->dk_badsect[i]) { 1492 newblk = du->dk_dd.d_secperunit - 1493 du->dk_dd.d_nsectors - i - 1; 1494 cylin = newblk / secpercyl; 1495 head = (newblk % secpercyl) / secpertrk; 1496 sector = newblk % secpertrk; 1497 /* found and repl; done scanning bad144 table */ 1498 break; 1499 } 1500 } 1501 } 1502 sector++; /* origin 1 */ 1503 1504 /* select drive. */ 1505 outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit<<4) | (head & 0xf)); 1506 while ((inb(wdc+wd_status) & WDCS_READY) == 0) 1507 ; 1508 1509 /* transfer some blocks */ 1510 outb(wdc+wd_sector, sector); 1511 outb(wdc+wd_seccnt,1); 1512 outb(wdc+wd_cyl_lo, cylin); 1513 outb(wdc+wd_cyl_hi, cylin >> 8); 1514 #ifdef notdef 1515 /* lets just talk about this first...*/ 1516 pg ("sdh 0%o sector %d cyl %d addr 0x%x", 1517 inb(wdc+wd_sdh), inb(wdc+wd_sector), 1518 inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr); 1519 #endif 1520 outb(wdc+wd_command, WDCC_WRITE); 1521 1522 /* Ready to send data? */ 1523 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) 1524 ; 1525 if (inb(wdc+wd_status) & WDCS_ERR) 1526 return(EIO); 1527 1528 outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256); 1529 1530 if (inb(wdc+wd_status) & WDCS_ERR) 1531 return(EIO); 1532 /* Check data request (should be done). */ 1533 if (inb(wdc+wd_status) & WDCS_DRQ) 1534 return(EIO); 1535 1536 /* wait for completion */ 1537 for (i=200000000; inb(wdc+wd_status) & WDCS_BUSY ; i--) { 1538 if (i < 0) 1539 return (EIO); 1540 } 1541 1542 /* error check the xfer */ 1543 if (inb(wdc+wd_status) & WDCS_ERR) 1544 return(EIO); 1545 1546 if ((unsigned)addr % (1024*1024) == 0) 1547 printf("%d ", num/2048); 1548 1549 /* update block count */ 1550 num--; 1551 blknum++; 1552 (int) addr += 512; 1553 1554 #if DO_NOT_KNOW_HOW 1555 /* operator aborting dump? non-blocking getc() */ 1556 if (cngetc()) 1557 return(EINTR); 1558 #endif 1559 } 1560 return(0); 1561 } 1562 #endif 1563 1564 /* 1565 * Internalize the bad sector table. 1566 */ 1567 void 1568 bad144intern(struct disk *du) 1569 { 1570 int i; 1571 if (du->dk_flags & DKFL_BADSECT) { 1572 for (i = 0; i < 127; i++) { 1573 du->dk_badsect[i] = -1; 1574 } 1575 1576 for (i = 0; i < 126; i++) { 1577 if (du->dk_cpd.bad.bt_bad[i].bt_cyl == 0xffff) { 1578 break; 1579 } else { 1580 du->dk_badsect[i] = 1581 du->dk_cpd.bad.bt_bad[i].bt_cyl * 1582 du->dk_dd.d_secpercyl + 1583 (du->dk_cpd.bad.bt_bad[i].bt_trksec >> 8) * 1584 du->dk_dd.d_nsectors + 1585 (du->dk_cpd.bad.bt_bad[i].bt_trksec & 0x00ff); 1586 } 1587 } 1588 } 1589 } 1590 1591 /* this routine was adopted from the kernel sources */ 1592 /* more efficient because b_cylin is not really as useful at this level */ 1593 /* so I eliminate the processing, I believe that sorting the sectors */ 1594 /* is adequate */ 1595 void 1596 wddisksort(struct buf *dp, struct buf *bp) 1597 { 1598 register struct buf *ap; 1599 1600 /* 1601 * If nothing on the activity queue, then 1602 * we become the only thing. 1603 */ 1604 ap = dp->b_actf; 1605 if(ap == NULL) { 1606 dp->b_actf = bp; 1607 dp->b_actl = bp; 1608 bp->av_forw = NULL; 1609 return; 1610 } 1611 while( ap->b_flags & B_XXX) { 1612 if( ap->av_forw == 0 || (ap->av_forw->b_flags & B_XXX) == 0) 1613 break; 1614 ap = ap->av_forw; 1615 } 1616 /* 1617 * If we lie after the first (currently active) 1618 * request, then we must locate the second request list 1619 * and add ourselves to it. 1620 */ 1621 if (bp->b_blkno < ap->b_blkno) { 1622 while (ap->av_forw) { 1623 /* 1624 * Check for an ``inversion'' in the 1625 * normally ascending cylinder numbers, 1626 * indicating the start of the second request list. 1627 */ 1628 if (ap->av_forw->b_blkno < ap->b_blkno) { 1629 /* 1630 * Search the second request list 1631 * for the first request at a larger 1632 * cylinder number. We go before that; 1633 * if there is no such request, we go at end. 1634 */ 1635 do { 1636 if (bp->b_blkno < ap->av_forw->b_blkno) 1637 goto insert; 1638 ap = ap->av_forw; 1639 } while (ap->av_forw); 1640 goto insert; /* after last */ 1641 } 1642 ap = ap->av_forw; 1643 } 1644 /* 1645 * No inversions... we will go after the last, and 1646 * be the first request in the second request list. 1647 */ 1648 goto insert; 1649 } 1650 /* 1651 * Request is at/after the current request... 1652 * sort in the first request list. 1653 */ 1654 while (ap->av_forw) { 1655 /* 1656 * We want to go after the current request 1657 * if there is an inversion after it (i.e. it is 1658 * the end of the first request list), or if 1659 * the next request is a larger cylinder than our request. 1660 */ 1661 if (ap->av_forw->b_blkno < ap->b_blkno || 1662 bp->b_blkno < ap->av_forw->b_blkno ) 1663 goto insert; 1664 ap = ap->av_forw; 1665 } 1666 /* 1667 * Neither a second list nor a larger 1668 * request... we go at the end of the first list, 1669 * which is the same as the end of the whole schebang. 1670 */ 1671 insert: 1672 bp->av_forw = ap->av_forw; 1673 ap->av_forw = bp; 1674 if (ap == dp->b_actl) 1675 dp->b_actl = bp; 1676 } 1677 1678 wdreset(ctrlr, wdc, err) 1679 int ctrlr; 1680 { 1681 int stat, timeout; 1682 1683 if(err) 1684 printf("wdc%d: busy too long, resetting\n", ctrlr); 1685 1686 /* reset the device */ 1687 outb(wdc+wd_ctlr, (WDCTL_RST|WDCTL_IDS)); 1688 DELAY(1000); 1689 outb(wdc+wd_ctlr, WDCTL_4BIT); 1690 1691 for (timeout=0; (stat=inb(wdc+wd_status)) & WDCS_BUSY; ) { 1692 DELAY(WDCDELAY); 1693 if(++timeout > WDCNDELAY) { 1694 printf("wdc%d: failed to reset controller\n", ctrlr); 1695 break; 1696 } 1697 } 1698 #ifdef WDCNDELAY_DEBUG 1699 if(timeout>WDCNDELAY_DEBUG) 1700 printf("wdc%d: timeout took %dus\n", ctrlr, WDCDELAY * timeout); 1701 #endif 1702 } 1703