141056Swilliam /*-
2*63364Sbostic * Copyright (c) 1990, 1993
3*63364Sbostic * The Regents of the University of California. 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*63364Sbostic * @(#)wd.c 8.1 (Berkeley) 06/11/93
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
2056513Sbostic #include <sys/param.h>
2156513Sbostic #include <sys/dkbad.h>
2256513Sbostic #include <sys/systm.h>
2356513Sbostic #include <sys/conf.h>
2456513Sbostic #include <sys/file.h>
2556513Sbostic #include <sys/stat.h>
2656513Sbostic #include <sys/ioctl.h>
2756513Sbostic #include <sys/disklabel.h>
2856513Sbostic #include <sys/buf.h>
2956513Sbostic #include <sys/uio.h>
3056513Sbostic #include <sys/syslog.h>
3141056Swilliam
3256513Sbostic #include <i386/isa/isa_device.h>
3356513Sbostic #include <i386/isa/icu.h>
3456513Sbostic #include <i386/isa/wdreg.h>
3556513Sbostic #include <vm/vm.h>
3656513Sbostic
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 */
wdstrategy(bp)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 */
wdustart(du)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
wdstart()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 */
wdintr(unit)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 */
wdopen(dev,flags,fmt)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 */
wdcontrol(bp)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
wdsetctlr(dev,du)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 */
wdclose(dev,flags,fmt)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
wdioctl(dev,cmd,addr,flag)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 */
wdread(dev,uio)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
wdwrite(dev,uio)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
wdsize(dev)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
wddump(dev)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