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