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 * 849617Sbostic * %sccs.include.redist.c% 941056Swilliam * 10*56513Sbostic * @(#)wd.c 7.4 (Berkeley) 10/11/92 1141056Swilliam */ 1243592Sdonahn 1349617Sbostic /* TODO:peel out buffer at low ipl, 1449617Sbostic speed improvement, rewrite to clean code from garbage artifacts */ 1549617Sbostic 1649617Sbostic 1741056Swilliam #include "wd.h" 1841056Swilliam #if NWD > 0 1941056Swilliam 20*56513Sbostic #include <sys/param.h> 21*56513Sbostic #include <sys/dkbad.h> 22*56513Sbostic #include <sys/systm.h> 23*56513Sbostic #include <sys/conf.h> 24*56513Sbostic #include <sys/file.h> 25*56513Sbostic #include <sys/stat.h> 26*56513Sbostic #include <sys/ioctl.h> 27*56513Sbostic #include <sys/disklabel.h> 28*56513Sbostic #include <sys/buf.h> 29*56513Sbostic #include <sys/uio.h> 30*56513Sbostic #include <sys/syslog.h> 3141056Swilliam 32*56513Sbostic #include <i386/isa/isa_device.h> 33*56513Sbostic #include <i386/isa/icu.h> 34*56513Sbostic #include <i386/isa/wdreg.h> 35*56513Sbostic #include <vm/vm.h> 36*56513Sbostic 3741056Swilliam #define RETRIES 5 /* number of retries before giving up */ 3849568Swilliam #define MAXTRANSFER 32 /* max size of transfer in page clusters */ 3941056Swilliam 4049568Swilliam #define wdctlr(dev) ((minor(dev) & 0x80) >> 7) 4149568Swilliam #define wdunit(dev) ((minor(dev) & 0x60) >> 5) 4249568Swilliam #define wdpart(dev) ((minor(dev) & 0x1f)) 4341056Swilliam 4441056Swilliam #define b_cylin b_resid /* cylinder number for doing IO to */ 4541056Swilliam /* shares an entry in the buf struct */ 4641056Swilliam 4741056Swilliam /* 4841056Swilliam * Drive states. Used for open and format operations. 4941056Swilliam * States < OPEN (> 0) are transient, during an open operation. 5041056Swilliam * OPENRAW is used for unlabeled disks, and for floppies, to inhibit 5141056Swilliam * bad-sector forwarding. 5241056Swilliam */ 5341056Swilliam #define RAWDISK 8 /* raw disk operation, no translation*/ 5441056Swilliam #define ISRAWSTATE(s) (RAWDISK&(s)) /* are we in a raw state? */ 5541056Swilliam #define DISKSTATE(s) (~RAWDISK&(s)) /* are we in a given state regardless 5641056Swilliam of raw or cooked mode? */ 5741056Swilliam 5841056Swilliam #define CLOSED 0 /* disk is closed. */ 5941056Swilliam /* "cooked" disk states */ 6041056Swilliam #define WANTOPEN 1 /* open requested, not started */ 6141056Swilliam #define RECAL 2 /* doing restore */ 6241056Swilliam #define RDLABEL 3 /* reading pack label */ 6341056Swilliam #define RDBADTBL 4 /* reading bad-sector table */ 6441056Swilliam #define OPEN 5 /* done with open */ 6541056Swilliam 6641056Swilliam #define WANTOPENRAW (WANTOPEN|RAWDISK) /* raw WANTOPEN */ 6741056Swilliam #define RECALRAW (RECAL|RAWDISK) /* raw open, doing restore */ 6841056Swilliam #define OPENRAW (OPEN|RAWDISK) /* open, but unlabeled disk or floppy */ 6941056Swilliam 7041056Swilliam 7141056Swilliam /* 7241056Swilliam * The structure of a disk drive. 7341056Swilliam */ 7441056Swilliam struct disk { 7549568Swilliam struct disklabel dk_dd; /* device configuration data */ 7649568Swilliam long dk_bc; /* byte count left */ 7749568Swilliam short dk_skip; /* blocks already transferred */ 7849568Swilliam char dk_unit; /* physical unit number */ 7949568Swilliam char dk_state; /* control state */ 8049568Swilliam u_char dk_status; /* copy of status reg. */ 8149568Swilliam u_char dk_error; /* copy of error reg. */ 8249568Swilliam short dk_open; /* open/closed refcnt */ 8349568Swilliam u_long dk_copenpart; /* character units open on this drive */ 8449568Swilliam u_long dk_bopenpart; /* block units open on this drive */ 8549568Swilliam u_long dk_openpart; /* all units open on this drive */ 8649568Swilliam short dk_wlabel; /* label writable? */ 8741056Swilliam }; 8841056Swilliam 8941056Swilliam /* 9041056Swilliam * This label is used as a default when initializing a new or raw disk. 9141056Swilliam * It really only lets us access the first track until we know more. 9241056Swilliam */ 9341056Swilliam struct disklabel dflt_sizes = { 9449568Swilliam DISKMAGIC, DTYPE_ST506, 0, "default", "", 9541056Swilliam 512, /* sector size */ 9649568Swilliam 17, /* # of sectors per track */ 9749568Swilliam 8, /* # of tracks per cylinder */ 9849568Swilliam 766, /* # of cylinders per unit */ 9949568Swilliam 17*8, /* # of sectors per cylinder */ 10049568Swilliam 766*8*17, /* # of sectors per unit */ 10149568Swilliam 0, /* # of spare sectors per track */ 10249568Swilliam 0, /* # of spare sectors per cylinder */ 10349568Swilliam 0, /* # of alt. cylinders per unit */ 10449568Swilliam 3600, /* rotational speed */ 10549568Swilliam 1, /* hardware sector interleave */ 10649568Swilliam 0, /* sector 0 skew, per track */ 10749568Swilliam 0, /* sector 0 skew, per cylinder */ 10849568Swilliam 0, /* head switch time, usec */ 10949568Swilliam 0, /* track-to-track seek, usec */ 11049568Swilliam 0, /* generic flags */ 11149568Swilliam 0,0,0,0,0, 11249568Swilliam 0,0,0,0,0, 11349568Swilliam DISKMAGIC, 11449568Swilliam 0, 11549568Swilliam 8, 11649568Swilliam 8192, 11749568Swilliam 8192, 11849568Swilliam 11949568Swilliam {{21600, 0, 0,0,0,0}, /* A=root filesystem */ 12049568Swilliam {21600, 40, 0,0,0,0}, 12149568Swilliam {660890, 0, 0,0,0,0}, /* C=whole disk */ 12249568Swilliam {216000, 80, 0,0,0,0}, 12349568Swilliam {0, 0, 0,0,0,0}, 12449568Swilliam {0, 0, 0,0,0,0}, 12549568Swilliam {0, 0, 0,0,0,0}, 12649568Swilliam {399600, 480, 0,0,0,0}} 12741056Swilliam }; 12845550Sbill 12941056Swilliam static struct dkbad dkbad[NWD]; 13041056Swilliam struct disk wddrives[NWD] = {0}; /* table of units */ 13141056Swilliam struct buf wdtab = {0}; 13241056Swilliam struct buf wdutab[NWD] = {0}; /* head of queue per drive */ 13341056Swilliam struct buf rwdbuf[NWD] = {0}; /* buffers for raw IO */ 13441056Swilliam long wdxfer[NWD] = {0}; /* count of transfers */ 13541056Swilliam int writeprotected[NWD] = { 0 }; 13641056Swilliam int wdprobe(), wdattach(), wdintr(); 13745552Sbill struct isa_driver wddriver = { 13841056Swilliam wdprobe, wdattach, "wd", 13941056Swilliam }; 14041056Swilliam 14145552Sbill static wdc; 14241056Swilliam /* 14341056Swilliam * Probe routine 14441056Swilliam */ 14541056Swilliam wdprobe(dvp) 14645552Sbill struct isa_device *dvp; 14741056Swilliam { 14845552Sbill wdc = dvp->id_iobase; 14941056Swilliam 15041056Swilliam #ifdef lint 15141056Swilliam wdintr(0); 15241056Swilliam #endif 15349568Swilliam /* XXX sorry, needs to be better */ 15441056Swilliam outb(wdc+wd_error, 0x5a) ; /* error register not writable */ 15541056Swilliam outb(wdc+wd_cyl_lo, 0xa5) ; /* but all of cyllo are implemented */ 15649568Swilliam if(inb(wdc+wd_error) != 0x5a && inb(wdc+wd_cyl_lo) == 0xa5) 15741056Swilliam return(1) ; 15841056Swilliam return (0); 15941056Swilliam } 16041056Swilliam 16141056Swilliam /* 16241056Swilliam * attach each drive if possible. 16341056Swilliam */ 16441056Swilliam wdattach(dvp) 16545552Sbill struct isa_device *dvp; 16641056Swilliam { 16745552Sbill int unit = dvp->id_unit; 16841056Swilliam 16945552Sbill outb(wdc+wd_ctlr,12); 17045552Sbill DELAY(1000); 17145552Sbill outb(wdc+wd_ctlr,8); 17241056Swilliam } 17341056Swilliam 17441056Swilliam /* Read/write routine for a buffer. Finds the proper unit, range checks 17541056Swilliam * arguments, and schedules the transfer. Does not wait for the transfer 17641056Swilliam * to complete. Multi-page transfers are supported. All I/O requests must 17741056Swilliam * be a multiple of a sector in length. 17841056Swilliam */ 17941056Swilliam wdstrategy(bp) 18041056Swilliam register struct buf *bp; /* IO operation to perform */ 18141056Swilliam { 18241056Swilliam register struct buf *dp; 18341056Swilliam register struct disk *du; /* Disk unit to do the IO. */ 18449568Swilliam register struct partition *p; 18549568Swilliam long maxsz, sz; 18649568Swilliam int unit = wdunit(bp->b_dev); 18741056Swilliam int s; 18841056Swilliam 18941056Swilliam if ((unit >= NWD) || (bp->b_blkno < 0)) { 19043592Sdonahn printf("wdstrat: unit = %d, blkno = %d, bcount = %d\n", 19141056Swilliam unit, bp->b_blkno, bp->b_bcount); 19243592Sdonahn pg("wd:error in wdstrategy"); 19341056Swilliam bp->b_flags |= B_ERROR; 19441056Swilliam goto bad; 19541056Swilliam } 19641056Swilliam if (writeprotected[unit] && (bp->b_flags & B_READ) == 0) { 19741056Swilliam printf("wd%d: write protected\n", unit); 19841056Swilliam goto bad; 19941056Swilliam } 20041056Swilliam du = &wddrives[unit]; 20141056Swilliam if (DISKSTATE(du->dk_state) != OPEN) 20241056Swilliam goto q; 20349568Swilliam #ifdef old 20441056Swilliam /* 20541056Swilliam * Convert DEV_BSIZE "blocks" to sectors. 20641056Swilliam * Note: doing the conversions this way limits the partition size 20741056Swilliam * to about 8 million sectors (1-8 Gb). 20841056Swilliam */ 20949568Swilliam blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize; 21049568Swilliam if (((u_long) bp->b_blkno * DEV_BSIZE % du->dk_dd.d_secsize != 0) || 21143592Sdonahn bp->b_bcount >= MAXTRANSFER * CLBYTES) { 21241056Swilliam bp->b_flags |= B_ERROR; 21341056Swilliam goto bad; 21441056Swilliam } 21549568Swilliam nblocks = du->dk_dd.d_partitions[part].p_size; 21649568Swilliam cyloff = du->dk_dd.d_partitions[part].p_offset; 21749568Swilliam if (blknum + (bp->b_bcount / du->dk_dd.d_secsize) > nblocks) { 21841056Swilliam if (blknum == nblocks) 21941056Swilliam bp->b_resid = bp->b_bcount; 22041056Swilliam else 22141056Swilliam bp->b_flags |= B_ERROR; 22241056Swilliam goto bad; 22341056Swilliam } 22449568Swilliam bp->b_cylin = blknum / du->dk_dd.d_secpercyl + cyloff; 22549568Swilliam #else 22649568Swilliam /* 22749568Swilliam * Determine the size of the transfer, and make sure it is 22849568Swilliam * within the boundaries of the partition. 22949568Swilliam */ 23049568Swilliam p = &du->dk_dd.d_partitions[wdpart(bp->b_dev)]; 23149568Swilliam maxsz = p->p_size; 23249568Swilliam sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; 23349568Swilliam if (bp->b_blkno + p->p_offset <= LABELSECTOR && 23449568Swilliam #if LABELSECTOR != 0 23549568Swilliam bp->b_blkno + p->p_offset + sz > LABELSECTOR && 23649568Swilliam #endif 23749568Swilliam (bp->b_flags & B_READ) == 0 && du->dk_wlabel == 0) { 23849568Swilliam bp->b_error = EROFS; 23949568Swilliam goto bad; 24049568Swilliam } 24149568Swilliam if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { 24249568Swilliam /* if exactly at end of disk, return an EOF */ 24349568Swilliam if (bp->b_blkno == maxsz) { 24449568Swilliam bp->b_resid = bp->b_bcount; 24549568Swilliam biodone(bp); 24649568Swilliam return; 24749568Swilliam } 24849568Swilliam /* or truncate if part of it fits */ 24949568Swilliam sz = maxsz - bp->b_blkno; 25049568Swilliam if (sz <= 0) 25149568Swilliam goto bad; 25249568Swilliam bp->b_bcount = sz << DEV_BSHIFT; 25349568Swilliam } 25449568Swilliam bp->b_cylin = (bp->b_blkno + p->p_offset) / du->dk_dd.d_secpercyl; 25549568Swilliam #endif 25641056Swilliam q: 25741056Swilliam dp = &wdutab[unit]; 25845550Sbill s = splhigh(); 25941056Swilliam disksort(dp, bp); 26041056Swilliam if (dp->b_active == 0) 26141056Swilliam wdustart(du); /* start drive if idle */ 26241056Swilliam if (wdtab.b_active == 0) 26341056Swilliam wdstart(s); /* start IO if controller idle */ 26441056Swilliam splx(s); 26541056Swilliam return; 26641056Swilliam 26741056Swilliam bad: 26841056Swilliam bp->b_error = EINVAL; 26941056Swilliam biodone(bp); 27041056Swilliam } 27141056Swilliam 27241056Swilliam /* Routine to queue a read or write command to the controller. The request is 27341056Swilliam * linked into the active list for the controller. If the controller is idle, 27441056Swilliam * the transfer is started. 27541056Swilliam */ 27641056Swilliam wdustart(du) 27741056Swilliam register struct disk *du; 27841056Swilliam { 27941056Swilliam register struct buf *bp, *dp; 28041056Swilliam 28141056Swilliam dp = &wdutab[du->dk_unit]; 28241056Swilliam if (dp->b_active) 28341056Swilliam return; 28441056Swilliam bp = dp->b_actf; 28541056Swilliam if (bp == NULL) 28641056Swilliam return; 28741056Swilliam dp->b_forw = NULL; 28841056Swilliam if (wdtab.b_actf == NULL) /* link unit into active list */ 28941056Swilliam wdtab.b_actf = dp; 29041056Swilliam else 29141056Swilliam wdtab.b_actl->b_forw = dp; 29241056Swilliam wdtab.b_actl = dp; 29341056Swilliam dp->b_active = 1; /* mark the drive as busy */ 29441056Swilliam } 29541056Swilliam 29641056Swilliam /* 29741056Swilliam * Controller startup routine. This does the calculation, and starts 29841056Swilliam * a single-sector read or write operation. Called to start a transfer, 29941056Swilliam * or from the interrupt routine to continue a multi-sector transfer. 30041056Swilliam * RESTRICTIONS: 30141056Swilliam * 1. The transfer length must be an exact multiple of the sector size. 30241056Swilliam */ 30341056Swilliam 30445550Sbill static wd_sebyse; 30545550Sbill 30641056Swilliam wdstart() 30741056Swilliam { 30841056Swilliam register struct disk *du; /* disk unit for IO */ 30941056Swilliam register struct buf *bp; 31041056Swilliam struct buf *dp; 31141056Swilliam register struct bt_bad *bt_ptr; 31241056Swilliam long blknum, pagcnt, cylin, head, sector; 31341056Swilliam long secpertrk, secpercyl, addr, i; 31449568Swilliam int unit, s; 31541056Swilliam 31641056Swilliam loop: 31741056Swilliam dp = wdtab.b_actf; 31841056Swilliam if (dp == NULL) 31941056Swilliam return; 32041056Swilliam bp = dp->b_actf; 32141056Swilliam if (bp == NULL) { 32241056Swilliam wdtab.b_actf = dp->b_forw; 32341056Swilliam goto loop; 32441056Swilliam } 32549568Swilliam unit = wdunit(bp->b_dev); 32641056Swilliam du = &wddrives[unit]; 32741056Swilliam if (DISKSTATE(du->dk_state) <= RDLABEL) { 32841056Swilliam if (wdcontrol(bp)) { 32941056Swilliam dp->b_actf = bp->av_forw; 33041056Swilliam goto loop; /* done */ 33141056Swilliam } 33241056Swilliam return; 33341056Swilliam } 33449568Swilliam secpertrk = du->dk_dd.d_nsectors; 33549568Swilliam secpercyl = du->dk_dd.d_secpercyl; 33641056Swilliam /* 33741056Swilliam * Convert DEV_BSIZE "blocks" to sectors. 33841056Swilliam */ 33949568Swilliam blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize 34041056Swilliam + du->dk_skip; 34141056Swilliam #ifdef WDDEBUG 34241056Swilliam if (du->dk_skip == 0) { 34341056Swilliam dprintf(DDSK,"\nwdstart %d: %s %d@%d; map ", unit, 34441056Swilliam (bp->b_flags & B_READ) ? "read" : "write", 34541056Swilliam bp->b_bcount, blknum); 34641056Swilliam } else { 34745550Sbill dprintf(DDSK," %d)%x", du->dk_skip, inb(wdc+wd_altsts)); 34841056Swilliam } 34941056Swilliam #endif 35041056Swilliam 35141056Swilliam addr = (int) bp->b_un.b_addr; 35241056Swilliam if(du->dk_skip==0) du->dk_bc = bp->b_bcount; 35341056Swilliam cylin = blknum / secpercyl; 35441056Swilliam head = (blknum % secpercyl) / secpertrk; 35545550Sbill sector = blknum % secpertrk; 35641056Swilliam if (DISKSTATE(du->dk_state) == OPEN) 35749568Swilliam cylin += du->dk_dd.d_partitions[wdpart(bp->b_dev)].p_offset 35849568Swilliam / secpercyl; 35941056Swilliam 36041056Swilliam /* 36141056Swilliam * See if the current block is in the bad block list. 36241056Swilliam * (If we have one, and not formatting.) 36341056Swilliam */ 36445550Sbill if (DISKSTATE(du->dk_state) == OPEN && wd_sebyse) 36541056Swilliam for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) { 36641056Swilliam if (bt_ptr->bt_cyl > cylin) 36741056Swilliam /* Sorted list, and we passed our cylinder. quit. */ 36841056Swilliam break; 36941056Swilliam if (bt_ptr->bt_cyl == cylin && 37041056Swilliam bt_ptr->bt_trksec == (head << 8) + sector) { 37141056Swilliam /* 37241056Swilliam * Found bad block. Calculate new block addr. 37341056Swilliam * This starts at the end of the disk (skip the 37441056Swilliam * last track which is used for the bad block list), 37541056Swilliam * and works backwards to the front of the disk. 37641056Swilliam */ 37741056Swilliam #ifdef WDDEBUG 37841056Swilliam dprintf(DDSK,"--- badblock code -> Old = %d; ", 37941056Swilliam blknum); 38041056Swilliam #endif 38149568Swilliam blknum = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors 38241056Swilliam - (bt_ptr - dkbad[unit].bt_bad) - 1; 38341056Swilliam cylin = blknum / secpercyl; 38441056Swilliam head = (blknum % secpercyl) / secpertrk; 38541056Swilliam sector = blknum % secpertrk; 38641056Swilliam #ifdef WDDEBUG 38741056Swilliam dprintf(DDSK, "new = %d\n", blknum); 38841056Swilliam #endif 38941056Swilliam break; 39041056Swilliam } 39141056Swilliam } 39245550Sbill sector += 1; /* sectors begin with 1, not 0 */ 39341056Swilliam 39441056Swilliam wdtab.b_active = 1; /* mark controller active */ 39541056Swilliam 39645550Sbill if(du->dk_skip==0 || wd_sebyse) { 39745550Sbill if(wdtab.b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += 512; 39845550Sbill while ((inb(wdc+wd_status) & WDCS_BUSY) != 0) ; 39945550Sbill /*while ((inb(wdc+wd_status) & WDCS_DRQ)) inb(wdc+wd_data);*/ 40041056Swilliam outb(wdc+wd_precomp, 0xff); 40141056Swilliam /*wr(wdc+wd_precomp, du->dk_dd.dk_precompcyl / 4);*/ 40241056Swilliam /*if (bp->b_flags & B_FORMAT) { 40341056Swilliam wr(wdc+wd_sector, du->dk_dd.dk_gap3); 40441056Swilliam wr(wdc+wd_seccnt, du->dk_dd.dk_nsectors); 40541056Swilliam } else {*/ 40645550Sbill if(wd_sebyse) 40745550Sbill outb(wdc+wd_seccnt, 1); 40845550Sbill else 40945550Sbill outb(wdc+wd_seccnt, ((du->dk_bc +511) / 512)); 41041056Swilliam outb(wdc+wd_sector, sector); 41141056Swilliam 41241056Swilliam outb(wdc+wd_cyl_lo, cylin); 41341056Swilliam outb(wdc+wd_cyl_hi, cylin >> 8); 41441056Swilliam 41541056Swilliam /* Set up the SDH register (select drive). */ 41641056Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); 41745551Sbill while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; 41841056Swilliam 41941056Swilliam /*if (bp->b_flags & B_FORMAT) 42041056Swilliam wr(wdc+wd_command, WDCC_FORMAT); 42141056Swilliam else*/ 42241056Swilliam outb(wdc+wd_command, 42341056Swilliam (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE); 42441056Swilliam #ifdef WDDEBUG 42545550Sbill dprintf(DDSK,"sector %d cylin %d head %d addr %x sts %x\n", 42645550Sbill sector, cylin, head, addr, inb(wdc+wd_altsts)); 42741056Swilliam #endif 42845550Sbill } 42941056Swilliam 43041056Swilliam /* If this is a read operation, just go away until it's done. */ 43141056Swilliam if (bp->b_flags & B_READ) return; 43241056Swilliam 43341056Swilliam /* Ready to send data? */ 43449568Swilliam while ((inb(wdc+wd_status) & WDCS_DRQ) == 0); 43541056Swilliam 43641056Swilliam /* ASSUMES CONTIGUOUS MEMORY */ 43745550Sbill outsw (wdc+wd_data, addr+du->dk_skip*512, 256); 43841056Swilliam du->dk_bc -= 512; 43941056Swilliam } 44041056Swilliam 44141056Swilliam /* 44241056Swilliam * these are globally defined so they can be found 44341056Swilliam * by the debugger easily in the case of a system crash 44441056Swilliam */ 44541056Swilliam daddr_t wd_errsector; 44641056Swilliam daddr_t wd_errbn; 44741056Swilliam unsigned char wd_errstat; 44841056Swilliam 44941056Swilliam /* Interrupt routine for the controller. Acknowledge the interrupt, check for 45041056Swilliam * errors on the current operation, mark it done if necessary, and start 45141056Swilliam * the next request. Also check for a partially done transfer, and 45241056Swilliam * continue with the next chunk if so. 45341056Swilliam */ 45449568Swilliam wdintr(unit) 45541056Swilliam { 45641056Swilliam register struct disk *du; 45741056Swilliam register struct buf *bp, *dp; 45841056Swilliam int status; 45941056Swilliam char partch ; 46049568Swilliam static wd_haderror; 46141056Swilliam 46241056Swilliam /* Shouldn't need this, but it may be a slow controller. */ 46349568Swilliam while ((status = inb(wdc+wd_status)) & WDCS_BUSY) ; 46441056Swilliam if (!wdtab.b_active) { 46541056Swilliam printf("wd: extra interrupt\n"); 46641056Swilliam return; 46741056Swilliam } 46841056Swilliam 46945549Sbill #ifdef WDDEBUG 47041056Swilliam dprintf(DDSK,"I "); 47141056Swilliam #endif 47241056Swilliam dp = wdtab.b_actf; 47341056Swilliam bp = dp->b_actf; 47449568Swilliam du = &wddrives[wdunit(bp->b_dev)]; 47549568Swilliam partch = wdpart(bp->b_dev) + 'a'; 47641056Swilliam if (DISKSTATE(du->dk_state) <= RDLABEL) { 47741056Swilliam if (wdcontrol(bp)) 47841056Swilliam goto done; 47941056Swilliam return; 48041056Swilliam } 48141056Swilliam if (status & (WDCS_ERR | WDCS_ECCCOR)) { 48245550Sbill wd_errstat = inb(wdc+wd_error); /* save error status */ 48341056Swilliam #ifdef WDDEBUG 48445550Sbill printf("status %x error %x\n", status, wd_errstat); 48541056Swilliam #endif 48645550Sbill if(wd_sebyse == 0) { 48745550Sbill wd_haderror = 1; 48845550Sbill goto outt; 48945550Sbill } 49041056Swilliam /*if (bp->b_flags & B_FORMAT) { 49141056Swilliam du->dk_status = status; 49241056Swilliam du->dk_error = wdp->wd_error; 49341056Swilliam bp->b_flags |= B_ERROR; 49441056Swilliam goto done; 49541056Swilliam }*/ 49641056Swilliam 49749568Swilliam wd_errsector = (bp->b_cylin * du->dk_dd.d_secpercyl) + 49841056Swilliam (((unsigned long) bp->b_blkno * DEV_BSIZE / 49949568Swilliam du->dk_dd.d_secsize) % du->dk_dd.d_secpercyl) + 50041056Swilliam du->dk_skip; 50141056Swilliam wd_errbn = bp->b_blkno 50249568Swilliam + du->dk_skip * du->dk_dd.d_secsize / DEV_BSIZE ; 50341056Swilliam if (status & WDCS_ERR) { 50445550Sbill if (++wdtab.b_errcnt < RETRIES) { 50541056Swilliam wdtab.b_active = 0; 50645550Sbill } else { 50741056Swilliam printf("wd%d%c: ", du->dk_unit, partch); 50841056Swilliam printf( 50941056Swilliam "hard %s error, sn %d bn %d status %b error %b\n", 51041056Swilliam (bp->b_flags & B_READ)? "read":"write", 51141056Swilliam wd_errsector, wd_errbn, status, WDCS_BITS, 51241056Swilliam wd_errstat, WDERR_BITS); 51341056Swilliam bp->b_flags |= B_ERROR; /* flag the error */ 51441056Swilliam } 51541056Swilliam } else 51641056Swilliam log(LOG_WARNING,"wd%d%c: soft ecc sn %d bn %d\n", 51741056Swilliam du->dk_unit, partch, wd_errsector, 51841056Swilliam wd_errbn); 51941056Swilliam } 52045550Sbill outt: 52141056Swilliam 52241056Swilliam /* 52341056Swilliam * If this was a successful read operation, fetch the data. 52441056Swilliam */ 52541056Swilliam if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) { 52641056Swilliam int chk, dummy; 52741056Swilliam 52845550Sbill chk = min(256,du->dk_bc/2); 52941056Swilliam /* Ready to receive data? */ 53049568Swilliam while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; 53141056Swilliam 53241056Swilliam /*dprintf(DDSK,"addr %x\n", (int)bp->b_un.b_addr + du->dk_skip * 512);*/ 53341056Swilliam insw(wdc+wd_data,(int)bp->b_un.b_addr + du->dk_skip * 512 ,chk); 53443592Sdonahn du->dk_bc -= 2*chk; 53545550Sbill while (chk++ < 256) insw (wdc+wd_data,&dummy,1); 53641056Swilliam } 53741056Swilliam 53841056Swilliam wdxfer[du->dk_unit]++; 53941056Swilliam if (wdtab.b_active) { 54041056Swilliam if ((bp->b_flags & B_ERROR) == 0) { 54141056Swilliam du->dk_skip++; /* Add to successful sectors. */ 54241056Swilliam if (wdtab.b_errcnt) { 54341056Swilliam log(LOG_WARNING, "wd%d%c: ", 54441056Swilliam du->dk_unit, partch); 54541056Swilliam log(LOG_WARNING, 54641056Swilliam "soft %s error, sn %d bn %d error %b retries %d\n", 54741056Swilliam (bp->b_flags & B_READ) ? "read" : "write", 54841056Swilliam wd_errsector, wd_errbn, wd_errstat, 54941056Swilliam WDERR_BITS, wdtab.b_errcnt); 55041056Swilliam } 55141056Swilliam wdtab.b_errcnt = 0; 55241056Swilliam 55341056Swilliam /* see if more to transfer */ 55443592Sdonahn /*if (du->dk_skip < (bp->b_bcount + 511) / 512) {*/ 55545550Sbill if (du->dk_bc > 0 && wd_haderror == 0) { 55641056Swilliam wdstart(); 55741056Swilliam return; /* next chunk is started */ 55845550Sbill } else if (wd_haderror && wd_sebyse == 0) { 55945550Sbill du->dk_skip = 0; 56045550Sbill wd_haderror = 0; 56145550Sbill wd_sebyse = 1; 56245550Sbill wdstart(); 56345550Sbill return; /* redo xfer sector by sector */ 56441056Swilliam } 56541056Swilliam } 56641056Swilliam 56741056Swilliam done: 56845550Sbill wd_sebyse = 0; 56941056Swilliam /* done with this transfer, with or without error */ 57041056Swilliam wdtab.b_actf = dp->b_forw; 57141056Swilliam wdtab.b_errcnt = 0; 57241056Swilliam du->dk_skip = 0; 57341056Swilliam dp->b_active = 0; 57441056Swilliam dp->b_actf = bp->av_forw; 57541056Swilliam dp->b_errcnt = 0; 57641056Swilliam bp->b_resid = 0; 57741056Swilliam biodone(bp); 57841056Swilliam } 57941056Swilliam wdtab.b_active = 0; 58041056Swilliam if (dp->b_actf) 58141056Swilliam wdustart(du); /* requeue disk if more io to do */ 58241056Swilliam if (wdtab.b_actf) 58341056Swilliam wdstart(); /* start IO on next drive */ 58441056Swilliam } 58541056Swilliam 58641056Swilliam /* 58741056Swilliam * Initialize a drive. 58841056Swilliam */ 58949568Swilliam wdopen(dev, flags, fmt) 59049568Swilliam dev_t dev; 59149568Swilliam int flags, fmt; 59241056Swilliam { 59341056Swilliam register unsigned int unit; 59441056Swilliam register struct buf *bp; 59541056Swilliam register struct disk *du; 59649568Swilliam int part = wdpart(dev), mask = 1 << part; 59749568Swilliam struct partition *pp; 59841056Swilliam struct dkbad *db; 59941056Swilliam int i, error = 0; 60041056Swilliam 60149568Swilliam unit = wdunit(dev); 60241056Swilliam if (unit >= NWD) return (ENXIO) ; 60341056Swilliam du = &wddrives[unit]; 60449568Swilliam #ifdef notdef 60541056Swilliam if (du->dk_open){ 60641056Swilliam du->dk_open++ ; 60741056Swilliam return(0); /* already is open, don't mess with it */ 60841056Swilliam } 60941056Swilliam #endif 61041056Swilliam du->dk_unit = unit; 61141056Swilliam wdutab[unit].b_actf = NULL; 61241056Swilliam /*if (flags & O_NDELAY) 61341056Swilliam du->dk_state = WANTOPENRAW; 61441056Swilliam else*/ 61541056Swilliam du->dk_state = WANTOPEN; 61641056Swilliam /* 61741056Swilliam * Use the default sizes until we've read the label, 61841056Swilliam * or longer if there isn't one there. 61941056Swilliam */ 62041056Swilliam du->dk_dd = dflt_sizes; 62141056Swilliam 62241056Swilliam /* 62341056Swilliam * Recal, read of disk label will be done in wdcontrol 62441056Swilliam * during first read operation. 62541056Swilliam */ 62641056Swilliam bp = geteblk(512); 62745549Sbill bp->b_dev = dev & 0xff00; 62849568Swilliam bp->b_bcount = 0; 62949568Swilliam bp->b_blkno = LABELSECTOR; 63041056Swilliam bp->b_flags = B_READ; 63141056Swilliam wdstrategy(bp); 63241056Swilliam biowait(bp); 63341056Swilliam if (bp->b_flags & B_ERROR) { 63441056Swilliam error = ENXIO; 63541056Swilliam du->dk_state = CLOSED; 63641056Swilliam goto done; 63741056Swilliam } 63841056Swilliam if (du->dk_state == OPENRAW) { 63941056Swilliam du->dk_state = OPENRAW; 64041056Swilliam goto done; 64141056Swilliam } 64241056Swilliam /* 64341056Swilliam * Read bad sector table into memory. 64441056Swilliam */ 64541056Swilliam i = 0; 64641056Swilliam do { 64741056Swilliam bp->b_flags = B_BUSY | B_READ; 64849568Swilliam bp->b_blkno = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors 64941056Swilliam + i; 65049568Swilliam if (du->dk_dd.d_secsize > DEV_BSIZE) 65149568Swilliam bp->b_blkno *= du->dk_dd.d_secsize / DEV_BSIZE; 65241056Swilliam else 65349568Swilliam bp->b_blkno /= DEV_BSIZE / du->dk_dd.d_secsize; 65449568Swilliam bp->b_bcount = du->dk_dd.d_secsize; 65549568Swilliam bp->b_cylin = du->dk_dd.d_ncylinders - 1; 65641056Swilliam wdstrategy(bp); 65741056Swilliam biowait(bp); 65841056Swilliam } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 && 65949568Swilliam i < du->dk_dd.d_nsectors); 66041056Swilliam db = (struct dkbad *)(bp->b_un.b_addr); 66145550Sbill #define DKBAD_MAGIC 0x4321 66241056Swilliam if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 && 66341056Swilliam db->bt_flag == DKBAD_MAGIC) { 66441056Swilliam dkbad[unit] = *db; 66541056Swilliam du->dk_state = OPEN; 66641056Swilliam } else { 66741056Swilliam printf("wd%d: %s bad-sector file\n", unit, 66841056Swilliam (bp->b_flags & B_ERROR) ? "can't read" : "format error in"); 66949568Swilliam error = ENXIO ; 67041056Swilliam du->dk_state = OPENRAW; 67141056Swilliam } 67241056Swilliam done: 67341056Swilliam bp->b_flags = B_INVAL | B_AGE; 67441056Swilliam brelse(bp); 67541056Swilliam if (error == 0) 67641056Swilliam du->dk_open = 1; 67749568Swilliam 67849568Swilliam /* 67949568Swilliam * Warn if a partion is opened 68049568Swilliam * that overlaps another partition which is open 68149568Swilliam * unless one is the "raw" partition (whole disk). 68249568Swilliam */ 68349568Swilliam #define RAWPART 8 /* 'x' partition */ /* XXX */ 68449568Swilliam if ((du->dk_openpart & mask) == 0 && part != RAWPART) { 68549568Swilliam int start, end; 68649568Swilliam 68749568Swilliam pp = &du->dk_dd.d_partitions[part]; 68849568Swilliam start = pp->p_offset; 68949568Swilliam end = pp->p_offset + pp->p_size; 69049568Swilliam for (pp = du->dk_dd.d_partitions; 69149568Swilliam pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions]; 69249568Swilliam pp++) { 69349568Swilliam if (pp->p_offset + pp->p_size <= start || 69449568Swilliam pp->p_offset >= end) 69549568Swilliam continue; 69649568Swilliam if (pp - du->dk_dd.d_partitions == RAWPART) 69749568Swilliam continue; 69849568Swilliam if (du->dk_openpart & (1 << (pp - 69949568Swilliam du->dk_dd.d_partitions))) 70049568Swilliam log(LOG_WARNING, 70149568Swilliam "wd%d%c: overlaps open partition (%c)\n", 70249568Swilliam unit, part + 'a', 70349568Swilliam pp - du->dk_dd.d_partitions + 'a'); 70449568Swilliam } 70549568Swilliam } 70649568Swilliam if (part >= du->dk_dd.d_npartitions) 70749568Swilliam return (ENXIO); 70849568Swilliam du->dk_openpart |= mask; 70949568Swilliam switch (fmt) { 71049568Swilliam case S_IFCHR: 71149568Swilliam du->dk_copenpart |= mask; 71249568Swilliam break; 71349568Swilliam case S_IFBLK: 71449568Swilliam du->dk_bopenpart |= mask; 71549568Swilliam break; 71649568Swilliam } 71741056Swilliam return (error); 71841056Swilliam } 71941056Swilliam 72041056Swilliam /* 72141056Swilliam * Implement operations other than read/write. 72241056Swilliam * Called from wdstart or wdintr during opens and formats. 72341056Swilliam * Uses finite-state-machine to track progress of operation in progress. 72441056Swilliam * Returns 0 if operation still in progress, 1 if completed. 72541056Swilliam */ 72641056Swilliam wdcontrol(bp) 72741056Swilliam register struct buf *bp; 72841056Swilliam { 72941056Swilliam register struct disk *du; 73041056Swilliam register unit; 73141056Swilliam unsigned char stat; 73241056Swilliam int s, cnt; 73341056Swilliam extern int bootdev, cyloffset; 73441056Swilliam 73549568Swilliam du = &wddrives[wdunit(bp->b_dev)]; 73641056Swilliam unit = du->dk_unit; 73741056Swilliam switch (DISKSTATE(du->dk_state)) { 73841056Swilliam 73941056Swilliam tryagainrecal: 74041056Swilliam case WANTOPEN: /* set SDH, step rate, do restore */ 74141056Swilliam #ifdef WDDEBUG 74241056Swilliam dprintf(DDSK,"wd%d: recal ", unit); 74341056Swilliam #endif 74441056Swilliam s = splbio(); /* not called from intr level ... */ 74541056Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 74641056Swilliam wdtab.b_active = 1; 74741056Swilliam outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); 74841056Swilliam du->dk_state++; 74941056Swilliam splx(s); 75041056Swilliam return(0); 75141056Swilliam 75241056Swilliam case RECAL: 75345551Sbill if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { 75441056Swilliam printf("wd%d: recal", du->dk_unit); 75541056Swilliam if (unit == 0) { 75641056Swilliam printf(": status %b error %b\n", 75741056Swilliam stat, WDCS_BITS, 75841056Swilliam inb(wdc+wd_error), WDERR_BITS); 75941056Swilliam if (++wdtab.b_errcnt < RETRIES) 76041056Swilliam goto tryagainrecal; 76141056Swilliam } 76241056Swilliam goto badopen; 76341056Swilliam } 76449568Swilliam 76549568Swilliam /* some compaq controllers require this ... */ 76649568Swilliam wdsetctlr(bp->b_dev, du); 76749568Swilliam 76841056Swilliam wdtab.b_errcnt = 0; 76941056Swilliam if (ISRAWSTATE(du->dk_state)) { 77041056Swilliam du->dk_state = OPENRAW; 77141056Swilliam return(1); 77241056Swilliam } 77341056Swilliam retry: 77441056Swilliam #ifdef WDDEBUG 77541056Swilliam dprintf(DDSK,"rdlabel "); 77641056Swilliam #endif 77749568Swilliam if( cyloffset < 0 || cyloffset > 8192) cyloffset=0; 77841056Swilliam /* 77949568Swilliam * Read in sector LABELSECTOR to get the pack label 78049568Swilliam * and geometry. 78141056Swilliam */ 78241056Swilliam outb(wdc+wd_precomp, 0xff);/* sometimes this is head bit 3 */ 78341056Swilliam outb(wdc+wd_seccnt, 1); 78449568Swilliam outb(wdc+wd_sector, LABELSECTOR+1); 78541056Swilliam /*if (bp->b_dev == bootdev) { 78641056Swilliam (wdc+wd_cyl_lo = cyloffset & 0xff; 78741056Swilliam (wdc+wd_cyl_hi = cyloffset >> 8; 78841056Swilliam } else { 78941056Swilliam (wdc+wd_cyl_lo = 0; 79041056Swilliam (wdc+wd_cyl_hi = 0; 79141056Swilliam }*/ 79241056Swilliam outb(wdc+wd_cyl_lo, (cyloffset & 0xff)); 79341056Swilliam outb(wdc+wd_cyl_hi, (cyloffset >> 8)); 79441056Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 79541056Swilliam outb(wdc+wd_command, WDCC_READ); 79641056Swilliam du->dk_state = RDLABEL; 79741056Swilliam return(0); 79841056Swilliam 79941056Swilliam case RDLABEL: 80041056Swilliam if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { 80141056Swilliam if (++wdtab.b_errcnt < RETRIES) 80241056Swilliam goto retry; 80341056Swilliam printf("wd%d: read label", unit); 80441056Swilliam goto badopen; 80541056Swilliam } 80641056Swilliam 80741056Swilliam insw(wdc+wd_data, bp->b_un.b_addr, 256); 80841056Swilliam 80941056Swilliam if (((struct disklabel *) 81049568Swilliam (bp->b_un.b_addr + LABELOFFSET))->d_magic == DISKMAGIC) { 81141056Swilliam du->dk_dd = 81241056Swilliam * (struct disklabel *) (bp->b_un.b_addr + LABELOFFSET); 81341056Swilliam } else { 81441056Swilliam printf("wd%d: bad disk label\n", du->dk_unit); 81541056Swilliam du->dk_state = OPENRAW; 81641056Swilliam } 81745552Sbill 81845552Sbill s = splbio(); /* not called from intr level ... */ 81949568Swilliam while ((stat = inb(wdc+wd_status)) & WDCS_BUSY); 82049568Swilliam 82149568Swilliam wdsetctlr(bp->b_dev, du); 82249568Swilliam 82345552Sbill outb(wdc+wd_seccnt, 0); 82445552Sbill splx(s); 82545552Sbill 82641056Swilliam if (du->dk_state == RDLABEL) 82741056Swilliam du->dk_state = RDBADTBL; 82841056Swilliam /* 82941056Swilliam * The rest of the initialization can be done 83041056Swilliam * by normal means. 83141056Swilliam */ 83241056Swilliam return(1); 83341056Swilliam 83441056Swilliam default: 83549568Swilliam panic("wdcontrol"); 83641056Swilliam } 83741056Swilliam /* NOTREACHED */ 83841056Swilliam 83941056Swilliam badopen: 84041056Swilliam printf(": status %b error %b\n", 84141056Swilliam stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS); 84241056Swilliam du->dk_state = OPENRAW; 84341056Swilliam return(1); 84441056Swilliam } 84541056Swilliam 84649568Swilliam wdsetctlr(dev, du) dev_t dev; struct disk *du; { 84749568Swilliam int stat; 84841056Swilliam 84949568Swilliam outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders); 85049568Swilliam outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders)>>8); 85149568Swilliam outb(wdc+wd_sdh, WDSD_IBM | (wdunit(dev) << 4) + du->dk_dd.d_ntracks-1); 85249568Swilliam outb(wdc+wd_seccnt, du->dk_dd.d_nsectors); 85349568Swilliam outb(wdc+wd_command, 0x91); 85449568Swilliam 85549568Swilliam while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) ; 85649568Swilliam stat = inb(wdc+wd_error); 85749568Swilliam return(stat); 85849568Swilliam } 85949568Swilliam 86049568Swilliam /* ARGSUSED */ 86149568Swilliam wdclose(dev, flags, fmt) 86249568Swilliam dev_t dev; 86349568Swilliam int flags, fmt; 86449568Swilliam { 86549568Swilliam register struct disk *du; 86649568Swilliam 86749568Swilliam du = &wddrives[wdunit(dev)]; 86841056Swilliam du->dk_open-- ; 86941056Swilliam /*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */ 87041056Swilliam } 87141056Swilliam 87241056Swilliam wdioctl(dev,cmd,addr,flag) 87341056Swilliam dev_t dev; 87441056Swilliam caddr_t addr; 87541056Swilliam { 87649568Swilliam int unit = wdunit(dev); 87741056Swilliam register struct disk *du; 87841056Swilliam int error = 0; 87941056Swilliam struct uio auio; 88041056Swilliam struct iovec aiov; 88141056Swilliam /*int wdformat();*/ 88241056Swilliam 88341056Swilliam du = &wddrives[unit]; 88441056Swilliam 88541056Swilliam switch (cmd) { 88641056Swilliam 88741056Swilliam case DIOCGDINFO: 88841056Swilliam *(struct disklabel *)addr = du->dk_dd; 88941056Swilliam break; 89041056Swilliam 89149568Swilliam case DIOCGPART: 89249568Swilliam ((struct partinfo *)addr)->disklab = &du->dk_dd; 89349568Swilliam ((struct partinfo *)addr)->part = 89449568Swilliam &du->dk_dd.d_partitions[wdpart(dev)]; 89549568Swilliam break; 89649568Swilliam 89749568Swilliam case DIOCSDINFO: 89849568Swilliam if ((flag & FWRITE) == 0) 89949568Swilliam error = EBADF; 90049568Swilliam else 90149568Swilliam error = setdisklabel(&du->dk_dd, 90249568Swilliam (struct disklabel *)addr, 90349568Swilliam 0 /*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/); 90449568Swilliam /*if (error == 0 && dk->dk_state == OPENRAW && 90549568Swilliam vdreset_drive(vddinfo[unit])) 90649568Swilliam dk->dk_state = OPEN;*/ 90749568Swilliam wdsetctlr(dev, du); 90849568Swilliam break; 90949568Swilliam 91049568Swilliam case DIOCWLABEL: 91149568Swilliam if ((flag & FWRITE) == 0) 91249568Swilliam error = EBADF; 91349568Swilliam else 91449568Swilliam du->dk_wlabel = *(int *)addr; 91549568Swilliam break; 91649568Swilliam 91749568Swilliam case DIOCWDINFO: 91849568Swilliam if ((flag & FWRITE) == 0) 91949568Swilliam error = EBADF; 92049568Swilliam else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr, 92149568Swilliam 0/*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/)) == 0) { 92249568Swilliam int wlab; 92349568Swilliam 92449568Swilliam /*if (error == 0 && dk->dk_state == OPENRAW && 92549568Swilliam vdreset_drive(vddinfo[unit])) 92649568Swilliam dk->dk_state = OPEN; */ 92749568Swilliam wdsetctlr(dev, du); 92849568Swilliam 92949568Swilliam /* simulate opening partition 0 so write succeeds */ 93049568Swilliam /* dk->dk_openpart |= (1 << 0); /* XXX */ 93149568Swilliam wlab = du->dk_wlabel; 93249568Swilliam du->dk_wlabel = 1; 93349568Swilliam error = writedisklabel(dev, wdstrategy, &du->dk_dd,wdpart(dev)); 93449568Swilliam /*dk->dk_openpart = dk->dk_copenpart | dk->dk_bopenpart;*/ 93549568Swilliam du->dk_wlabel = wlab; 93649568Swilliam } 93749568Swilliam break; 93849568Swilliam 93949568Swilliam #ifdef notyet 94041056Swilliam case DIOCGDINFOP: 94141056Swilliam *(struct disklabel **)addr = &(du->dk_dd); 94241056Swilliam break; 94341056Swilliam 94441056Swilliam case DIOCWFORMAT: 94541056Swilliam if ((flag & FWRITE) == 0) 94641056Swilliam error = EBADF; 94741056Swilliam else { 94841056Swilliam register struct format_op *fop; 94941056Swilliam 95041056Swilliam fop = (struct format_op *)addr; 95141056Swilliam aiov.iov_base = fop->df_buf; 95241056Swilliam aiov.iov_len = fop->df_count; 95341056Swilliam auio.uio_iov = &aiov; 95441056Swilliam auio.uio_iovcnt = 1; 95541056Swilliam auio.uio_resid = fop->df_count; 95641056Swilliam auio.uio_segflg = 0; 95741056Swilliam auio.uio_offset = 95849568Swilliam fop->df_startblk * du->dk_dd.d_secsize; 95941056Swilliam error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE, 96041056Swilliam minphys, &auio); 96141056Swilliam fop->df_count -= auio.uio_resid; 96241056Swilliam fop->df_reg[0] = du->dk_status; 96341056Swilliam fop->df_reg[1] = du->dk_error; 96441056Swilliam } 96541056Swilliam break; 96641056Swilliam #endif 96741056Swilliam 96841056Swilliam default: 96941056Swilliam error = ENOTTY; 97041056Swilliam break; 97141056Swilliam } 97241056Swilliam return (error); 97341056Swilliam } 97441056Swilliam 97541056Swilliam /*wdformat(bp) 97641056Swilliam struct buf *bp; 97741056Swilliam { 97841056Swilliam 97941056Swilliam bp->b_flags |= B_FORMAT; 98041056Swilliam return (wdstrategy(bp)); 98141056Swilliam }*/ 98241056Swilliam 98341056Swilliam /* 98441056Swilliam * Routines to do raw IO for a unit. 98541056Swilliam */ 98641056Swilliam wdread(dev, uio) /* character read routine */ 98741056Swilliam dev_t dev; 98841056Swilliam struct uio *uio; 98941056Swilliam { 99049568Swilliam int unit = wdunit(dev) ; 99141056Swilliam 99241056Swilliam if (unit >= NWD) return(ENXIO); 99341056Swilliam return(physio(wdstrategy, &rwdbuf[unit], dev, B_READ, minphys, uio)); 99441056Swilliam } 99541056Swilliam 99641056Swilliam 99741056Swilliam wdwrite(dev, uio) /* character write routine */ 99841056Swilliam dev_t dev; 99941056Swilliam struct uio *uio; 100041056Swilliam { 100149568Swilliam int unit = wdunit(dev) ; 100241056Swilliam 100341056Swilliam if (unit >= NWD) return(ENXIO); 100441056Swilliam return(physio(wdstrategy, &rwdbuf[unit], dev, B_WRITE, minphys, uio)); 100541056Swilliam } 100641056Swilliam 100741056Swilliam wdsize(dev) 100841056Swilliam dev_t dev; 100941056Swilliam { 101049568Swilliam register unit = wdunit(dev); 101149568Swilliam register part = wdpart(dev); 101241056Swilliam register struct disk *du; 101341056Swilliam register val ; 101441056Swilliam 101541056Swilliam if (unit >= NWD) return(-1); 101649568Swilliam if (wddrives[unit].dk_state == 0) { 101749568Swilliam val = wdopen (dev, 0); 101849568Swilliam if (val < 0) 101949568Swilliam return (-1); 102049568Swilliam } 102141056Swilliam du = &wddrives[unit]; 102249568Swilliam return((int)((u_long)du->dk_dd.d_partitions[part].p_size * 102349568Swilliam du->dk_dd.d_secsize / 512)); 102441056Swilliam } 102541056Swilliam 102649568Swilliam extern char *vmmap; /* poor name! */ 102749568Swilliam 102841056Swilliam wddump(dev) /* dump core after a system crash */ 102941056Swilliam dev_t dev; 103041056Swilliam { 103141056Swilliam register struct disk *du; /* disk unit to do the IO */ 103241056Swilliam register struct bt_bad *bt_ptr; 103341056Swilliam long num; /* number of sectors to write */ 103449568Swilliam int unit, part; 103541056Swilliam long cyloff, blknum, blkcnt; 103649568Swilliam long cylin, head, sector, stat; 103741056Swilliam long secpertrk, secpercyl, nblocks, i; 103849568Swilliam char *addr; 103949568Swilliam extern int Maxmem; 104041056Swilliam static wddoingadump = 0 ; 104149568Swilliam extern CMAP1; 104249568Swilliam extern char CADDR1[]; 104341056Swilliam 104449568Swilliam 104549568Swilliam #ifdef ARGO 104649568Swilliam outb(0x461,0); /* disable failsafe timer */ 104749568Swilliam #endif 104849568Swilliam addr = (char *) 0; /* starting address */ 104941056Swilliam /* size of memory to dump */ 105049568Swilliam num = Maxmem; 105149568Swilliam unit = wdunit(dev); /* eventually support floppies? */ 105249568Swilliam part = wdpart(dev); /* file system */ 105341056Swilliam /* check for acceptable drive number */ 105441056Swilliam if (unit >= NWD) return(ENXIO); 105541056Swilliam 105641056Swilliam du = &wddrives[unit]; 105741056Swilliam /* was it ever initialized ? */ 105841056Swilliam if (du->dk_state < OPEN) return (ENXIO) ; 105941056Swilliam 106041056Swilliam /* Convert to disk sectors */ 106149568Swilliam num = (u_long) num * NBPG / du->dk_dd.d_secsize; 106241056Swilliam 106341056Swilliam /* check if controller active */ 106441056Swilliam /*if (wdtab.b_active) return(EFAULT); */ 106541056Swilliam if (wddoingadump) return(EFAULT); 106641056Swilliam 106749568Swilliam secpertrk = du->dk_dd.d_nsectors; 106849568Swilliam secpercyl = du->dk_dd.d_secpercyl; 106949568Swilliam nblocks = du->dk_dd.d_partitions[part].p_size; 107049568Swilliam cyloff = du->dk_dd.d_partitions[part].p_offset / secpercyl; 107141056Swilliam 107249568Swilliam /*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/ 107341056Swilliam /* check transfer bounds against partition size */ 107449568Swilliam if ((dumplo < 0) || ((dumplo + num) > nblocks)) 107541056Swilliam return(EINVAL); 107641056Swilliam 107741056Swilliam /*wdtab.b_active = 1; /* mark controller active for if we 107841056Swilliam panic during the dump */ 107941056Swilliam wddoingadump = 1 ; i = 100000 ; 108049568Swilliam while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ; 108149568Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 108249568Swilliam outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); 108349568Swilliam while (inb(wdc+wd_status) & WDCS_BUSY) ; 108449568Swilliam 108549568Swilliam /* some compaq controllers require this ... */ 108649568Swilliam wdsetctlr(dev, du); 108741056Swilliam 108841056Swilliam blknum = dumplo; 108941056Swilliam while (num > 0) { 109041056Swilliam #ifdef notdef 109141056Swilliam if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER; 109241056Swilliam if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl) 109341056Swilliam blkcnt = secpercyl - (blknum % secpercyl); 109441056Swilliam /* keep transfer within current cylinder */ 109541056Swilliam #endif 109653408Sbostic pmap_enter(kernel_pmap, vmmap, addr, VM_PROT_READ, TRUE); 109741056Swilliam 109841056Swilliam /* compute disk address */ 109941056Swilliam cylin = blknum / secpercyl; 110041056Swilliam head = (blknum % secpercyl) / secpertrk; 110141056Swilliam sector = blknum % secpertrk; 110243592Sdonahn cylin += cyloff; 110341056Swilliam 110449568Swilliam #ifdef notyet 110541056Swilliam /* 110641056Swilliam * See if the current block is in the bad block list. 110741056Swilliam * (If we have one.) 110841056Swilliam */ 110941056Swilliam for (bt_ptr = dkbad[unit].bt_bad; 111041056Swilliam bt_ptr->bt_cyl != -1; bt_ptr++) { 111141056Swilliam if (bt_ptr->bt_cyl > cylin) 111241056Swilliam /* Sorted list, and we passed our cylinder. 111341056Swilliam quit. */ 111441056Swilliam break; 111541056Swilliam if (bt_ptr->bt_cyl == cylin && 111641056Swilliam bt_ptr->bt_trksec == (head << 8) + sector) { 111741056Swilliam /* 111841056Swilliam * Found bad block. Calculate new block addr. 111941056Swilliam * This starts at the end of the disk (skip the 112041056Swilliam * last track which is used for the bad block list), 112141056Swilliam * and works backwards to the front of the disk. 112241056Swilliam */ 112349568Swilliam blknum = (du->dk_dd.d_secperunit) 112449568Swilliam - du->dk_dd.d_nsectors 112541056Swilliam - (bt_ptr - dkbad[unit].bt_bad) - 1; 112641056Swilliam cylin = blknum / secpercyl; 112741056Swilliam head = (blknum % secpercyl) / secpertrk; 112841056Swilliam sector = blknum % secpertrk; 112941056Swilliam break; 113041056Swilliam } 113141056Swilliam 113249568Swilliam #endif 113349568Swilliam sector++; /* origin 1 */ 113449568Swilliam 113541056Swilliam /* select drive. */ 113649568Swilliam outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); 113749568Swilliam while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; 113841056Swilliam 113941056Swilliam /* transfer some blocks */ 114049568Swilliam outb(wdc+wd_sector, sector); 114149568Swilliam outb(wdc+wd_seccnt,1); 114249568Swilliam outb(wdc+wd_cyl_lo, cylin); 114349568Swilliam outb(wdc+wd_cyl_hi, cylin >> 8); 114441056Swilliam #ifdef notdef 114541056Swilliam /* lets just talk about this first...*/ 114649568Swilliam pg ("sdh 0%o sector %d cyl %d addr 0x%x", 114749568Swilliam inb(wdc+wd_sdh), inb(wdc+wd_sector), 114849568Swilliam inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr) ; 114941056Swilliam #endif 115049568Swilliam #ifdef ODYSSEUS 115149568Swilliam if(cylin < 46 || cylin > 91)pg("oops"); 115249568Swilliam #endif 115349568Swilliam #ifdef PRIAM 115449568Swilliam if(cylin < 40 || cylin > 79)pg("oops"); 115549568Swilliam #endif 115649568Swilliam outb(wdc+wd_command, WDCC_WRITE); 115741056Swilliam 115841056Swilliam /* Ready to send data? */ 115949568Swilliam while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; 116049568Swilliam if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; 116141056Swilliam 116249568Swilliam outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256); 116349568Swilliam (int) addr += 512; 116449568Swilliam 116549568Swilliam if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; 116641056Swilliam /* Check data request (should be done). */ 116749568Swilliam if (inb(wdc+wd_status) & WDCS_DRQ) return(EIO) ; 116841056Swilliam 116941056Swilliam /* wait for completion */ 117049568Swilliam for ( i = 1000000 ; inb(wdc+wd_status) & WDCS_BUSY ; i--) { 117141056Swilliam if (i < 0) return (EIO) ; 117241056Swilliam } 117341056Swilliam /* error check the xfer */ 117449568Swilliam if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; 117541056Swilliam /* update block count */ 117641056Swilliam num--; 117741056Swilliam blknum++ ; 117841056Swilliam if (num % 100 == 0) printf(".") ; 117941056Swilliam } 118041056Swilliam return(0); 118141056Swilliam } 118241056Swilliam #endif 1183