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