1*49568Swilliam /* TODO:peel out buffer at low ipl, 2*49568Swilliam speed improvement, rewrite to clean code from garbage artifacts */ 3*49568Swilliam 441056Swilliam /*- 541056Swilliam * Copyright (c) 1990 The Regents of the University of California. 641056Swilliam * All rights reserved. 741056Swilliam * 841056Swilliam * This code is derived from software contributed to Berkeley by 941056Swilliam * William Jolitz. 1041056Swilliam * 11*49568Swilliam * %sccs.include.redist% 1241056Swilliam * 13*49568Swilliam * @(#)wd.c 7.1 (Berkeley) 05/09/91 1441056Swilliam */ 1543592Sdonahn 1641056Swilliam #include "wd.h" 1741056Swilliam #if NWD > 0 1841056Swilliam 1941056Swilliam #include "param.h" 2041056Swilliam #include "dkbad.h" 2141056Swilliam #include "systm.h" 2241056Swilliam #include "conf.h" 2341056Swilliam #include "file.h" 24*49568Swilliam #include "stat.h" 2541056Swilliam #include "ioctl.h" 26*49568Swilliam #include "disklabel.h" 2741056Swilliam #include "buf.h" 2841056Swilliam #include "uio.h" 29*49568Swilliam #include "i386/isa/isa_device.h" 30*49568Swilliam #include "i386/isa/icu.h" 31*49568Swilliam #include "i386/isa/wdreg.h" 3241056Swilliam #include "syslog.h" 33*49568Swilliam #include "vm/vm.h" 3441056Swilliam 3541056Swilliam #define RETRIES 5 /* number of retries before giving up */ 36*49568Swilliam #define MAXTRANSFER 32 /* max size of transfer in page clusters */ 3741056Swilliam 38*49568Swilliam #define wdctlr(dev) ((minor(dev) & 0x80) >> 7) 39*49568Swilliam #define wdunit(dev) ((minor(dev) & 0x60) >> 5) 40*49568Swilliam #define wdpart(dev) ((minor(dev) & 0x1f)) 4141056Swilliam 4241056Swilliam #define b_cylin b_resid /* cylinder number for doing IO to */ 4341056Swilliam /* shares an entry in the buf struct */ 4441056Swilliam 4541056Swilliam /* 4641056Swilliam * Drive states. Used for open and format operations. 4741056Swilliam * States < OPEN (> 0) are transient, during an open operation. 4841056Swilliam * OPENRAW is used for unlabeled disks, and for floppies, to inhibit 4941056Swilliam * bad-sector forwarding. 5041056Swilliam */ 5141056Swilliam #define RAWDISK 8 /* raw disk operation, no translation*/ 5241056Swilliam #define ISRAWSTATE(s) (RAWDISK&(s)) /* are we in a raw state? */ 5341056Swilliam #define DISKSTATE(s) (~RAWDISK&(s)) /* are we in a given state regardless 5441056Swilliam of raw or cooked mode? */ 5541056Swilliam 5641056Swilliam #define CLOSED 0 /* disk is closed. */ 5741056Swilliam /* "cooked" disk states */ 5841056Swilliam #define WANTOPEN 1 /* open requested, not started */ 5941056Swilliam #define RECAL 2 /* doing restore */ 6041056Swilliam #define RDLABEL 3 /* reading pack label */ 6141056Swilliam #define RDBADTBL 4 /* reading bad-sector table */ 6241056Swilliam #define OPEN 5 /* done with open */ 6341056Swilliam 6441056Swilliam #define WANTOPENRAW (WANTOPEN|RAWDISK) /* raw WANTOPEN */ 6541056Swilliam #define RECALRAW (RECAL|RAWDISK) /* raw open, doing restore */ 6641056Swilliam #define OPENRAW (OPEN|RAWDISK) /* open, but unlabeled disk or floppy */ 6741056Swilliam 6841056Swilliam 6941056Swilliam /* 7041056Swilliam * The structure of a disk drive. 7141056Swilliam */ 7241056Swilliam struct disk { 73*49568Swilliam struct disklabel dk_dd; /* device configuration data */ 74*49568Swilliam long dk_bc; /* byte count left */ 75*49568Swilliam short dk_skip; /* blocks already transferred */ 76*49568Swilliam char dk_unit; /* physical unit number */ 77*49568Swilliam char dk_state; /* control state */ 78*49568Swilliam u_char dk_status; /* copy of status reg. */ 79*49568Swilliam u_char dk_error; /* copy of error reg. */ 80*49568Swilliam short dk_open; /* open/closed refcnt */ 81*49568Swilliam u_long dk_copenpart; /* character units open on this drive */ 82*49568Swilliam u_long dk_bopenpart; /* block units open on this drive */ 83*49568Swilliam u_long dk_openpart; /* all units open on this drive */ 84*49568Swilliam short dk_wlabel; /* label writable? */ 8541056Swilliam }; 8641056Swilliam 8741056Swilliam /* 8841056Swilliam * This label is used as a default when initializing a new or raw disk. 8941056Swilliam * It really only lets us access the first track until we know more. 9041056Swilliam */ 9141056Swilliam struct disklabel dflt_sizes = { 92*49568Swilliam DISKMAGIC, DTYPE_ST506, 0, "default", "", 9341056Swilliam 512, /* sector size */ 94*49568Swilliam 17, /* # of sectors per track */ 95*49568Swilliam 8, /* # of tracks per cylinder */ 96*49568Swilliam 766, /* # of cylinders per unit */ 97*49568Swilliam 17*8, /* # of sectors per cylinder */ 98*49568Swilliam 766*8*17, /* # of sectors per unit */ 99*49568Swilliam 0, /* # of spare sectors per track */ 100*49568Swilliam 0, /* # of spare sectors per cylinder */ 101*49568Swilliam 0, /* # of alt. cylinders per unit */ 102*49568Swilliam 3600, /* rotational speed */ 103*49568Swilliam 1, /* hardware sector interleave */ 104*49568Swilliam 0, /* sector 0 skew, per track */ 105*49568Swilliam 0, /* sector 0 skew, per cylinder */ 106*49568Swilliam 0, /* head switch time, usec */ 107*49568Swilliam 0, /* track-to-track seek, usec */ 108*49568Swilliam 0, /* generic flags */ 109*49568Swilliam 0,0,0,0,0, 110*49568Swilliam 0,0,0,0,0, 111*49568Swilliam DISKMAGIC, 112*49568Swilliam 0, 113*49568Swilliam 8, 114*49568Swilliam 8192, 115*49568Swilliam 8192, 116*49568Swilliam 117*49568Swilliam {{21600, 0, 0,0,0,0}, /* A=root filesystem */ 118*49568Swilliam {21600, 40, 0,0,0,0}, 119*49568Swilliam {660890, 0, 0,0,0,0}, /* C=whole disk */ 120*49568Swilliam {216000, 80, 0,0,0,0}, 121*49568Swilliam {0, 0, 0,0,0,0}, 122*49568Swilliam {0, 0, 0,0,0,0}, 123*49568Swilliam {0, 0, 0,0,0,0}, 124*49568Swilliam {399600, 480, 0,0,0,0}} 12541056Swilliam }; 12645550Sbill 12741056Swilliam static struct dkbad dkbad[NWD]; 12841056Swilliam struct disk wddrives[NWD] = {0}; /* table of units */ 12941056Swilliam struct buf wdtab = {0}; 13041056Swilliam struct buf wdutab[NWD] = {0}; /* head of queue per drive */ 13141056Swilliam struct buf rwdbuf[NWD] = {0}; /* buffers for raw IO */ 13241056Swilliam long wdxfer[NWD] = {0}; /* count of transfers */ 13341056Swilliam int writeprotected[NWD] = { 0 }; 13441056Swilliam int wdprobe(), wdattach(), wdintr(); 13545552Sbill struct isa_driver wddriver = { 13641056Swilliam wdprobe, wdattach, "wd", 13741056Swilliam }; 13841056Swilliam 13945552Sbill static wdc; 14041056Swilliam /* 14141056Swilliam * Probe routine 14241056Swilliam */ 14341056Swilliam wdprobe(dvp) 14445552Sbill struct isa_device *dvp; 14541056Swilliam { 14645552Sbill wdc = dvp->id_iobase; 14741056Swilliam 14841056Swilliam #ifdef lint 14941056Swilliam wdintr(0); 15041056Swilliam #endif 151*49568Swilliam /* XXX sorry, needs to be better */ 15241056Swilliam outb(wdc+wd_error, 0x5a) ; /* error register not writable */ 15341056Swilliam outb(wdc+wd_cyl_lo, 0xa5) ; /* but all of cyllo are implemented */ 154*49568Swilliam if(inb(wdc+wd_error) != 0x5a && inb(wdc+wd_cyl_lo) == 0xa5) 15541056Swilliam return(1) ; 15641056Swilliam return (0); 15741056Swilliam } 15841056Swilliam 15941056Swilliam /* 16041056Swilliam * attach each drive if possible. 16141056Swilliam */ 16241056Swilliam wdattach(dvp) 16345552Sbill struct isa_device *dvp; 16441056Swilliam { 16545552Sbill int unit = dvp->id_unit; 16641056Swilliam 16745552Sbill outb(wdc+wd_ctlr,12); 16845552Sbill DELAY(1000); 16945552Sbill outb(wdc+wd_ctlr,8); 17041056Swilliam } 17141056Swilliam 17241056Swilliam /* Read/write routine for a buffer. Finds the proper unit, range checks 17341056Swilliam * arguments, and schedules the transfer. Does not wait for the transfer 17441056Swilliam * to complete. Multi-page transfers are supported. All I/O requests must 17541056Swilliam * be a multiple of a sector in length. 17641056Swilliam */ 17741056Swilliam wdstrategy(bp) 17841056Swilliam register struct buf *bp; /* IO operation to perform */ 17941056Swilliam { 18041056Swilliam register struct buf *dp; 18141056Swilliam register struct disk *du; /* Disk unit to do the IO. */ 182*49568Swilliam register struct partition *p; 183*49568Swilliam long maxsz, sz; 184*49568Swilliam int unit = wdunit(bp->b_dev); 18541056Swilliam int s; 18641056Swilliam 18741056Swilliam if ((unit >= NWD) || (bp->b_blkno < 0)) { 18843592Sdonahn printf("wdstrat: unit = %d, blkno = %d, bcount = %d\n", 18941056Swilliam unit, bp->b_blkno, bp->b_bcount); 19043592Sdonahn pg("wd:error in wdstrategy"); 19141056Swilliam bp->b_flags |= B_ERROR; 19241056Swilliam goto bad; 19341056Swilliam } 19441056Swilliam if (writeprotected[unit] && (bp->b_flags & B_READ) == 0) { 19541056Swilliam printf("wd%d: write protected\n", unit); 19641056Swilliam goto bad; 19741056Swilliam } 19841056Swilliam du = &wddrives[unit]; 19941056Swilliam if (DISKSTATE(du->dk_state) != OPEN) 20041056Swilliam goto q; 201*49568Swilliam #ifdef old 20241056Swilliam /* 20341056Swilliam * Convert DEV_BSIZE "blocks" to sectors. 20441056Swilliam * Note: doing the conversions this way limits the partition size 20541056Swilliam * to about 8 million sectors (1-8 Gb). 20641056Swilliam */ 207*49568Swilliam blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize; 208*49568Swilliam if (((u_long) bp->b_blkno * DEV_BSIZE % du->dk_dd.d_secsize != 0) || 20943592Sdonahn bp->b_bcount >= MAXTRANSFER * CLBYTES) { 21041056Swilliam bp->b_flags |= B_ERROR; 21141056Swilliam goto bad; 21241056Swilliam } 213*49568Swilliam nblocks = du->dk_dd.d_partitions[part].p_size; 214*49568Swilliam cyloff = du->dk_dd.d_partitions[part].p_offset; 215*49568Swilliam if (blknum + (bp->b_bcount / du->dk_dd.d_secsize) > nblocks) { 21641056Swilliam if (blknum == nblocks) 21741056Swilliam bp->b_resid = bp->b_bcount; 21841056Swilliam else 21941056Swilliam bp->b_flags |= B_ERROR; 22041056Swilliam goto bad; 22141056Swilliam } 222*49568Swilliam bp->b_cylin = blknum / du->dk_dd.d_secpercyl + cyloff; 223*49568Swilliam #else 224*49568Swilliam /* 225*49568Swilliam * Determine the size of the transfer, and make sure it is 226*49568Swilliam * within the boundaries of the partition. 227*49568Swilliam */ 228*49568Swilliam p = &du->dk_dd.d_partitions[wdpart(bp->b_dev)]; 229*49568Swilliam maxsz = p->p_size; 230*49568Swilliam sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; 231*49568Swilliam if (bp->b_blkno + p->p_offset <= LABELSECTOR && 232*49568Swilliam #if LABELSECTOR != 0 233*49568Swilliam bp->b_blkno + p->p_offset + sz > LABELSECTOR && 234*49568Swilliam #endif 235*49568Swilliam (bp->b_flags & B_READ) == 0 && du->dk_wlabel == 0) { 236*49568Swilliam bp->b_error = EROFS; 237*49568Swilliam goto bad; 238*49568Swilliam } 239*49568Swilliam if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { 240*49568Swilliam /* if exactly at end of disk, return an EOF */ 241*49568Swilliam if (bp->b_blkno == maxsz) { 242*49568Swilliam bp->b_resid = bp->b_bcount; 243*49568Swilliam biodone(bp); 244*49568Swilliam return; 245*49568Swilliam } 246*49568Swilliam /* or truncate if part of it fits */ 247*49568Swilliam sz = maxsz - bp->b_blkno; 248*49568Swilliam if (sz <= 0) 249*49568Swilliam goto bad; 250*49568Swilliam bp->b_bcount = sz << DEV_BSHIFT; 251*49568Swilliam } 252*49568Swilliam bp->b_cylin = (bp->b_blkno + p->p_offset) / du->dk_dd.d_secpercyl; 253*49568Swilliam #endif 25441056Swilliam q: 25541056Swilliam dp = &wdutab[unit]; 25645550Sbill s = splhigh(); 25741056Swilliam disksort(dp, bp); 25841056Swilliam if (dp->b_active == 0) 25941056Swilliam wdustart(du); /* start drive if idle */ 26041056Swilliam if (wdtab.b_active == 0) 26141056Swilliam wdstart(s); /* start IO if controller idle */ 26241056Swilliam splx(s); 26341056Swilliam return; 26441056Swilliam 26541056Swilliam bad: 26641056Swilliam bp->b_error = EINVAL; 26741056Swilliam biodone(bp); 26841056Swilliam } 26941056Swilliam 27041056Swilliam /* Routine to queue a read or write command to the controller. The request is 27141056Swilliam * linked into the active list for the controller. If the controller is idle, 27241056Swilliam * the transfer is started. 27341056Swilliam */ 27441056Swilliam wdustart(du) 27541056Swilliam register struct disk *du; 27641056Swilliam { 27741056Swilliam register struct buf *bp, *dp; 27841056Swilliam 27941056Swilliam dp = &wdutab[du->dk_unit]; 28041056Swilliam if (dp->b_active) 28141056Swilliam return; 28241056Swilliam bp = dp->b_actf; 28341056Swilliam if (bp == NULL) 28441056Swilliam return; 28541056Swilliam dp->b_forw = NULL; 28641056Swilliam if (wdtab.b_actf == NULL) /* link unit into active list */ 28741056Swilliam wdtab.b_actf = dp; 28841056Swilliam else 28941056Swilliam wdtab.b_actl->b_forw = dp; 29041056Swilliam wdtab.b_actl = dp; 29141056Swilliam dp->b_active = 1; /* mark the drive as busy */ 29241056Swilliam } 29341056Swilliam 29441056Swilliam /* 29541056Swilliam * Controller startup routine. This does the calculation, and starts 29641056Swilliam * a single-sector read or write operation. Called to start a transfer, 29741056Swilliam * or from the interrupt routine to continue a multi-sector transfer. 29841056Swilliam * RESTRICTIONS: 29941056Swilliam * 1. The transfer length must be an exact multiple of the sector size. 30041056Swilliam */ 30141056Swilliam 30245550Sbill static wd_sebyse; 30345550Sbill 30441056Swilliam wdstart() 30541056Swilliam { 30641056Swilliam register struct disk *du; /* disk unit for IO */ 30741056Swilliam register struct buf *bp; 30841056Swilliam struct buf *dp; 30941056Swilliam register struct bt_bad *bt_ptr; 31041056Swilliam long blknum, pagcnt, cylin, head, sector; 31141056Swilliam long secpertrk, secpercyl, addr, i; 312*49568Swilliam int unit, s; 31341056Swilliam 31441056Swilliam loop: 31541056Swilliam dp = wdtab.b_actf; 31641056Swilliam if (dp == NULL) 31741056Swilliam return; 31841056Swilliam bp = dp->b_actf; 31941056Swilliam if (bp == NULL) { 32041056Swilliam wdtab.b_actf = dp->b_forw; 32141056Swilliam goto loop; 32241056Swilliam } 323*49568Swilliam unit = wdunit(bp->b_dev); 32441056Swilliam du = &wddrives[unit]; 32541056Swilliam if (DISKSTATE(du->dk_state) <= RDLABEL) { 32641056Swilliam if (wdcontrol(bp)) { 32741056Swilliam dp->b_actf = bp->av_forw; 32841056Swilliam goto loop; /* done */ 32941056Swilliam } 33041056Swilliam return; 33141056Swilliam } 332*49568Swilliam secpertrk = du->dk_dd.d_nsectors; 333*49568Swilliam secpercyl = du->dk_dd.d_secpercyl; 33441056Swilliam /* 33541056Swilliam * Convert DEV_BSIZE "blocks" to sectors. 33641056Swilliam */ 337*49568Swilliam blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize 33841056Swilliam + du->dk_skip; 33941056Swilliam #ifdef WDDEBUG 34041056Swilliam if (du->dk_skip == 0) { 34141056Swilliam dprintf(DDSK,"\nwdstart %d: %s %d@%d; map ", unit, 34241056Swilliam (bp->b_flags & B_READ) ? "read" : "write", 34341056Swilliam bp->b_bcount, blknum); 34441056Swilliam } else { 34545550Sbill dprintf(DDSK," %d)%x", du->dk_skip, inb(wdc+wd_altsts)); 34641056Swilliam } 34741056Swilliam #endif 34841056Swilliam 34941056Swilliam addr = (int) bp->b_un.b_addr; 35041056Swilliam if(du->dk_skip==0) du->dk_bc = bp->b_bcount; 35141056Swilliam cylin = blknum / secpercyl; 35241056Swilliam head = (blknum % secpercyl) / secpertrk; 35345550Sbill sector = blknum % secpertrk; 35441056Swilliam if (DISKSTATE(du->dk_state) == OPEN) 355*49568Swilliam cylin += du->dk_dd.d_partitions[wdpart(bp->b_dev)].p_offset 356*49568Swilliam / secpercyl; 35741056Swilliam 35841056Swilliam /* 35941056Swilliam * See if the current block is in the bad block list. 36041056Swilliam * (If we have one, and not formatting.) 36141056Swilliam */ 36245550Sbill if (DISKSTATE(du->dk_state) == OPEN && wd_sebyse) 36341056Swilliam for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) { 36441056Swilliam if (bt_ptr->bt_cyl > cylin) 36541056Swilliam /* Sorted list, and we passed our cylinder. quit. */ 36641056Swilliam break; 36741056Swilliam if (bt_ptr->bt_cyl == cylin && 36841056Swilliam bt_ptr->bt_trksec == (head << 8) + sector) { 36941056Swilliam /* 37041056Swilliam * Found bad block. Calculate new block addr. 37141056Swilliam * This starts at the end of the disk (skip the 37241056Swilliam * last track which is used for the bad block list), 37341056Swilliam * and works backwards to the front of the disk. 37441056Swilliam */ 37541056Swilliam #ifdef WDDEBUG 37641056Swilliam dprintf(DDSK,"--- badblock code -> Old = %d; ", 37741056Swilliam blknum); 37841056Swilliam #endif 379*49568Swilliam blknum = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors 38041056Swilliam - (bt_ptr - dkbad[unit].bt_bad) - 1; 38141056Swilliam cylin = blknum / secpercyl; 38241056Swilliam head = (blknum % secpercyl) / secpertrk; 38341056Swilliam sector = blknum % secpertrk; 38441056Swilliam #ifdef WDDEBUG 38541056Swilliam dprintf(DDSK, "new = %d\n", blknum); 38641056Swilliam #endif 38741056Swilliam break; 38841056Swilliam } 38941056Swilliam } 39045550Sbill sector += 1; /* sectors begin with 1, not 0 */ 39141056Swilliam 39241056Swilliam wdtab.b_active = 1; /* mark controller active */ 39341056Swilliam 39445550Sbill if(du->dk_skip==0 || wd_sebyse) { 39545550Sbill if(wdtab.b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += 512; 39645550Sbill while ((inb(wdc+wd_status) & WDCS_BUSY) != 0) ; 39745550Sbill /*while ((inb(wdc+wd_status) & WDCS_DRQ)) inb(wdc+wd_data);*/ 39841056Swilliam outb(wdc+wd_precomp, 0xff); 39941056Swilliam /*wr(wdc+wd_precomp, du->dk_dd.dk_precompcyl / 4);*/ 40041056Swilliam /*if (bp->b_flags & B_FORMAT) { 40141056Swilliam wr(wdc+wd_sector, du->dk_dd.dk_gap3); 40241056Swilliam wr(wdc+wd_seccnt, du->dk_dd.dk_nsectors); 40341056Swilliam } else {*/ 40445550Sbill if(wd_sebyse) 40545550Sbill outb(wdc+wd_seccnt, 1); 40645550Sbill else 40745550Sbill outb(wdc+wd_seccnt, ((du->dk_bc +511) / 512)); 40841056Swilliam outb(wdc+wd_sector, sector); 40941056Swilliam 41041056Swilliam outb(wdc+wd_cyl_lo, cylin); 41141056Swilliam outb(wdc+wd_cyl_hi, cylin >> 8); 41241056Swilliam 41341056Swilliam /* Set up the SDH register (select drive). */ 41441056Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); 41545551Sbill while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; 41641056Swilliam 41741056Swilliam /*if (bp->b_flags & B_FORMAT) 41841056Swilliam wr(wdc+wd_command, WDCC_FORMAT); 41941056Swilliam else*/ 42041056Swilliam outb(wdc+wd_command, 42141056Swilliam (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE); 42241056Swilliam #ifdef WDDEBUG 42345550Sbill dprintf(DDSK,"sector %d cylin %d head %d addr %x sts %x\n", 42445550Sbill sector, cylin, head, addr, inb(wdc+wd_altsts)); 42541056Swilliam #endif 42645550Sbill } 42741056Swilliam 42841056Swilliam /* If this is a read operation, just go away until it's done. */ 42941056Swilliam if (bp->b_flags & B_READ) return; 43041056Swilliam 43141056Swilliam /* Ready to send data? */ 432*49568Swilliam while ((inb(wdc+wd_status) & WDCS_DRQ) == 0); 43341056Swilliam 43441056Swilliam /* ASSUMES CONTIGUOUS MEMORY */ 43545550Sbill outsw (wdc+wd_data, addr+du->dk_skip*512, 256); 43641056Swilliam du->dk_bc -= 512; 43741056Swilliam } 43841056Swilliam 43941056Swilliam /* 44041056Swilliam * these are globally defined so they can be found 44141056Swilliam * by the debugger easily in the case of a system crash 44241056Swilliam */ 44341056Swilliam daddr_t wd_errsector; 44441056Swilliam daddr_t wd_errbn; 44541056Swilliam unsigned char wd_errstat; 44641056Swilliam 44741056Swilliam /* Interrupt routine for the controller. Acknowledge the interrupt, check for 44841056Swilliam * errors on the current operation, mark it done if necessary, and start 44941056Swilliam * the next request. Also check for a partially done transfer, and 45041056Swilliam * continue with the next chunk if so. 45141056Swilliam */ 452*49568Swilliam wdintr(unit) 45341056Swilliam { 45441056Swilliam register struct disk *du; 45541056Swilliam register struct buf *bp, *dp; 45641056Swilliam int status; 45741056Swilliam char partch ; 458*49568Swilliam static wd_haderror; 45941056Swilliam 46041056Swilliam /* Shouldn't need this, but it may be a slow controller. */ 461*49568Swilliam while ((status = inb(wdc+wd_status)) & WDCS_BUSY) ; 46241056Swilliam if (!wdtab.b_active) { 46341056Swilliam printf("wd: extra interrupt\n"); 46441056Swilliam return; 46541056Swilliam } 46641056Swilliam 46745549Sbill #ifdef WDDEBUG 46841056Swilliam dprintf(DDSK,"I "); 46941056Swilliam #endif 47041056Swilliam dp = wdtab.b_actf; 47141056Swilliam bp = dp->b_actf; 472*49568Swilliam du = &wddrives[wdunit(bp->b_dev)]; 473*49568Swilliam partch = wdpart(bp->b_dev) + 'a'; 47441056Swilliam if (DISKSTATE(du->dk_state) <= RDLABEL) { 47541056Swilliam if (wdcontrol(bp)) 47641056Swilliam goto done; 47741056Swilliam return; 47841056Swilliam } 47941056Swilliam if (status & (WDCS_ERR | WDCS_ECCCOR)) { 48045550Sbill wd_errstat = inb(wdc+wd_error); /* save error status */ 48141056Swilliam #ifdef WDDEBUG 48245550Sbill printf("status %x error %x\n", status, wd_errstat); 48341056Swilliam #endif 48445550Sbill if(wd_sebyse == 0) { 48545550Sbill wd_haderror = 1; 48645550Sbill goto outt; 48745550Sbill } 48841056Swilliam /*if (bp->b_flags & B_FORMAT) { 48941056Swilliam du->dk_status = status; 49041056Swilliam du->dk_error = wdp->wd_error; 49141056Swilliam bp->b_flags |= B_ERROR; 49241056Swilliam goto done; 49341056Swilliam }*/ 49441056Swilliam 495*49568Swilliam wd_errsector = (bp->b_cylin * du->dk_dd.d_secpercyl) + 49641056Swilliam (((unsigned long) bp->b_blkno * DEV_BSIZE / 497*49568Swilliam du->dk_dd.d_secsize) % du->dk_dd.d_secpercyl) + 49841056Swilliam du->dk_skip; 49941056Swilliam wd_errbn = bp->b_blkno 500*49568Swilliam + du->dk_skip * du->dk_dd.d_secsize / DEV_BSIZE ; 50141056Swilliam if (status & WDCS_ERR) { 50245550Sbill if (++wdtab.b_errcnt < RETRIES) { 50341056Swilliam wdtab.b_active = 0; 50445550Sbill } else { 50541056Swilliam printf("wd%d%c: ", du->dk_unit, partch); 50641056Swilliam printf( 50741056Swilliam "hard %s error, sn %d bn %d status %b error %b\n", 50841056Swilliam (bp->b_flags & B_READ)? "read":"write", 50941056Swilliam wd_errsector, wd_errbn, status, WDCS_BITS, 51041056Swilliam wd_errstat, WDERR_BITS); 51141056Swilliam bp->b_flags |= B_ERROR; /* flag the error */ 51241056Swilliam } 51341056Swilliam } else 51441056Swilliam log(LOG_WARNING,"wd%d%c: soft ecc sn %d bn %d\n", 51541056Swilliam du->dk_unit, partch, wd_errsector, 51641056Swilliam wd_errbn); 51741056Swilliam } 51845550Sbill outt: 51941056Swilliam 52041056Swilliam /* 52141056Swilliam * If this was a successful read operation, fetch the data. 52241056Swilliam */ 52341056Swilliam if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) { 52441056Swilliam int chk, dummy; 52541056Swilliam 52645550Sbill chk = min(256,du->dk_bc/2); 52741056Swilliam /* Ready to receive data? */ 528*49568Swilliam while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; 52941056Swilliam 53041056Swilliam /*dprintf(DDSK,"addr %x\n", (int)bp->b_un.b_addr + du->dk_skip * 512);*/ 53141056Swilliam insw(wdc+wd_data,(int)bp->b_un.b_addr + du->dk_skip * 512 ,chk); 53243592Sdonahn du->dk_bc -= 2*chk; 53345550Sbill while (chk++ < 256) insw (wdc+wd_data,&dummy,1); 53441056Swilliam } 53541056Swilliam 53641056Swilliam wdxfer[du->dk_unit]++; 53741056Swilliam if (wdtab.b_active) { 53841056Swilliam if ((bp->b_flags & B_ERROR) == 0) { 53941056Swilliam du->dk_skip++; /* Add to successful sectors. */ 54041056Swilliam if (wdtab.b_errcnt) { 54141056Swilliam log(LOG_WARNING, "wd%d%c: ", 54241056Swilliam du->dk_unit, partch); 54341056Swilliam log(LOG_WARNING, 54441056Swilliam "soft %s error, sn %d bn %d error %b retries %d\n", 54541056Swilliam (bp->b_flags & B_READ) ? "read" : "write", 54641056Swilliam wd_errsector, wd_errbn, wd_errstat, 54741056Swilliam WDERR_BITS, wdtab.b_errcnt); 54841056Swilliam } 54941056Swilliam wdtab.b_errcnt = 0; 55041056Swilliam 55141056Swilliam /* see if more to transfer */ 55243592Sdonahn /*if (du->dk_skip < (bp->b_bcount + 511) / 512) {*/ 55345550Sbill if (du->dk_bc > 0 && wd_haderror == 0) { 55441056Swilliam wdstart(); 55541056Swilliam return; /* next chunk is started */ 55645550Sbill } else if (wd_haderror && wd_sebyse == 0) { 55745550Sbill du->dk_skip = 0; 55845550Sbill wd_haderror = 0; 55945550Sbill wd_sebyse = 1; 56045550Sbill wdstart(); 56145550Sbill return; /* redo xfer sector by sector */ 56241056Swilliam } 56341056Swilliam } 56441056Swilliam 56541056Swilliam done: 56645550Sbill wd_sebyse = 0; 56741056Swilliam /* done with this transfer, with or without error */ 56841056Swilliam wdtab.b_actf = dp->b_forw; 56941056Swilliam wdtab.b_errcnt = 0; 57041056Swilliam du->dk_skip = 0; 57141056Swilliam dp->b_active = 0; 57241056Swilliam dp->b_actf = bp->av_forw; 57341056Swilliam dp->b_errcnt = 0; 57441056Swilliam bp->b_resid = 0; 57541056Swilliam biodone(bp); 57641056Swilliam } 57741056Swilliam wdtab.b_active = 0; 57841056Swilliam if (dp->b_actf) 57941056Swilliam wdustart(du); /* requeue disk if more io to do */ 58041056Swilliam if (wdtab.b_actf) 58141056Swilliam wdstart(); /* start IO on next drive */ 58241056Swilliam } 58341056Swilliam 58441056Swilliam /* 58541056Swilliam * Initialize a drive. 58641056Swilliam */ 587*49568Swilliam wdopen(dev, flags, fmt) 588*49568Swilliam dev_t dev; 589*49568Swilliam int flags, fmt; 59041056Swilliam { 59141056Swilliam register unsigned int unit; 59241056Swilliam register struct buf *bp; 59341056Swilliam register struct disk *du; 594*49568Swilliam int part = wdpart(dev), mask = 1 << part; 595*49568Swilliam struct partition *pp; 59641056Swilliam struct dkbad *db; 59741056Swilliam int i, error = 0; 59841056Swilliam 599*49568Swilliam unit = wdunit(dev); 60041056Swilliam if (unit >= NWD) return (ENXIO) ; 60141056Swilliam du = &wddrives[unit]; 602*49568Swilliam #ifdef notdef 60341056Swilliam if (du->dk_open){ 60441056Swilliam du->dk_open++ ; 60541056Swilliam return(0); /* already is open, don't mess with it */ 60641056Swilliam } 60741056Swilliam #endif 60841056Swilliam du->dk_unit = unit; 60941056Swilliam wdutab[unit].b_actf = NULL; 61041056Swilliam /*if (flags & O_NDELAY) 61141056Swilliam du->dk_state = WANTOPENRAW; 61241056Swilliam else*/ 61341056Swilliam du->dk_state = WANTOPEN; 61441056Swilliam /* 61541056Swilliam * Use the default sizes until we've read the label, 61641056Swilliam * or longer if there isn't one there. 61741056Swilliam */ 61841056Swilliam du->dk_dd = dflt_sizes; 61941056Swilliam 62041056Swilliam /* 62141056Swilliam * Recal, read of disk label will be done in wdcontrol 62241056Swilliam * during first read operation. 62341056Swilliam */ 62441056Swilliam bp = geteblk(512); 62545549Sbill bp->b_dev = dev & 0xff00; 626*49568Swilliam bp->b_bcount = 0; 627*49568Swilliam bp->b_blkno = LABELSECTOR; 62841056Swilliam bp->b_flags = B_READ; 62941056Swilliam wdstrategy(bp); 63041056Swilliam biowait(bp); 63141056Swilliam if (bp->b_flags & B_ERROR) { 63241056Swilliam error = ENXIO; 63341056Swilliam du->dk_state = CLOSED; 63441056Swilliam goto done; 63541056Swilliam } 63641056Swilliam if (du->dk_state == OPENRAW) { 63741056Swilliam du->dk_state = OPENRAW; 63841056Swilliam goto done; 63941056Swilliam } 64041056Swilliam /* 64141056Swilliam * Read bad sector table into memory. 64241056Swilliam */ 64341056Swilliam i = 0; 64441056Swilliam do { 64541056Swilliam bp->b_flags = B_BUSY | B_READ; 646*49568Swilliam bp->b_blkno = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors 64741056Swilliam + i; 648*49568Swilliam if (du->dk_dd.d_secsize > DEV_BSIZE) 649*49568Swilliam bp->b_blkno *= du->dk_dd.d_secsize / DEV_BSIZE; 65041056Swilliam else 651*49568Swilliam bp->b_blkno /= DEV_BSIZE / du->dk_dd.d_secsize; 652*49568Swilliam bp->b_bcount = du->dk_dd.d_secsize; 653*49568Swilliam bp->b_cylin = du->dk_dd.d_ncylinders - 1; 65441056Swilliam wdstrategy(bp); 65541056Swilliam biowait(bp); 65641056Swilliam } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 && 657*49568Swilliam i < du->dk_dd.d_nsectors); 65841056Swilliam db = (struct dkbad *)(bp->b_un.b_addr); 65945550Sbill #define DKBAD_MAGIC 0x4321 66041056Swilliam if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 && 66141056Swilliam db->bt_flag == DKBAD_MAGIC) { 66241056Swilliam dkbad[unit] = *db; 66341056Swilliam du->dk_state = OPEN; 66441056Swilliam } else { 66541056Swilliam printf("wd%d: %s bad-sector file\n", unit, 66641056Swilliam (bp->b_flags & B_ERROR) ? "can't read" : "format error in"); 667*49568Swilliam error = ENXIO ; 66841056Swilliam du->dk_state = OPENRAW; 66941056Swilliam } 67041056Swilliam done: 67141056Swilliam bp->b_flags = B_INVAL | B_AGE; 67241056Swilliam brelse(bp); 67341056Swilliam if (error == 0) 67441056Swilliam du->dk_open = 1; 675*49568Swilliam 676*49568Swilliam /* 677*49568Swilliam * Warn if a partion is opened 678*49568Swilliam * that overlaps another partition which is open 679*49568Swilliam * unless one is the "raw" partition (whole disk). 680*49568Swilliam */ 681*49568Swilliam #define RAWPART 8 /* 'x' partition */ /* XXX */ 682*49568Swilliam if ((du->dk_openpart & mask) == 0 && part != RAWPART) { 683*49568Swilliam int start, end; 684*49568Swilliam 685*49568Swilliam pp = &du->dk_dd.d_partitions[part]; 686*49568Swilliam start = pp->p_offset; 687*49568Swilliam end = pp->p_offset + pp->p_size; 688*49568Swilliam for (pp = du->dk_dd.d_partitions; 689*49568Swilliam pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions]; 690*49568Swilliam pp++) { 691*49568Swilliam if (pp->p_offset + pp->p_size <= start || 692*49568Swilliam pp->p_offset >= end) 693*49568Swilliam continue; 694*49568Swilliam if (pp - du->dk_dd.d_partitions == RAWPART) 695*49568Swilliam continue; 696*49568Swilliam if (du->dk_openpart & (1 << (pp - 697*49568Swilliam du->dk_dd.d_partitions))) 698*49568Swilliam log(LOG_WARNING, 699*49568Swilliam "wd%d%c: overlaps open partition (%c)\n", 700*49568Swilliam unit, part + 'a', 701*49568Swilliam pp - du->dk_dd.d_partitions + 'a'); 702*49568Swilliam } 703*49568Swilliam } 704*49568Swilliam if (part >= du->dk_dd.d_npartitions) 705*49568Swilliam return (ENXIO); 706*49568Swilliam du->dk_openpart |= mask; 707*49568Swilliam switch (fmt) { 708*49568Swilliam case S_IFCHR: 709*49568Swilliam du->dk_copenpart |= mask; 710*49568Swilliam break; 711*49568Swilliam case S_IFBLK: 712*49568Swilliam du->dk_bopenpart |= mask; 713*49568Swilliam break; 714*49568Swilliam } 71541056Swilliam return (error); 71641056Swilliam } 71741056Swilliam 71841056Swilliam /* 71941056Swilliam * Implement operations other than read/write. 72041056Swilliam * Called from wdstart or wdintr during opens and formats. 72141056Swilliam * Uses finite-state-machine to track progress of operation in progress. 72241056Swilliam * Returns 0 if operation still in progress, 1 if completed. 72341056Swilliam */ 72441056Swilliam wdcontrol(bp) 72541056Swilliam register struct buf *bp; 72641056Swilliam { 72741056Swilliam register struct disk *du; 72841056Swilliam register unit; 72941056Swilliam unsigned char stat; 73041056Swilliam int s, cnt; 73141056Swilliam extern int bootdev, cyloffset; 73241056Swilliam 733*49568Swilliam du = &wddrives[wdunit(bp->b_dev)]; 73441056Swilliam unit = du->dk_unit; 73541056Swilliam switch (DISKSTATE(du->dk_state)) { 73641056Swilliam 73741056Swilliam tryagainrecal: 73841056Swilliam case WANTOPEN: /* set SDH, step rate, do restore */ 73941056Swilliam #ifdef WDDEBUG 74041056Swilliam dprintf(DDSK,"wd%d: recal ", unit); 74141056Swilliam #endif 74241056Swilliam s = splbio(); /* not called from intr level ... */ 74341056Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 74441056Swilliam wdtab.b_active = 1; 74541056Swilliam outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); 74641056Swilliam du->dk_state++; 74741056Swilliam splx(s); 74841056Swilliam return(0); 74941056Swilliam 75041056Swilliam case RECAL: 75145551Sbill if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { 75241056Swilliam printf("wd%d: recal", du->dk_unit); 75341056Swilliam if (unit == 0) { 75441056Swilliam printf(": status %b error %b\n", 75541056Swilliam stat, WDCS_BITS, 75641056Swilliam inb(wdc+wd_error), WDERR_BITS); 75741056Swilliam if (++wdtab.b_errcnt < RETRIES) 75841056Swilliam goto tryagainrecal; 75941056Swilliam } 76041056Swilliam goto badopen; 76141056Swilliam } 762*49568Swilliam 763*49568Swilliam /* some compaq controllers require this ... */ 764*49568Swilliam wdsetctlr(bp->b_dev, du); 765*49568Swilliam 76641056Swilliam wdtab.b_errcnt = 0; 76741056Swilliam if (ISRAWSTATE(du->dk_state)) { 76841056Swilliam du->dk_state = OPENRAW; 76941056Swilliam return(1); 77041056Swilliam } 77141056Swilliam retry: 77241056Swilliam #ifdef WDDEBUG 77341056Swilliam dprintf(DDSK,"rdlabel "); 77441056Swilliam #endif 775*49568Swilliam if( cyloffset < 0 || cyloffset > 8192) cyloffset=0; 77641056Swilliam /* 777*49568Swilliam * Read in sector LABELSECTOR to get the pack label 778*49568Swilliam * and geometry. 77941056Swilliam */ 78041056Swilliam outb(wdc+wd_precomp, 0xff);/* sometimes this is head bit 3 */ 78141056Swilliam outb(wdc+wd_seccnt, 1); 782*49568Swilliam outb(wdc+wd_sector, LABELSECTOR+1); 78341056Swilliam /*if (bp->b_dev == bootdev) { 78441056Swilliam (wdc+wd_cyl_lo = cyloffset & 0xff; 78541056Swilliam (wdc+wd_cyl_hi = cyloffset >> 8; 78641056Swilliam } else { 78741056Swilliam (wdc+wd_cyl_lo = 0; 78841056Swilliam (wdc+wd_cyl_hi = 0; 78941056Swilliam }*/ 79041056Swilliam outb(wdc+wd_cyl_lo, (cyloffset & 0xff)); 79141056Swilliam outb(wdc+wd_cyl_hi, (cyloffset >> 8)); 79241056Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 79341056Swilliam outb(wdc+wd_command, WDCC_READ); 79441056Swilliam du->dk_state = RDLABEL; 79541056Swilliam return(0); 79641056Swilliam 79741056Swilliam case RDLABEL: 79841056Swilliam if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { 79941056Swilliam if (++wdtab.b_errcnt < RETRIES) 80041056Swilliam goto retry; 80141056Swilliam printf("wd%d: read label", unit); 80241056Swilliam goto badopen; 80341056Swilliam } 80441056Swilliam 80541056Swilliam insw(wdc+wd_data, bp->b_un.b_addr, 256); 80641056Swilliam 80741056Swilliam if (((struct disklabel *) 808*49568Swilliam (bp->b_un.b_addr + LABELOFFSET))->d_magic == DISKMAGIC) { 80941056Swilliam du->dk_dd = 81041056Swilliam * (struct disklabel *) (bp->b_un.b_addr + LABELOFFSET); 81141056Swilliam } else { 81241056Swilliam printf("wd%d: bad disk label\n", du->dk_unit); 81341056Swilliam du->dk_state = OPENRAW; 81441056Swilliam } 81545552Sbill 81645552Sbill s = splbio(); /* not called from intr level ... */ 817*49568Swilliam while ((stat = inb(wdc+wd_status)) & WDCS_BUSY); 818*49568Swilliam 819*49568Swilliam wdsetctlr(bp->b_dev, du); 820*49568Swilliam 82145552Sbill outb(wdc+wd_seccnt, 0); 82245552Sbill splx(s); 82345552Sbill 82441056Swilliam if (du->dk_state == RDLABEL) 82541056Swilliam du->dk_state = RDBADTBL; 82641056Swilliam /* 82741056Swilliam * The rest of the initialization can be done 82841056Swilliam * by normal means. 82941056Swilliam */ 83041056Swilliam return(1); 83141056Swilliam 83241056Swilliam default: 833*49568Swilliam panic("wdcontrol"); 83441056Swilliam } 83541056Swilliam /* NOTREACHED */ 83641056Swilliam 83741056Swilliam badopen: 83841056Swilliam printf(": status %b error %b\n", 83941056Swilliam stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS); 84041056Swilliam du->dk_state = OPENRAW; 84141056Swilliam return(1); 84241056Swilliam } 84341056Swilliam 844*49568Swilliam wdsetctlr(dev, du) dev_t dev; struct disk *du; { 845*49568Swilliam int stat; 84641056Swilliam 847*49568Swilliam outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders); 848*49568Swilliam outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders)>>8); 849*49568Swilliam outb(wdc+wd_sdh, WDSD_IBM | (wdunit(dev) << 4) + du->dk_dd.d_ntracks-1); 850*49568Swilliam outb(wdc+wd_seccnt, du->dk_dd.d_nsectors); 851*49568Swilliam outb(wdc+wd_command, 0x91); 852*49568Swilliam 853*49568Swilliam while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) ; 854*49568Swilliam stat = inb(wdc+wd_error); 855*49568Swilliam return(stat); 856*49568Swilliam } 857*49568Swilliam 858*49568Swilliam /* ARGSUSED */ 859*49568Swilliam wdclose(dev, flags, fmt) 860*49568Swilliam dev_t dev; 861*49568Swilliam int flags, fmt; 862*49568Swilliam { 863*49568Swilliam register struct disk *du; 864*49568Swilliam 865*49568Swilliam du = &wddrives[wdunit(dev)]; 86641056Swilliam du->dk_open-- ; 86741056Swilliam /*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */ 86841056Swilliam } 86941056Swilliam 87041056Swilliam wdioctl(dev,cmd,addr,flag) 87141056Swilliam dev_t dev; 87241056Swilliam caddr_t addr; 87341056Swilliam { 874*49568Swilliam int unit = wdunit(dev); 87541056Swilliam register struct disk *du; 87641056Swilliam int error = 0; 87741056Swilliam struct uio auio; 87841056Swilliam struct iovec aiov; 87941056Swilliam /*int wdformat();*/ 88041056Swilliam 88141056Swilliam du = &wddrives[unit]; 88241056Swilliam 88341056Swilliam switch (cmd) { 88441056Swilliam 88541056Swilliam case DIOCGDINFO: 88641056Swilliam *(struct disklabel *)addr = du->dk_dd; 88741056Swilliam break; 88841056Swilliam 889*49568Swilliam case DIOCGPART: 890*49568Swilliam ((struct partinfo *)addr)->disklab = &du->dk_dd; 891*49568Swilliam ((struct partinfo *)addr)->part = 892*49568Swilliam &du->dk_dd.d_partitions[wdpart(dev)]; 893*49568Swilliam break; 894*49568Swilliam 895*49568Swilliam case DIOCSDINFO: 896*49568Swilliam if ((flag & FWRITE) == 0) 897*49568Swilliam error = EBADF; 898*49568Swilliam else 899*49568Swilliam error = setdisklabel(&du->dk_dd, 900*49568Swilliam (struct disklabel *)addr, 901*49568Swilliam 0 /*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/); 902*49568Swilliam /*if (error == 0 && dk->dk_state == OPENRAW && 903*49568Swilliam vdreset_drive(vddinfo[unit])) 904*49568Swilliam dk->dk_state = OPEN;*/ 905*49568Swilliam wdsetctlr(dev, du); 906*49568Swilliam break; 907*49568Swilliam 908*49568Swilliam case DIOCWLABEL: 909*49568Swilliam if ((flag & FWRITE) == 0) 910*49568Swilliam error = EBADF; 911*49568Swilliam else 912*49568Swilliam du->dk_wlabel = *(int *)addr; 913*49568Swilliam break; 914*49568Swilliam 915*49568Swilliam case DIOCWDINFO: 916*49568Swilliam if ((flag & FWRITE) == 0) 917*49568Swilliam error = EBADF; 918*49568Swilliam else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr, 919*49568Swilliam 0/*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/)) == 0) { 920*49568Swilliam int wlab; 921*49568Swilliam 922*49568Swilliam /*if (error == 0 && dk->dk_state == OPENRAW && 923*49568Swilliam vdreset_drive(vddinfo[unit])) 924*49568Swilliam dk->dk_state = OPEN; */ 925*49568Swilliam wdsetctlr(dev, du); 926*49568Swilliam 927*49568Swilliam /* simulate opening partition 0 so write succeeds */ 928*49568Swilliam /* dk->dk_openpart |= (1 << 0); /* XXX */ 929*49568Swilliam wlab = du->dk_wlabel; 930*49568Swilliam du->dk_wlabel = 1; 931*49568Swilliam error = writedisklabel(dev, wdstrategy, &du->dk_dd,wdpart(dev)); 932*49568Swilliam /*dk->dk_openpart = dk->dk_copenpart | dk->dk_bopenpart;*/ 933*49568Swilliam du->dk_wlabel = wlab; 934*49568Swilliam } 935*49568Swilliam break; 936*49568Swilliam 937*49568Swilliam #ifdef notyet 93841056Swilliam case DIOCGDINFOP: 93941056Swilliam *(struct disklabel **)addr = &(du->dk_dd); 94041056Swilliam break; 94141056Swilliam 94241056Swilliam case DIOCWFORMAT: 94341056Swilliam if ((flag & FWRITE) == 0) 94441056Swilliam error = EBADF; 94541056Swilliam else { 94641056Swilliam register struct format_op *fop; 94741056Swilliam 94841056Swilliam fop = (struct format_op *)addr; 94941056Swilliam aiov.iov_base = fop->df_buf; 95041056Swilliam aiov.iov_len = fop->df_count; 95141056Swilliam auio.uio_iov = &aiov; 95241056Swilliam auio.uio_iovcnt = 1; 95341056Swilliam auio.uio_resid = fop->df_count; 95441056Swilliam auio.uio_segflg = 0; 95541056Swilliam auio.uio_offset = 956*49568Swilliam fop->df_startblk * du->dk_dd.d_secsize; 95741056Swilliam error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE, 95841056Swilliam minphys, &auio); 95941056Swilliam fop->df_count -= auio.uio_resid; 96041056Swilliam fop->df_reg[0] = du->dk_status; 96141056Swilliam fop->df_reg[1] = du->dk_error; 96241056Swilliam } 96341056Swilliam break; 96441056Swilliam #endif 96541056Swilliam 96641056Swilliam default: 96741056Swilliam error = ENOTTY; 96841056Swilliam break; 96941056Swilliam } 97041056Swilliam return (error); 97141056Swilliam } 97241056Swilliam 97341056Swilliam /*wdformat(bp) 97441056Swilliam struct buf *bp; 97541056Swilliam { 97641056Swilliam 97741056Swilliam bp->b_flags |= B_FORMAT; 97841056Swilliam return (wdstrategy(bp)); 97941056Swilliam }*/ 98041056Swilliam 98141056Swilliam /* 98241056Swilliam * Routines to do raw IO for a unit. 98341056Swilliam */ 98441056Swilliam wdread(dev, uio) /* character read routine */ 98541056Swilliam dev_t dev; 98641056Swilliam struct uio *uio; 98741056Swilliam { 988*49568Swilliam int unit = wdunit(dev) ; 98941056Swilliam 99041056Swilliam if (unit >= NWD) return(ENXIO); 99141056Swilliam return(physio(wdstrategy, &rwdbuf[unit], dev, B_READ, minphys, uio)); 99241056Swilliam } 99341056Swilliam 99441056Swilliam 99541056Swilliam wdwrite(dev, uio) /* character write routine */ 99641056Swilliam dev_t dev; 99741056Swilliam struct uio *uio; 99841056Swilliam { 999*49568Swilliam int unit = wdunit(dev) ; 100041056Swilliam 100141056Swilliam if (unit >= NWD) return(ENXIO); 100241056Swilliam return(physio(wdstrategy, &rwdbuf[unit], dev, B_WRITE, minphys, uio)); 100341056Swilliam } 100441056Swilliam 100541056Swilliam wdsize(dev) 100641056Swilliam dev_t dev; 100741056Swilliam { 1008*49568Swilliam register unit = wdunit(dev); 1009*49568Swilliam register part = wdpart(dev); 101041056Swilliam register struct disk *du; 101141056Swilliam register val ; 101241056Swilliam 101341056Swilliam if (unit >= NWD) return(-1); 1014*49568Swilliam if (wddrives[unit].dk_state == 0) { 1015*49568Swilliam val = wdopen (dev, 0); 1016*49568Swilliam if (val < 0) 1017*49568Swilliam return (-1); 1018*49568Swilliam } 101941056Swilliam du = &wddrives[unit]; 1020*49568Swilliam return((int)((u_long)du->dk_dd.d_partitions[part].p_size * 1021*49568Swilliam du->dk_dd.d_secsize / 512)); 102241056Swilliam } 102341056Swilliam 1024*49568Swilliam extern char *vmmap; /* poor name! */ 1025*49568Swilliam 102641056Swilliam wddump(dev) /* dump core after a system crash */ 102741056Swilliam dev_t dev; 102841056Swilliam { 102941056Swilliam register struct disk *du; /* disk unit to do the IO */ 103041056Swilliam register struct bt_bad *bt_ptr; 103141056Swilliam long num; /* number of sectors to write */ 1032*49568Swilliam int unit, part; 103341056Swilliam long cyloff, blknum, blkcnt; 1034*49568Swilliam long cylin, head, sector, stat; 103541056Swilliam long secpertrk, secpercyl, nblocks, i; 1036*49568Swilliam char *addr; 1037*49568Swilliam extern int Maxmem; 103841056Swilliam static wddoingadump = 0 ; 1039*49568Swilliam extern CMAP1; 1040*49568Swilliam extern char CADDR1[]; 104141056Swilliam 1042*49568Swilliam 1043*49568Swilliam #ifdef ARGO 1044*49568Swilliam outb(0x461,0); /* disable failsafe timer */ 1045*49568Swilliam #endif 1046*49568Swilliam addr = (char *) 0; /* starting address */ 104741056Swilliam /* size of memory to dump */ 1048*49568Swilliam num = Maxmem; 1049*49568Swilliam unit = wdunit(dev); /* eventually support floppies? */ 1050*49568Swilliam part = wdpart(dev); /* file system */ 105141056Swilliam /* check for acceptable drive number */ 105241056Swilliam if (unit >= NWD) return(ENXIO); 105341056Swilliam 105441056Swilliam du = &wddrives[unit]; 105541056Swilliam /* was it ever initialized ? */ 105641056Swilliam if (du->dk_state < OPEN) return (ENXIO) ; 105741056Swilliam 105841056Swilliam /* Convert to disk sectors */ 1059*49568Swilliam num = (u_long) num * NBPG / du->dk_dd.d_secsize; 106041056Swilliam 106141056Swilliam /* check if controller active */ 106241056Swilliam /*if (wdtab.b_active) return(EFAULT); */ 106341056Swilliam if (wddoingadump) return(EFAULT); 106441056Swilliam 1065*49568Swilliam secpertrk = du->dk_dd.d_nsectors; 1066*49568Swilliam secpercyl = du->dk_dd.d_secpercyl; 1067*49568Swilliam nblocks = du->dk_dd.d_partitions[part].p_size; 1068*49568Swilliam cyloff = du->dk_dd.d_partitions[part].p_offset / secpercyl; 106941056Swilliam 1070*49568Swilliam /*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/ 107141056Swilliam /* check transfer bounds against partition size */ 1072*49568Swilliam if ((dumplo < 0) || ((dumplo + num) > nblocks)) 107341056Swilliam return(EINVAL); 107441056Swilliam 107541056Swilliam /*wdtab.b_active = 1; /* mark controller active for if we 107641056Swilliam panic during the dump */ 107741056Swilliam wddoingadump = 1 ; i = 100000 ; 1078*49568Swilliam while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ; 1079*49568Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 1080*49568Swilliam outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); 1081*49568Swilliam while (inb(wdc+wd_status) & WDCS_BUSY) ; 1082*49568Swilliam 1083*49568Swilliam /* some compaq controllers require this ... */ 1084*49568Swilliam wdsetctlr(dev, du); 108541056Swilliam 108641056Swilliam blknum = dumplo; 108741056Swilliam while (num > 0) { 108841056Swilliam #ifdef notdef 108941056Swilliam if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER; 109041056Swilliam if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl) 109141056Swilliam blkcnt = secpercyl - (blknum % secpercyl); 109241056Swilliam /* keep transfer within current cylinder */ 109341056Swilliam #endif 1094*49568Swilliam pmap_enter(pmap_kernel(), vmmap, addr, VM_PROT_READ, TRUE); 109541056Swilliam 109641056Swilliam /* compute disk address */ 109741056Swilliam cylin = blknum / secpercyl; 109841056Swilliam head = (blknum % secpercyl) / secpertrk; 109941056Swilliam sector = blknum % secpertrk; 110043592Sdonahn cylin += cyloff; 110141056Swilliam 1102*49568Swilliam #ifdef notyet 110341056Swilliam /* 110441056Swilliam * See if the current block is in the bad block list. 110541056Swilliam * (If we have one.) 110641056Swilliam */ 110741056Swilliam for (bt_ptr = dkbad[unit].bt_bad; 110841056Swilliam bt_ptr->bt_cyl != -1; bt_ptr++) { 110941056Swilliam if (bt_ptr->bt_cyl > cylin) 111041056Swilliam /* Sorted list, and we passed our cylinder. 111141056Swilliam quit. */ 111241056Swilliam break; 111341056Swilliam if (bt_ptr->bt_cyl == cylin && 111441056Swilliam bt_ptr->bt_trksec == (head << 8) + sector) { 111541056Swilliam /* 111641056Swilliam * Found bad block. Calculate new block addr. 111741056Swilliam * This starts at the end of the disk (skip the 111841056Swilliam * last track which is used for the bad block list), 111941056Swilliam * and works backwards to the front of the disk. 112041056Swilliam */ 1121*49568Swilliam blknum = (du->dk_dd.d_secperunit) 1122*49568Swilliam - du->dk_dd.d_nsectors 112341056Swilliam - (bt_ptr - dkbad[unit].bt_bad) - 1; 112441056Swilliam cylin = blknum / secpercyl; 112541056Swilliam head = (blknum % secpercyl) / secpertrk; 112641056Swilliam sector = blknum % secpertrk; 112741056Swilliam break; 112841056Swilliam } 112941056Swilliam 1130*49568Swilliam #endif 1131*49568Swilliam sector++; /* origin 1 */ 1132*49568Swilliam 113341056Swilliam /* select drive. */ 1134*49568Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); 1135*49568Swilliam while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; 113641056Swilliam 113741056Swilliam /* transfer some blocks */ 1138*49568Swilliam outb(wdc+wd_sector, sector); 1139*49568Swilliam outb(wdc+wd_seccnt,1); 1140*49568Swilliam outb(wdc+wd_cyl_lo, cylin); 1141*49568Swilliam outb(wdc+wd_cyl_hi, cylin >> 8); 114241056Swilliam #ifdef notdef 114341056Swilliam /* lets just talk about this first...*/ 1144*49568Swilliam pg ("sdh 0%o sector %d cyl %d addr 0x%x", 1145*49568Swilliam inb(wdc+wd_sdh), inb(wdc+wd_sector), 1146*49568Swilliam inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr) ; 114741056Swilliam #endif 1148*49568Swilliam #ifdef ODYSSEUS 1149*49568Swilliam if(cylin < 46 || cylin > 91)pg("oops"); 1150*49568Swilliam #endif 1151*49568Swilliam #ifdef PRIAM 1152*49568Swilliam if(cylin < 40 || cylin > 79)pg("oops"); 1153*49568Swilliam #endif 1154*49568Swilliam outb(wdc+wd_command, WDCC_WRITE); 115541056Swilliam 115641056Swilliam /* Ready to send data? */ 1157*49568Swilliam while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; 1158*49568Swilliam if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; 115941056Swilliam 1160*49568Swilliam outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256); 1161*49568Swilliam (int) addr += 512; 1162*49568Swilliam 1163*49568Swilliam if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; 116441056Swilliam /* Check data request (should be done). */ 1165*49568Swilliam if (inb(wdc+wd_status) & WDCS_DRQ) return(EIO) ; 116641056Swilliam 116741056Swilliam /* wait for completion */ 1168*49568Swilliam for ( i = 1000000 ; inb(wdc+wd_status) & WDCS_BUSY ; i--) { 116941056Swilliam if (i < 0) return (EIO) ; 117041056Swilliam } 117141056Swilliam /* error check the xfer */ 1172*49568Swilliam if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; 117341056Swilliam /* update block count */ 117441056Swilliam num--; 117541056Swilliam blknum++ ; 117641056Swilliam if (num % 100 == 0) printf(".") ; 117741056Swilliam } 117841056Swilliam return(0); 117941056Swilliam } 118041056Swilliam #endif 1181