141056Swilliam /*- 241056Swilliam * Copyright (c) 1990 The Regents of the University of California. 341056Swilliam * All rights reserved. 441056Swilliam * 541056Swilliam * This code is derived from software contributed to Berkeley by 641056Swilliam * William Jolitz. 741056Swilliam * 841056Swilliam * %sccs.include.386.c% 941056Swilliam * 10*45552Sbill * @(#)wd.c 5.6 (Berkeley) 11/08/90 1141056Swilliam */ 1243592Sdonahn 1341056Swilliam #include "wd.h" 1441056Swilliam #if NWD > 0 1541056Swilliam 1641056Swilliam #include "param.h" 1741056Swilliam #include "dkbad.h" 1841056Swilliam #include "systm.h" 1941056Swilliam #include "conf.h" 2041056Swilliam #include "file.h" 2141056Swilliam #include "dir.h" 2241056Swilliam #include "user.h" 2341056Swilliam #include "ioctl.h" 2441056Swilliam #include "disk.h" 2541056Swilliam #include "buf.h" 2641056Swilliam #include "vm.h" 2741056Swilliam #include "uio.h" 2841056Swilliam #include "machine/pte.h" 29*45552Sbill #include "machine/isa/device.h" 3041056Swilliam #include "atio.h" 3141056Swilliam #include "icu.h" 3241056Swilliam #include "wdreg.h" 3341056Swilliam #include "syslog.h" 3441056Swilliam 3541056Swilliam #define RETRIES 5 /* number of retries before giving up */ 3641056Swilliam #define MAXTRANSFER 256 /* max size of transfer in page clusters */ 3741056Swilliam 3841056Swilliam #define WDUNIT(dev) ((minor(dev) & 070) >> 3) 3941056Swilliam 4041056Swilliam #define b_cylin b_resid /* cylinder number for doing IO to */ 4141056Swilliam /* shares an entry in the buf struct */ 4241056Swilliam 4341056Swilliam /* 4441056Swilliam * Drive states. Used for open and format operations. 4541056Swilliam * States < OPEN (> 0) are transient, during an open operation. 4641056Swilliam * OPENRAW is used for unlabeled disks, and for floppies, to inhibit 4741056Swilliam * bad-sector forwarding. 4841056Swilliam */ 4941056Swilliam #define RAWDISK 8 /* raw disk operation, no translation*/ 5041056Swilliam #define ISRAWSTATE(s) (RAWDISK&(s)) /* are we in a raw state? */ 5141056Swilliam #define DISKSTATE(s) (~RAWDISK&(s)) /* are we in a given state regardless 5241056Swilliam of raw or cooked mode? */ 5341056Swilliam 5441056Swilliam #define CLOSED 0 /* disk is closed. */ 5541056Swilliam /* "cooked" disk states */ 5641056Swilliam #define WANTOPEN 1 /* open requested, not started */ 5741056Swilliam #define RECAL 2 /* doing restore */ 5841056Swilliam #define RDLABEL 3 /* reading pack label */ 5941056Swilliam #define RDBADTBL 4 /* reading bad-sector table */ 6041056Swilliam #define OPEN 5 /* done with open */ 6141056Swilliam 6241056Swilliam #define WANTOPENRAW (WANTOPEN|RAWDISK) /* raw WANTOPEN */ 6341056Swilliam #define RECALRAW (RECAL|RAWDISK) /* raw open, doing restore */ 6441056Swilliam #define OPENRAW (OPEN|RAWDISK) /* open, but unlabeled disk or floppy */ 6541056Swilliam 6641056Swilliam 6741056Swilliam /* 6841056Swilliam * The structure of a disk drive. 6941056Swilliam */ 7041056Swilliam struct disk { 7141056Swilliam struct disklabel dk_dd; /* device configuration data */ 7241056Swilliam long dk_bc; /* byte count left */ 7341056Swilliam short dk_skip; /* blocks already transferred */ 7441056Swilliam char dk_unit; /* physical unit number */ 7541056Swilliam char dk_sdh; /* sdh prototype */ 7641056Swilliam char dk_state; /* control state */ 7741056Swilliam u_char dk_status; /* copy of status reg. */ 7841056Swilliam u_char dk_error; /* copy of error reg. */ 7941056Swilliam short dk_open; /* open/closed refcnt */ 8041056Swilliam }; 8141056Swilliam 8241056Swilliam /* 8341056Swilliam * This label is used as a default when initializing a new or raw disk. 8441056Swilliam * It really only lets us access the first track until we know more. 8541056Swilliam */ 8641056Swilliam struct disklabel dflt_sizes = { 8741056Swilliam DISKMAGIC, DTYPE_ST506, 8841056Swilliam { 8941056Swilliam 512, /* sector size */ 9045550Sbill 36, /* # of sectors per track */ 9141056Swilliam 15, /* # of tracks per cylinder */ 9245550Sbill 1224, /* # of cylinders per unit */ 9345550Sbill 36*15, /* # of sectors per cylinder */ 9445550Sbill 1224*15*36, /* # of sectors per unit */ 9541056Swilliam 0 /* write precomp cylinder (none) */ 9641056Swilliam }, 9745550Sbill 21600, 0, /* A=root filesystem */ 9845550Sbill 21600, 40, 9945550Sbill 660890, 0, /* C=whole disk */ 10045550Sbill 216000, 80, 10141056Swilliam 0, 0, 10241056Swilliam 0, 0, 10341056Swilliam 0, 0, 10445550Sbill 399600, 480 10541056Swilliam }; 10645550Sbill 10741056Swilliam static struct dkbad dkbad[NWD]; 10841056Swilliam struct disk wddrives[NWD] = {0}; /* table of units */ 10941056Swilliam struct buf wdtab = {0}; 11041056Swilliam struct buf wdutab[NWD] = {0}; /* head of queue per drive */ 11141056Swilliam struct buf rwdbuf[NWD] = {0}; /* buffers for raw IO */ 11241056Swilliam long wdxfer[NWD] = {0}; /* count of transfers */ 11341056Swilliam int writeprotected[NWD] = { 0 }; 11441056Swilliam int wdprobe(), wdattach(), wdintr(); 115*45552Sbill struct isa_driver wddriver = { 11641056Swilliam wdprobe, wdattach, "wd", 11741056Swilliam }; 11841056Swilliam #include "dbg.h" 11941056Swilliam 120*45552Sbill static wdc; 12141056Swilliam /* 12241056Swilliam * Probe routine 12341056Swilliam */ 12441056Swilliam wdprobe(dvp) 125*45552Sbill struct isa_device *dvp; 12641056Swilliam { 127*45552Sbill wdc = dvp->id_iobase; 12841056Swilliam 12941056Swilliam #ifdef lint 13041056Swilliam wdintr(0); 13141056Swilliam #endif 13241056Swilliam outb(wdc+wd_error, 0x5a) ; /* error register not writable */ 13341056Swilliam /*wdp->wd_cyl_hi = 0xff ;/* only two bits of cylhi are implemented */ 13441056Swilliam outb(wdc+wd_cyl_lo, 0xa5) ; /* but all of cyllo are implemented */ 13541056Swilliam if(inb(wdc+wd_error) != 0x5a /*&& wdp->wd_cyl_hi == 3*/ 13641056Swilliam && inb(wdc+wd_cyl_lo) == 0xa5) 13741056Swilliam return(1) ; 13841056Swilliam return (0); 13941056Swilliam } 14041056Swilliam 14141056Swilliam /* 14241056Swilliam * attach each drive if possible. 14341056Swilliam */ 14441056Swilliam wdattach(dvp) 145*45552Sbill struct isa_device *dvp; 14641056Swilliam { 147*45552Sbill int unit = dvp->id_unit; 14841056Swilliam 149*45552Sbill outb(wdc+wd_ctlr,12); 150*45552Sbill DELAY(1000); 151*45552Sbill outb(wdc+wd_ctlr,8); 15241056Swilliam } 15341056Swilliam 15441056Swilliam /* Read/write routine for a buffer. Finds the proper unit, range checks 15541056Swilliam * arguments, and schedules the transfer. Does not wait for the transfer 15641056Swilliam * to complete. Multi-page transfers are supported. All I/O requests must 15741056Swilliam * be a multiple of a sector in length. 15841056Swilliam */ 15941056Swilliam wdstrategy(bp) 16041056Swilliam register struct buf *bp; /* IO operation to perform */ 16141056Swilliam { 16241056Swilliam register struct buf *dp; 16341056Swilliam register struct disk *du; /* Disk unit to do the IO. */ 16441056Swilliam long nblocks, cyloff, blknum; 16541056Swilliam int unit = WDUNIT(bp->b_dev), xunit = minor(bp->b_dev) & 7; 16641056Swilliam int s; 16741056Swilliam 16841056Swilliam if ((unit >= NWD) || (bp->b_blkno < 0)) { 16943592Sdonahn printf("wdstrat: unit = %d, blkno = %d, bcount = %d\n", 17041056Swilliam unit, bp->b_blkno, bp->b_bcount); 17143592Sdonahn pg("wd:error in wdstrategy"); 17241056Swilliam bp->b_flags |= B_ERROR; 17341056Swilliam goto bad; 17441056Swilliam } 17541056Swilliam if (writeprotected[unit] && (bp->b_flags & B_READ) == 0) { 17641056Swilliam printf("wd%d: write protected\n", unit); 17741056Swilliam goto bad; 17841056Swilliam } 17941056Swilliam du = &wddrives[unit]; 18041056Swilliam if (DISKSTATE(du->dk_state) != OPEN) 18141056Swilliam goto q; 18241056Swilliam /* 18341056Swilliam * Convert DEV_BSIZE "blocks" to sectors. 18441056Swilliam * Note: doing the conversions this way limits the partition size 18541056Swilliam * to about 8 million sectors (1-8 Gb). 18641056Swilliam */ 18741056Swilliam blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.dk_secsize; 18841056Swilliam if (((u_long) bp->b_blkno * DEV_BSIZE % du->dk_dd.dk_secsize != 0) || 18943592Sdonahn bp->b_bcount >= MAXTRANSFER * CLBYTES) { 19041056Swilliam bp->b_flags |= B_ERROR; 19141056Swilliam goto bad; 19241056Swilliam } 19341056Swilliam nblocks = du->dk_dd.dk_partition[xunit].nblocks; 19441056Swilliam cyloff = du->dk_dd.dk_partition[xunit].cyloff; 19541056Swilliam if (blknum + (bp->b_bcount / du->dk_dd.dk_secsize) > nblocks) { 19641056Swilliam if (blknum == nblocks) 19741056Swilliam bp->b_resid = bp->b_bcount; 19841056Swilliam else 19941056Swilliam bp->b_flags |= B_ERROR; 20041056Swilliam goto bad; 20141056Swilliam } 20241056Swilliam bp->b_cylin = blknum / du->dk_dd.dk_secpercyl + cyloff; 20341056Swilliam q: 20441056Swilliam dp = &wdutab[unit]; 20545550Sbill s = splhigh(); 20641056Swilliam disksort(dp, bp); 20741056Swilliam if (dp->b_active == 0) 20841056Swilliam wdustart(du); /* start drive if idle */ 20941056Swilliam if (wdtab.b_active == 0) 21041056Swilliam wdstart(s); /* start IO if controller idle */ 21141056Swilliam splx(s); 21241056Swilliam return; 21341056Swilliam 21441056Swilliam bad: 21541056Swilliam bp->b_error = EINVAL; 21641056Swilliam biodone(bp); 21741056Swilliam } 21841056Swilliam 21941056Swilliam /* Routine to queue a read or write command to the controller. The request is 22041056Swilliam * linked into the active list for the controller. If the controller is idle, 22141056Swilliam * the transfer is started. 22241056Swilliam */ 22341056Swilliam wdustart(du) 22441056Swilliam register struct disk *du; 22541056Swilliam { 22641056Swilliam register struct buf *bp, *dp; 22741056Swilliam 22841056Swilliam dp = &wdutab[du->dk_unit]; 22941056Swilliam if (dp->b_active) 23041056Swilliam return; 23141056Swilliam bp = dp->b_actf; 23241056Swilliam if (bp == NULL) 23341056Swilliam return; 23441056Swilliam dp->b_forw = NULL; 23541056Swilliam if (wdtab.b_actf == NULL) /* link unit into active list */ 23641056Swilliam wdtab.b_actf = dp; 23741056Swilliam else 23841056Swilliam wdtab.b_actl->b_forw = dp; 23941056Swilliam wdtab.b_actl = dp; 24041056Swilliam dp->b_active = 1; /* mark the drive as busy */ 24141056Swilliam } 24241056Swilliam 24341056Swilliam /* 24441056Swilliam * Controller startup routine. This does the calculation, and starts 24541056Swilliam * a single-sector read or write operation. Called to start a transfer, 24641056Swilliam * or from the interrupt routine to continue a multi-sector transfer. 24741056Swilliam * RESTRICTIONS: 24841056Swilliam * 1. The transfer length must be an exact multiple of the sector size. 24941056Swilliam */ 25041056Swilliam 25145550Sbill static wd_sebyse; 25245550Sbill 25341056Swilliam wdstart() 25441056Swilliam { 25541056Swilliam register struct disk *du; /* disk unit for IO */ 25641056Swilliam register struct buf *bp; 25741056Swilliam struct buf *dp; 25841056Swilliam register struct bt_bad *bt_ptr; 25941056Swilliam long blknum, pagcnt, cylin, head, sector; 26041056Swilliam long secpertrk, secpercyl, addr, i; 26141056Swilliam int minor_dev, unit, s; 26241056Swilliam 26341056Swilliam loop: 26441056Swilliam dp = wdtab.b_actf; 26541056Swilliam if (dp == NULL) 26641056Swilliam return; 26741056Swilliam bp = dp->b_actf; 26841056Swilliam if (bp == NULL) { 26941056Swilliam wdtab.b_actf = dp->b_forw; 27041056Swilliam goto loop; 27141056Swilliam } 27241056Swilliam unit = WDUNIT(bp->b_dev); 27341056Swilliam du = &wddrives[unit]; 27441056Swilliam if (DISKSTATE(du->dk_state) <= RDLABEL) { 27541056Swilliam if (wdcontrol(bp)) { 27641056Swilliam dp->b_actf = bp->av_forw; 27741056Swilliam goto loop; /* done */ 27841056Swilliam } 27941056Swilliam return; 28041056Swilliam } 28141056Swilliam minor_dev = minor(bp->b_dev) & 7; 28241056Swilliam secpertrk = du->dk_dd.dk_nsectors; 28341056Swilliam secpercyl = du->dk_dd.dk_secpercyl; 28441056Swilliam /* 28541056Swilliam * Convert DEV_BSIZE "blocks" to sectors. 28641056Swilliam */ 28741056Swilliam blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.dk_secsize 28841056Swilliam + du->dk_skip; 28941056Swilliam #ifdef WDDEBUG 29041056Swilliam if (du->dk_skip == 0) { 29141056Swilliam dprintf(DDSK,"\nwdstart %d: %s %d@%d; map ", unit, 29241056Swilliam (bp->b_flags & B_READ) ? "read" : "write", 29341056Swilliam bp->b_bcount, blknum); 29441056Swilliam } else { 29545550Sbill dprintf(DDSK," %d)%x", du->dk_skip, inb(wdc+wd_altsts)); 29641056Swilliam } 29741056Swilliam #endif 29841056Swilliam 29941056Swilliam addr = (int) bp->b_un.b_addr; 30041056Swilliam if(du->dk_skip==0) du->dk_bc = bp->b_bcount; 30141056Swilliam cylin = blknum / secpercyl; 30241056Swilliam head = (blknum % secpercyl) / secpertrk; 30345550Sbill sector = blknum % secpertrk; 30441056Swilliam if (DISKSTATE(du->dk_state) == OPEN) 30541056Swilliam cylin += du->dk_dd.dk_partition[minor_dev].cyloff; 30641056Swilliam 30741056Swilliam /* 30841056Swilliam * See if the current block is in the bad block list. 30941056Swilliam * (If we have one, and not formatting.) 31041056Swilliam */ 31145550Sbill if (DISKSTATE(du->dk_state) == OPEN && wd_sebyse) 31241056Swilliam for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) { 31341056Swilliam if (bt_ptr->bt_cyl > cylin) 31441056Swilliam /* Sorted list, and we passed our cylinder. quit. */ 31541056Swilliam break; 31641056Swilliam if (bt_ptr->bt_cyl == cylin && 31741056Swilliam bt_ptr->bt_trksec == (head << 8) + sector) { 31841056Swilliam /* 31941056Swilliam * Found bad block. Calculate new block addr. 32041056Swilliam * This starts at the end of the disk (skip the 32141056Swilliam * last track which is used for the bad block list), 32241056Swilliam * and works backwards to the front of the disk. 32341056Swilliam */ 32441056Swilliam #ifdef WDDEBUG 32541056Swilliam dprintf(DDSK,"--- badblock code -> Old = %d; ", 32641056Swilliam blknum); 32741056Swilliam #endif 32841056Swilliam blknum = du->dk_dd.dk_secperunit - du->dk_dd.dk_nsectors 32941056Swilliam - (bt_ptr - dkbad[unit].bt_bad) - 1; 33041056Swilliam cylin = blknum / secpercyl; 33141056Swilliam head = (blknum % secpercyl) / secpertrk; 33241056Swilliam sector = blknum % secpertrk; 33341056Swilliam #ifdef WDDEBUG 33441056Swilliam dprintf(DDSK, "new = %d\n", blknum); 33541056Swilliam #endif 33641056Swilliam break; 33741056Swilliam } 33841056Swilliam } 33945550Sbill sector += 1; /* sectors begin with 1, not 0 */ 34041056Swilliam 34141056Swilliam wdtab.b_active = 1; /* mark controller active */ 34241056Swilliam 34345550Sbill if(du->dk_skip==0 || wd_sebyse) { 34445550Sbill if(wdtab.b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += 512; 34545550Sbill while ((inb(wdc+wd_status) & WDCS_BUSY) != 0) ; 34645550Sbill /*while ((inb(wdc+wd_status) & WDCS_DRQ)) inb(wdc+wd_data);*/ 34741056Swilliam outb(wdc+wd_precomp, 0xff); 34841056Swilliam /*wr(wdc+wd_precomp, du->dk_dd.dk_precompcyl / 4);*/ 34941056Swilliam /*if (bp->b_flags & B_FORMAT) { 35041056Swilliam wr(wdc+wd_sector, du->dk_dd.dk_gap3); 35141056Swilliam wr(wdc+wd_seccnt, du->dk_dd.dk_nsectors); 35241056Swilliam } else {*/ 35345550Sbill if(wd_sebyse) 35445550Sbill outb(wdc+wd_seccnt, 1); 35545550Sbill else 35645550Sbill outb(wdc+wd_seccnt, ((du->dk_bc +511) / 512)); 35741056Swilliam outb(wdc+wd_sector, sector); 35841056Swilliam 35941056Swilliam outb(wdc+wd_cyl_lo, cylin); 36041056Swilliam outb(wdc+wd_cyl_hi, cylin >> 8); 36141056Swilliam 36241056Swilliam /* Set up the SDH register (select drive). */ 36341056Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); 36445551Sbill while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; 36541056Swilliam 36641056Swilliam /*if (bp->b_flags & B_FORMAT) 36741056Swilliam wr(wdc+wd_command, WDCC_FORMAT); 36841056Swilliam else*/ 36941056Swilliam outb(wdc+wd_command, 37041056Swilliam (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE); 37141056Swilliam #ifdef WDDEBUG 37245550Sbill dprintf(DDSK,"sector %d cylin %d head %d addr %x sts %x\n", 37345550Sbill sector, cylin, head, addr, inb(wdc+wd_altsts)); 37441056Swilliam #endif 37545550Sbill } 37641056Swilliam 37741056Swilliam /* If this is a read operation, just go away until it's done. */ 37841056Swilliam if (bp->b_flags & B_READ) return; 37941056Swilliam 38041056Swilliam /* Ready to send data? */ 38145551Sbill while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) 38241056Swilliam nulldev(); /* So compiler won't optimize out */ 38341056Swilliam 38441056Swilliam /* ASSUMES CONTIGUOUS MEMORY */ 38545550Sbill outsw (wdc+wd_data, addr+du->dk_skip*512, 256); 38641056Swilliam du->dk_bc -= 512; 38741056Swilliam } 38841056Swilliam 38941056Swilliam /* 39041056Swilliam * these are globally defined so they can be found 39141056Swilliam * by the debugger easily in the case of a system crash 39241056Swilliam */ 39341056Swilliam daddr_t wd_errsector; 39441056Swilliam daddr_t wd_errbn; 39541056Swilliam unsigned char wd_errstat; 39641056Swilliam 39741056Swilliam /* Interrupt routine for the controller. Acknowledge the interrupt, check for 39841056Swilliam * errors on the current operation, mark it done if necessary, and start 39941056Swilliam * the next request. Also check for a partially done transfer, and 40041056Swilliam * continue with the next chunk if so. 40141056Swilliam */ 40241056Swilliam wdintr() 40341056Swilliam { 40441056Swilliam register struct disk *du; 40541056Swilliam register struct buf *bp, *dp; 40641056Swilliam int status; 40741056Swilliam char partch ; 40845550Sbill static shit[32]; 40945550Sbill static wd_haderror; 41041056Swilliam 41141056Swilliam /* Shouldn't need this, but it may be a slow controller. */ 41245550Sbill while ((status = inb(wdc+wd_status)) & WDCS_BUSY) 41341056Swilliam nulldev(); 41441056Swilliam if (!wdtab.b_active) { 41541056Swilliam printf("wd: extra interrupt\n"); 41641056Swilliam return; 41741056Swilliam } 41841056Swilliam 41945549Sbill #ifdef WDDEBUG 42041056Swilliam dprintf(DDSK,"I "); 42141056Swilliam #endif 42241056Swilliam dp = wdtab.b_actf; 42341056Swilliam bp = dp->b_actf; 42441056Swilliam du = &wddrives[WDUNIT(bp->b_dev)]; 42541056Swilliam partch = "abcdefgh"[minor(bp->b_dev)&7] ; 42641056Swilliam if (DISKSTATE(du->dk_state) <= RDLABEL) { 42741056Swilliam if (wdcontrol(bp)) 42841056Swilliam goto done; 42941056Swilliam return; 43041056Swilliam } 43141056Swilliam if (status & (WDCS_ERR | WDCS_ECCCOR)) { 43245550Sbill wd_errstat = inb(wdc+wd_error); /* save error status */ 43341056Swilliam #ifdef WDDEBUG 43445550Sbill printf("status %x error %x\n", status, wd_errstat); 43541056Swilliam #endif 43645550Sbill if(wd_sebyse == 0) { 43745550Sbill wd_haderror = 1; 43845550Sbill goto outt; 43945550Sbill } 44041056Swilliam /*if (bp->b_flags & B_FORMAT) { 44141056Swilliam du->dk_status = status; 44241056Swilliam du->dk_error = wdp->wd_error; 44341056Swilliam bp->b_flags |= B_ERROR; 44441056Swilliam goto done; 44541056Swilliam }*/ 44641056Swilliam 44741056Swilliam wd_errsector = (bp->b_cylin * du->dk_dd.dk_secpercyl) + 44841056Swilliam (((unsigned long) bp->b_blkno * DEV_BSIZE / 44941056Swilliam du->dk_dd.dk_secsize) % du->dk_dd.dk_secpercyl) + 45041056Swilliam du->dk_skip; 45141056Swilliam wd_errbn = bp->b_blkno 45241056Swilliam + du->dk_skip * du->dk_dd.dk_secsize / DEV_BSIZE ; 45341056Swilliam if (status & WDCS_ERR) { 45445550Sbill if (++wdtab.b_errcnt < RETRIES) { 45541056Swilliam wdtab.b_active = 0; 45645550Sbill /*while ((inb(wdc+wd_status) & WDCS_DRQ)) 45745550Sbill insw(wdc+wd_data, &shit, sizeof(shit)/2);*/ 45845550Sbill } else { 45941056Swilliam printf("wd%d%c: ", du->dk_unit, partch); 46041056Swilliam printf( 46141056Swilliam "hard %s error, sn %d bn %d status %b error %b\n", 46241056Swilliam (bp->b_flags & B_READ)? "read":"write", 46341056Swilliam wd_errsector, wd_errbn, status, WDCS_BITS, 46441056Swilliam wd_errstat, WDERR_BITS); 46541056Swilliam bp->b_flags |= B_ERROR; /* flag the error */ 46641056Swilliam } 46741056Swilliam } else 46841056Swilliam log(LOG_WARNING,"wd%d%c: soft ecc sn %d bn %d\n", 46941056Swilliam du->dk_unit, partch, wd_errsector, 47041056Swilliam wd_errbn); 47141056Swilliam } 47245550Sbill outt: 47341056Swilliam 47441056Swilliam /* 47541056Swilliam * If this was a successful read operation, fetch the data. 47641056Swilliam */ 47741056Swilliam if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) { 47841056Swilliam int chk, dummy; 47941056Swilliam 48045550Sbill chk = min(256,du->dk_bc/2); 48141056Swilliam /* Ready to receive data? */ 48241056Swilliam while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) 48341056Swilliam nulldev(); 48441056Swilliam 48541056Swilliam /*dprintf(DDSK,"addr %x\n", (int)bp->b_un.b_addr + du->dk_skip * 512);*/ 48641056Swilliam insw(wdc+wd_data,(int)bp->b_un.b_addr + du->dk_skip * 512 ,chk); 48743592Sdonahn du->dk_bc -= 2*chk; 48845550Sbill while (chk++ < 256) insw (wdc+wd_data,&dummy,1); 48941056Swilliam } 49041056Swilliam 49141056Swilliam wdxfer[du->dk_unit]++; 49241056Swilliam if (wdtab.b_active) { 49341056Swilliam if ((bp->b_flags & B_ERROR) == 0) { 49441056Swilliam du->dk_skip++; /* Add to successful sectors. */ 49541056Swilliam if (wdtab.b_errcnt) { 49641056Swilliam log(LOG_WARNING, "wd%d%c: ", 49741056Swilliam du->dk_unit, partch); 49841056Swilliam log(LOG_WARNING, 49941056Swilliam "soft %s error, sn %d bn %d error %b retries %d\n", 50041056Swilliam (bp->b_flags & B_READ) ? "read" : "write", 50141056Swilliam wd_errsector, wd_errbn, wd_errstat, 50241056Swilliam WDERR_BITS, wdtab.b_errcnt); 50341056Swilliam } 50441056Swilliam wdtab.b_errcnt = 0; 50541056Swilliam 50641056Swilliam /* see if more to transfer */ 50743592Sdonahn /*if (du->dk_skip < (bp->b_bcount + 511) / 512) {*/ 50845550Sbill if (du->dk_bc > 0 && wd_haderror == 0) { 50941056Swilliam wdstart(); 51041056Swilliam return; /* next chunk is started */ 51145550Sbill } else if (wd_haderror && wd_sebyse == 0) { 51245550Sbill du->dk_skip = 0; 51345550Sbill wd_haderror = 0; 51445550Sbill wd_sebyse = 1; 51545550Sbill wdstart(); 51645550Sbill return; /* redo xfer sector by sector */ 51741056Swilliam } 51841056Swilliam } 51941056Swilliam 52041056Swilliam done: 52145550Sbill wd_sebyse = 0; 52241056Swilliam /* done with this transfer, with or without error */ 52341056Swilliam wdtab.b_actf = dp->b_forw; 52441056Swilliam wdtab.b_errcnt = 0; 52541056Swilliam du->dk_skip = 0; 52641056Swilliam dp->b_active = 0; 52741056Swilliam dp->b_actf = bp->av_forw; 52841056Swilliam dp->b_errcnt = 0; 52941056Swilliam bp->b_resid = 0; 53041056Swilliam biodone(bp); 53141056Swilliam } 53241056Swilliam wdtab.b_active = 0; 53341056Swilliam if (dp->b_actf) 53441056Swilliam wdustart(du); /* requeue disk if more io to do */ 53541056Swilliam if (wdtab.b_actf) 53641056Swilliam wdstart(); /* start IO on next drive */ 53741056Swilliam } 53841056Swilliam 53941056Swilliam /* 54041056Swilliam * Initialize a drive. 54141056Swilliam */ 54241056Swilliam wdopen(dev, flags) 54341056Swilliam dev_t dev; 54441056Swilliam int flags; 54541056Swilliam { 54641056Swilliam register unsigned int unit; 54741056Swilliam register struct buf *bp; 54841056Swilliam register struct disk *du; 54941056Swilliam struct dkbad *db; 55041056Swilliam int i, error = 0; 55141056Swilliam 55241056Swilliam unit = WDUNIT(dev); 55341056Swilliam if (unit >= NWD) return (ENXIO) ; 55441056Swilliam du = &wddrives[unit]; 55541056Swilliam if (du->dk_open){ 55641056Swilliam du->dk_open++ ; 55741056Swilliam return(0); /* already is open, don't mess with it */ 55841056Swilliam } 55941056Swilliam #ifdef THE_BUG 56041056Swilliam if (du->dk_state && DISKSTATE(du->dk_state) <= OPEN) 56141056Swilliam return(0); 56241056Swilliam #endif 56341056Swilliam du->dk_unit = unit; 56441056Swilliam wdutab[unit].b_actf = NULL; 56541056Swilliam /*if (flags & O_NDELAY) 56641056Swilliam du->dk_state = WANTOPENRAW; 56741056Swilliam else*/ 56841056Swilliam du->dk_state = WANTOPEN; 56941056Swilliam /* 57041056Swilliam * Use the default sizes until we've read the label, 57141056Swilliam * or longer if there isn't one there. 57241056Swilliam */ 57341056Swilliam du->dk_dd = dflt_sizes; 57441056Swilliam 57541056Swilliam /* 57641056Swilliam * Recal, read of disk label will be done in wdcontrol 57741056Swilliam * during first read operation. 57841056Swilliam */ 57941056Swilliam bp = geteblk(512); 58045549Sbill bp->b_dev = dev & 0xff00; 58141056Swilliam bp->b_blkno = bp->b_bcount = 0; 58241056Swilliam bp->b_flags = B_READ; 58341056Swilliam wdstrategy(bp); 58441056Swilliam biowait(bp); 58541056Swilliam if (bp->b_flags & B_ERROR) { 58641056Swilliam u.u_error = 0; /* XXX */ 58741056Swilliam error = ENXIO; 58841056Swilliam du->dk_state = CLOSED; 58941056Swilliam goto done; 59041056Swilliam } 59141056Swilliam if (du->dk_state == OPENRAW) { 59241056Swilliam du->dk_state = OPENRAW; 59341056Swilliam goto done; 59441056Swilliam } 59541056Swilliam /* 59641056Swilliam * Read bad sector table into memory. 59741056Swilliam */ 59841056Swilliam i = 0; 59941056Swilliam do { 60041056Swilliam u.u_error = 0; /* XXX */ 60141056Swilliam bp->b_flags = B_BUSY | B_READ; 60241056Swilliam bp->b_blkno = du->dk_dd.dk_secperunit - du->dk_dd.dk_nsectors 60341056Swilliam + i; 60441056Swilliam if (du->dk_dd.dk_secsize > DEV_BSIZE) 60541056Swilliam bp->b_blkno *= du->dk_dd.dk_secsize / DEV_BSIZE; 60641056Swilliam else 60741056Swilliam bp->b_blkno /= DEV_BSIZE / du->dk_dd.dk_secsize; 60841056Swilliam bp->b_bcount = du->dk_dd.dk_secsize; 60941056Swilliam bp->b_cylin = du->dk_dd.dk_ncylinders - 1; 61041056Swilliam wdstrategy(bp); 61141056Swilliam biowait(bp); 61241056Swilliam } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 && 61341056Swilliam i < du->dk_dd.dk_nsectors); 61441056Swilliam db = (struct dkbad *)(bp->b_un.b_addr); 61545550Sbill #define DKBAD_MAGIC 0x4321 61641056Swilliam if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 && 61741056Swilliam db->bt_flag == DKBAD_MAGIC) { 61841056Swilliam dkbad[unit] = *db; 61941056Swilliam du->dk_state = OPEN; 62041056Swilliam } else { 62141056Swilliam printf("wd%d: %s bad-sector file\n", unit, 62241056Swilliam (bp->b_flags & B_ERROR) ? "can't read" : "format error in"); 62341056Swilliam u.u_error = 0; /* XXX */ 62441056Swilliam /*error = ENXIO ;*/ 62541056Swilliam du->dk_state = OPENRAW; 62641056Swilliam } 62741056Swilliam done: 62841056Swilliam bp->b_flags = B_INVAL | B_AGE; 62941056Swilliam brelse(bp); 63041056Swilliam if (error == 0) 63141056Swilliam du->dk_open = 1; 63241056Swilliam return (error); 63341056Swilliam } 63441056Swilliam 63541056Swilliam /* 63641056Swilliam * Implement operations other than read/write. 63741056Swilliam * Called from wdstart or wdintr during opens and formats. 63841056Swilliam * Uses finite-state-machine to track progress of operation in progress. 63941056Swilliam * Returns 0 if operation still in progress, 1 if completed. 64041056Swilliam */ 64141056Swilliam wdcontrol(bp) 64241056Swilliam register struct buf *bp; 64341056Swilliam { 64441056Swilliam register struct disk *du; 64541056Swilliam register unit; 64641056Swilliam unsigned char stat; 64741056Swilliam int s, cnt; 64841056Swilliam extern int bootdev, cyloffset; 64941056Swilliam 65041056Swilliam du = &wddrives[WDUNIT(bp->b_dev)]; 65141056Swilliam unit = du->dk_unit; 65241056Swilliam switch (DISKSTATE(du->dk_state)) { 65341056Swilliam 65441056Swilliam tryagainrecal: 65541056Swilliam case WANTOPEN: /* set SDH, step rate, do restore */ 65641056Swilliam #ifdef WDDEBUG 65741056Swilliam dprintf(DDSK,"wd%d: recal ", unit); 65841056Swilliam #endif 65941056Swilliam s = splbio(); /* not called from intr level ... */ 660*45552Sbill 661*45552Sbill #ifdef notdef 662*45552Sbill /* some compaq controllers require this ... */ 663*45552Sbill outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) 664*45552Sbill + du->dk_dd.dk_ntracks-1); 665*45552Sbill outb(wdc+wd_seccnt, du->dk_dd.dk_nsectors); 666*45552Sbill outb(wdc+wd_command, 0x91); 667*45552Sbill while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) nulldev(); 668*45552Sbill #endif 669*45552Sbill 67041056Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 67141056Swilliam wdtab.b_active = 1; 67241056Swilliam outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); 67341056Swilliam du->dk_state++; 67441056Swilliam splx(s); 67541056Swilliam return(0); 67641056Swilliam 67741056Swilliam case RECAL: 67845551Sbill if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { 67941056Swilliam printf("wd%d: recal", du->dk_unit); 68041056Swilliam if (unit == 0) { 68141056Swilliam printf(": status %b error %b\n", 68241056Swilliam stat, WDCS_BITS, 68341056Swilliam inb(wdc+wd_error), WDERR_BITS); 68441056Swilliam if (++wdtab.b_errcnt < RETRIES) 68541056Swilliam goto tryagainrecal; 68641056Swilliam } 68741056Swilliam goto badopen; 68841056Swilliam } 68941056Swilliam wdtab.b_errcnt = 0; 69041056Swilliam if (ISRAWSTATE(du->dk_state)) { 69141056Swilliam du->dk_state = OPENRAW; 69241056Swilliam return(1); 69341056Swilliam } 69441056Swilliam retry: 69541056Swilliam #ifdef WDDEBUG 69641056Swilliam dprintf(DDSK,"rdlabel "); 69741056Swilliam #endif 698*45552Sbill if( cyloffset < 0 || cyloffset > 2048) cyloffset=0; 69941056Swilliam /* 70041056Swilliam * Read in sector 0 to get the pack label and geometry. 70141056Swilliam */ 70241056Swilliam outb(wdc+wd_precomp, 0xff);/* sometimes this is head bit 3 */ 70341056Swilliam outb(wdc+wd_seccnt, 1); 70441056Swilliam outb(wdc+wd_sector, 1); 70541056Swilliam /*if (bp->b_dev == bootdev) { 70641056Swilliam (wdc+wd_cyl_lo = cyloffset & 0xff; 70741056Swilliam (wdc+wd_cyl_hi = cyloffset >> 8; 70841056Swilliam } else { 70941056Swilliam (wdc+wd_cyl_lo = 0; 71041056Swilliam (wdc+wd_cyl_hi = 0; 71141056Swilliam }*/ 71241056Swilliam outb(wdc+wd_cyl_lo, (cyloffset & 0xff)); 71341056Swilliam outb(wdc+wd_cyl_hi, (cyloffset >> 8)); 71441056Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 71541056Swilliam outb(wdc+wd_command, WDCC_READ); 71641056Swilliam du->dk_state = RDLABEL; 71741056Swilliam return(0); 71841056Swilliam 71941056Swilliam case RDLABEL: 72041056Swilliam if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { 72141056Swilliam if (++wdtab.b_errcnt < RETRIES) 72241056Swilliam goto retry; 72341056Swilliam printf("wd%d: read label", unit); 72441056Swilliam goto badopen; 72541056Swilliam } 72641056Swilliam 72741056Swilliam insw(wdc+wd_data, bp->b_un.b_addr, 256); 72841056Swilliam 72941056Swilliam if (((struct disklabel *) 73041056Swilliam (bp->b_un.b_addr + LABELOFFSET))->dk_magic == DISKMAGIC) { 73141056Swilliam du->dk_dd = 73241056Swilliam * (struct disklabel *) (bp->b_un.b_addr + LABELOFFSET); 73341056Swilliam } else { 73441056Swilliam printf("wd%d: bad disk label\n", du->dk_unit); 73541056Swilliam du->dk_state = OPENRAW; 73641056Swilliam } 737*45552Sbill 738*45552Sbill s = splbio(); /* not called from intr level ... */ 739*45552Sbill while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) nulldev(); 740*45552Sbill outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) 741*45552Sbill + du->dk_dd.dk_ntracks-1); 742*45552Sbill outb(wdc+wd_cyl_lo, du->dk_dd.dk_ncylinders); 743*45552Sbill outb(wdc+wd_cyl_hi, du->dk_dd.dk_ncylinders>>8); 744*45552Sbill outb(wdc+wd_seccnt, du->dk_dd.dk_nsectors); 745*45552Sbill outb(wdc+wd_command, 0x91); 746*45552Sbill while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) nulldev(); 747*45552Sbill outb(wdc+wd_seccnt, 0); 748*45552Sbill splx(s); 749*45552Sbill 75041056Swilliam if (du->dk_state == RDLABEL) 75141056Swilliam du->dk_state = RDBADTBL; 75241056Swilliam /* 75341056Swilliam * The rest of the initialization can be done 75441056Swilliam * by normal means. 75541056Swilliam */ 75641056Swilliam return(1); 75741056Swilliam 75841056Swilliam default: 75941056Swilliam panic("wdcontrol %x", du->dk_state ); 76041056Swilliam } 76141056Swilliam /* NOTREACHED */ 76241056Swilliam 76341056Swilliam badopen: 76441056Swilliam printf(": status %b error %b\n", 76541056Swilliam stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS); 76641056Swilliam du->dk_state = OPENRAW; 76741056Swilliam return(1); 76841056Swilliam } 76941056Swilliam 77041056Swilliam wdclose(dev) 77141056Swilliam dev_t dev; 77241056Swilliam { struct disk *du; 77341056Swilliam 77441056Swilliam du = &wddrives[WDUNIT(dev)]; 77541056Swilliam du->dk_open-- ; 77641056Swilliam /*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */ 77741056Swilliam } 77841056Swilliam 77941056Swilliam wdioctl(dev,cmd,addr,flag) 78041056Swilliam dev_t dev; 78141056Swilliam caddr_t addr; 78241056Swilliam { 78341056Swilliam int unit = WDUNIT(dev); 78441056Swilliam register struct disk *du; 78541056Swilliam int error = 0; 78641056Swilliam struct uio auio; 78741056Swilliam struct iovec aiov; 78841056Swilliam /*int wdformat();*/ 78941056Swilliam 79041056Swilliam du = &wddrives[unit]; 79141056Swilliam 79241056Swilliam switch (cmd) { 79341056Swilliam 79441056Swilliam case DIOCGDINFO: 79541056Swilliam *(struct disklabel *)addr = du->dk_dd; 79641056Swilliam break; 79741056Swilliam 79841056Swilliam case DIOCGDINFOP: 79941056Swilliam *(struct disklabel **)addr = &(du->dk_dd); 80041056Swilliam break; 80141056Swilliam 80241056Swilliam #ifdef notyet 80341056Swilliam case DIOCWFORMAT: 80441056Swilliam if ((flag & FWRITE) == 0) 80541056Swilliam error = EBADF; 80641056Swilliam else { 80741056Swilliam register struct format_op *fop; 80841056Swilliam 80941056Swilliam fop = (struct format_op *)addr; 81041056Swilliam aiov.iov_base = fop->df_buf; 81141056Swilliam aiov.iov_len = fop->df_count; 81241056Swilliam auio.uio_iov = &aiov; 81341056Swilliam auio.uio_iovcnt = 1; 81441056Swilliam auio.uio_resid = fop->df_count; 81541056Swilliam auio.uio_segflg = 0; 81641056Swilliam auio.uio_offset = 81741056Swilliam fop->df_startblk * du->dk_dd.dk_secsize; 81841056Swilliam error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE, 81941056Swilliam minphys, &auio); 82041056Swilliam fop->df_count -= auio.uio_resid; 82141056Swilliam fop->df_reg[0] = du->dk_status; 82241056Swilliam fop->df_reg[1] = du->dk_error; 82341056Swilliam } 82441056Swilliam break; 82541056Swilliam #endif 82641056Swilliam 82741056Swilliam default: 82841056Swilliam error = ENOTTY; 82941056Swilliam break; 83041056Swilliam } 83141056Swilliam return (error); 83241056Swilliam } 83341056Swilliam 83441056Swilliam /*wdformat(bp) 83541056Swilliam struct buf *bp; 83641056Swilliam { 83741056Swilliam 83841056Swilliam bp->b_flags |= B_FORMAT; 83941056Swilliam return (wdstrategy(bp)); 84041056Swilliam }*/ 84141056Swilliam 84241056Swilliam /* 84341056Swilliam * Routines to do raw IO for a unit. 84441056Swilliam */ 84541056Swilliam wdread(dev, uio) /* character read routine */ 84641056Swilliam dev_t dev; 84741056Swilliam struct uio *uio; 84841056Swilliam { 84941056Swilliam int unit = WDUNIT(dev) ; 85041056Swilliam 85141056Swilliam if (unit >= NWD) return(ENXIO); 85241056Swilliam return(physio(wdstrategy, &rwdbuf[unit], dev, B_READ, minphys, uio)); 85341056Swilliam } 85441056Swilliam 85541056Swilliam 85641056Swilliam wdwrite(dev, uio) /* character write routine */ 85741056Swilliam dev_t dev; 85841056Swilliam struct uio *uio; 85941056Swilliam { 86041056Swilliam int unit = WDUNIT(dev) ; 86141056Swilliam 86241056Swilliam if (unit >= NWD) return(ENXIO); 86341056Swilliam return(physio(wdstrategy, &rwdbuf[unit], dev, B_WRITE, minphys, uio)); 86441056Swilliam } 86541056Swilliam 86641056Swilliam wdsize(dev) 86741056Swilliam dev_t dev; 86841056Swilliam { 86941056Swilliam register unit = WDUNIT(dev) ; 87041056Swilliam register xunit = minor(dev) & 07; 87141056Swilliam register struct disk *du; 87241056Swilliam register val ; 87341056Swilliam 87445550Sbill return(21600); 87541056Swilliam #ifdef notdef 87641056Swilliam if (unit >= NWD) return(-1); 87741056Swilliam if (wddrives[unit].dk_state == 0) /*{ 87841056Swilliam val = wdopen (dev, 0) ; 87941056Swilliam if (val < 0) return (val) ; 88041056Swilliam }*/ return (-1) ; 88141056Swilliam du = &wddrives[unit]; 88241056Swilliam return((int)((u_long)du->dk_dd.dk_partition[xunit].nblocks * 88341056Swilliam du->dk_dd.dk_secsize / 512)); 88441056Swilliam #endif 88541056Swilliam } 88641056Swilliam 88741056Swilliam wddump(dev) /* dump core after a system crash */ 88841056Swilliam dev_t dev; 88941056Swilliam { 89041056Swilliam #ifdef notyet 89141056Swilliam register struct disk *du; /* disk unit to do the IO */ 89241056Swilliam register struct wd1010 *wdp = (struct wd1010 *) VA_WD; 89341056Swilliam register struct bt_bad *bt_ptr; 89441056Swilliam long num; /* number of sectors to write */ 89541056Swilliam int unit, xunit; 89641056Swilliam long cyloff, blknum, blkcnt; 89741056Swilliam long cylin, head, sector; 89841056Swilliam long secpertrk, secpercyl, nblocks, i; 89941056Swilliam register char *addr; 90041056Swilliam char *end; 90141056Swilliam extern int dumplo, totalclusters; 90241056Swilliam static wddoingadump = 0 ; 90341056Swilliam 90441056Swilliam addr = (char *) PA_RAM; /* starting address */ 90541056Swilliam /* size of memory to dump */ 90641056Swilliam num = totalclusters * CLSIZE - PA_RAM / PGSIZE; 90741056Swilliam unit = WDUNIT(dev) ; /* eventually support floppies? */ 90841056Swilliam xunit = minor(dev) & 7; /* file system */ 90941056Swilliam /* check for acceptable drive number */ 91041056Swilliam if (unit >= NWD) return(ENXIO); 91141056Swilliam 91241056Swilliam du = &wddrives[unit]; 91341056Swilliam /* was it ever initialized ? */ 91441056Swilliam if (du->dk_state < OPEN) return (ENXIO) ; 91541056Swilliam 91641056Swilliam /* Convert to disk sectors */ 91741056Swilliam num = (u_long) num * PGSIZE / du->dk_dd.dk_secsize; 91841056Swilliam 91941056Swilliam /* check if controller active */ 92041056Swilliam /*if (wdtab.b_active) return(EFAULT); */ 92141056Swilliam if (wddoingadump) return(EFAULT); 92241056Swilliam 92341056Swilliam secpertrk = du->dk_dd.dk_nsectors; 92441056Swilliam secpercyl = du->dk_dd.dk_secpercyl; 92541056Swilliam nblocks = du->dk_dd.dk_partition[xunit].nblocks; 92641056Swilliam cyloff = du->dk_dd.dk_partition[xunit].cyloff; 92741056Swilliam 92841056Swilliam /* check transfer bounds against partition size */ 92941056Swilliam if ((dumplo < 0) || ((dumplo + num) >= nblocks)) 93041056Swilliam return(EINVAL); 93141056Swilliam 93241056Swilliam /*wdtab.b_active = 1; /* mark controller active for if we 93341056Swilliam panic during the dump */ 93441056Swilliam wddoingadump = 1 ; i = 100000 ; 93541056Swilliam while ((wdp->wd_status & WDCS_BUSY) && (i-- > 0)) nulldev() ; 93641056Swilliam inb(wdc+wd_sdh = du->dk_sdh ; 93741056Swilliam inb(wdc+wd_command = WDCC_RESTORE | WD_STEP; 93841056Swilliam while (inb(wdc+wd_status & WDCS_BUSY) nulldev() ; 93941056Swilliam 94041056Swilliam blknum = dumplo; 94141056Swilliam while (num > 0) { 94241056Swilliam #ifdef notdef 94341056Swilliam if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER; 94441056Swilliam if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl) 94541056Swilliam blkcnt = secpercyl - (blknum % secpercyl); 94641056Swilliam /* keep transfer within current cylinder */ 94741056Swilliam #endif 94841056Swilliam 94941056Swilliam /* compute disk address */ 95041056Swilliam cylin = blknum / secpercyl; 95141056Swilliam head = (blknum % secpercyl) / secpertrk; 95241056Swilliam sector = blknum % secpertrk; 95341056Swilliam sector++; /* origin 1 */ 95443592Sdonahn cylin += cyloff; 95541056Swilliam 95641056Swilliam /* 95741056Swilliam * See if the current block is in the bad block list. 95841056Swilliam * (If we have one.) 95941056Swilliam */ 96041056Swilliam for (bt_ptr = dkbad[unit].bt_bad; 96141056Swilliam bt_ptr->bt_cyl != -1; bt_ptr++) { 96241056Swilliam if (bt_ptr->bt_cyl > cylin) 96341056Swilliam /* Sorted list, and we passed our cylinder. 96441056Swilliam quit. */ 96541056Swilliam break; 96641056Swilliam if (bt_ptr->bt_cyl == cylin && 96741056Swilliam bt_ptr->bt_trksec == (head << 8) + sector) { 96841056Swilliam /* 96941056Swilliam * Found bad block. Calculate new block addr. 97041056Swilliam * This starts at the end of the disk (skip the 97141056Swilliam * last track which is used for the bad block list), 97241056Swilliam * and works backwards to the front of the disk. 97341056Swilliam */ 97441056Swilliam blknum = (du->dk_dd.dk_secperunit) 97541056Swilliam - du->dk_dd.dk_nsectors 97641056Swilliam - (bt_ptr - dkbad[unit].bt_bad) - 1; 97741056Swilliam cylin = blknum / secpercyl; 97841056Swilliam head = (blknum % secpercyl) / secpertrk; 97941056Swilliam sector = blknum % secpertrk; 98041056Swilliam break; 98141056Swilliam } 98241056Swilliam 98341056Swilliam /* select drive. */ 98441056Swilliam inb(wdc+wd_sdh = du->dk_sdh | (head&07); 98541056Swilliam while ((inb(wdc+wd_status & WDCS_READY) == 0) nulldev(); 98641056Swilliam 98741056Swilliam /* transfer some blocks */ 98841056Swilliam inb(wdc+wd_sector = sector; 98941056Swilliam inb(wdc+wd_seccnt = 1; 99041056Swilliam inb(wdc+wd_cyl_lo = cylin; 99141056Swilliam if (du->dk_dd.dk_ntracks > 8) { 99241056Swilliam if (head > 7) 99341056Swilliam inb(wdc+wd_precomp = 0; /* set 3rd head bit */ 99441056Swilliam else 99541056Swilliam inb(wdc+wd_precomp = 0xff; /* set 3rd head bit */ 99641056Swilliam } 99741056Swilliam inb(wdc+wd_cyl_hi = cylin >> 8; 99841056Swilliam #ifdef notdef 99941056Swilliam /* lets just talk about this first...*/ 100041056Swilliam printf ("sdh 0%o sector %d cyl %d addr 0x%x\n", 100141056Swilliam wdp->wd_sdh, wdp->wd_sector, 100241056Swilliam wdp->wd_cyl_hi*256+wdp->wd_cyl_lo, addr) ; 100341056Swilliam for (i=10000; i > 0 ; i--) 100441056Swilliam ; 100541056Swilliam continue; 100641056Swilliam #endif 100741056Swilliam inb(wdc+wd_command = WDCC_WRITE; 100841056Swilliam 100941056Swilliam /* Ready to send data? */ 101041056Swilliam while ((inb(wdc+wd_status & WDCS_DRQ) == 0) nulldev(); 101141056Swilliam if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ; 101241056Swilliam 101341056Swilliam end = (char *)addr + du->dk_dd.dk_secsize; 101441056Swilliam for (; addr < end; addr += 8) { 101541056Swilliam wdp->wd_data = addr[0]; 101641056Swilliam wdp->wd_data = addr[1]; 101741056Swilliam wdp->wd_data = addr[2]; 101841056Swilliam wdp->wd_data = addr[3]; 101941056Swilliam wdp->wd_data = addr[4]; 102041056Swilliam wdp->wd_data = addr[5]; 102141056Swilliam wdp->wd_data = addr[6]; 102241056Swilliam wdp->wd_data = addr[7]; 102341056Swilliam } 102441056Swilliam if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ; 102541056Swilliam /* Check data request (should be done). */ 102641056Swilliam if (inb(wdc+wd_status & WDCS_DRQ) return(EIO) ; 102741056Swilliam 102841056Swilliam /* wait for completion */ 102941056Swilliam for ( i = 1000000 ; inb(wdc+wd_status & WDCS_BUSY ; i--) { 103041056Swilliam if (i < 0) return (EIO) ; 103141056Swilliam nulldev () ; 103241056Swilliam } 103341056Swilliam /* error check the xfer */ 103441056Swilliam if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ; 103541056Swilliam /* update block count */ 103641056Swilliam num--; 103741056Swilliam blknum++ ; 103841056Swilliam #ifdef WDDEBUG 103941056Swilliam if (num % 100 == 0) printf(".") ; 104041056Swilliam #endif 104141056Swilliam } 104241056Swilliam return(0); 104341056Swilliam #endif 104441056Swilliam } 104541056Swilliam #endif 1046