xref: /csrg-svn/sys/i386/isa/wd.c (revision 45550)
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*45550Sbill  *	@(#)wd.c	5.4 (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 */
90*45550Sbill 		36,		/* # of sectors per track */
9141056Swilliam 		15,		/* # of tracks per cylinder */
92*45550Sbill 		1224,		/* # of cylinders per unit */
93*45550Sbill 		36*15,		/* # of sectors per cylinder */
94*45550Sbill 		1224*15*36,	/* # of sectors per unit */
9541056Swilliam 		0		/* write precomp cylinder (none) */
9641056Swilliam 	},
97*45550Sbill 	21600,	0,	/* A=root filesystem */
98*45550Sbill 	21600,	40,
99*45550Sbill 	660890, 0,	/* C=whole disk */
100*45550Sbill 	216000,	80,
10141056Swilliam 	0,	0,
10241056Swilliam 	0,	0,
10341056Swilliam 	0,	0,
104*45550Sbill 	399600,	480
10541056Swilliam };
106*45550Sbill 
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));
149*45550Sbill 	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];
203*45550Sbill 	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 
249*45550Sbill static wd_sebyse;
250*45550Sbill 
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 {
294*45550Sbill 		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;
302*45550Sbill 	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 	 */
310*45550Sbill 	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 	}
338*45550Sbill 	sector += 1;	/* sectors begin with 1, not 0 */
33941056Swilliam 
34041056Swilliam 	wdtab.b_active = 1;		/* mark controller active */
34141056Swilliam 
342*45550Sbill 	if(du->dk_skip==0 || wd_sebyse) {
343*45550Sbill 	if(wdtab.b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += 512;
344*45550Sbill 	while ((inb(wdc+wd_status) & WDCS_BUSY) != 0) ;
345*45550Sbill 	/*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 {*/
352*45550Sbill 	if(wd_sebyse)
353*45550Sbill 		outb(wdc+wd_seccnt, 1);
354*45550Sbill 	else
355*45550Sbill 		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));
36341056Swilliam 	while ((inb(wdc+wd_altsts) & 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
371*45550Sbill 	dprintf(DDSK,"sector %d cylin %d head %d addr %x sts %x\n",
372*45550Sbill 	    sector, cylin, head, addr, inb(wdc+wd_altsts));
37341056Swilliam #endif
374*45550Sbill }
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?	*/
38041056Swilliam 	while ((inb(wdc+wd_altsts) & WDCS_DRQ) == 0)
38141056Swilliam 		nulldev();		/* So compiler won't optimize out */
38241056Swilliam 
38341056Swilliam 	/* ASSUMES CONTIGUOUS MEMORY */
384*45550Sbill 	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 ;
408*45550Sbill static shit[32];
409*45550Sbill static wd_haderror;
41041056Swilliam 
41141056Swilliam 	/* Shouldn't need this, but it may be a slow controller.	*/
412*45550Sbill 	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)) {
432*45550Sbill 		wd_errstat = inb(wdc+wd_error);		/* save error status */
43341056Swilliam #ifdef	WDDEBUG
434*45550Sbill 		printf("status %x error %x\n", status, wd_errstat);
43541056Swilliam #endif
436*45550Sbill 		if(wd_sebyse == 0) {
437*45550Sbill 			wd_haderror = 1;
438*45550Sbill 			goto outt;
439*45550Sbill 		}
44041056Swilliam 		/*if (bp->b_flags & B_FORMAT) {
44141056Swilliam 			du->dk_status = status;
44241056Swilliam 			du->dk_error = wdp->wd_error;
44341056Swilliam 			bp->b_flags |= B_ERROR;
44441056Swilliam 			goto done;
44541056Swilliam 		}*/
44641056Swilliam 
44741056Swilliam 		wd_errsector = (bp->b_cylin * du->dk_dd.dk_secpercyl) +
44841056Swilliam 			(((unsigned long) bp->b_blkno * DEV_BSIZE /
44941056Swilliam 			    du->dk_dd.dk_secsize) % du->dk_dd.dk_secpercyl) +
45041056Swilliam 			du->dk_skip;
45141056Swilliam 		wd_errbn = bp->b_blkno
45241056Swilliam 			+ du->dk_skip * du->dk_dd.dk_secsize / DEV_BSIZE ;
45341056Swilliam 		if (status & WDCS_ERR) {
454*45550Sbill 			if (++wdtab.b_errcnt < RETRIES) {
45541056Swilliam 				wdtab.b_active = 0;
456*45550Sbill 				/*while ((inb(wdc+wd_status) & WDCS_DRQ))
457*45550Sbill 				insw(wdc+wd_data, &shit, sizeof(shit)/2);*/
458*45550Sbill 			} else {
45941056Swilliam 				printf("wd%d%c: ", du->dk_unit, partch);
46041056Swilliam 				printf(
46141056Swilliam 				"hard %s error, sn %d bn %d status %b error %b\n",
46241056Swilliam 					(bp->b_flags & B_READ)? "read":"write",
46341056Swilliam 					wd_errsector, wd_errbn, status, WDCS_BITS,
46441056Swilliam 					wd_errstat, WDERR_BITS);
46541056Swilliam 				bp->b_flags |= B_ERROR;	/* flag the error */
46641056Swilliam 			}
46741056Swilliam 		} else
46841056Swilliam 			log(LOG_WARNING,"wd%d%c: soft ecc sn %d bn %d\n",
46941056Swilliam 				du->dk_unit, partch, wd_errsector,
47041056Swilliam 				wd_errbn);
47141056Swilliam 	}
472*45550Sbill outt:
47341056Swilliam 
47441056Swilliam 	/*
47541056Swilliam 	 * If this was a successful read operation, fetch the data.
47641056Swilliam 	 */
47741056Swilliam 	if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) {
47841056Swilliam 		int chk, dummy;
47941056Swilliam 
480*45550Sbill 		chk = min(256,du->dk_bc/2);
48141056Swilliam 		/* Ready to receive data?	*/
48241056Swilliam 		while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
48341056Swilliam 			nulldev();
48441056Swilliam 
48541056Swilliam /*dprintf(DDSK,"addr %x\n", (int)bp->b_un.b_addr + du->dk_skip * 512);*/
48641056Swilliam 		insw(wdc+wd_data,(int)bp->b_un.b_addr + du->dk_skip * 512 ,chk);
48743592Sdonahn 		du->dk_bc -= 2*chk;
488*45550Sbill 		while (chk++ < 256) insw (wdc+wd_data,&dummy,1);
48941056Swilliam 	}
49041056Swilliam 
49141056Swilliam 	wdxfer[du->dk_unit]++;
49241056Swilliam 	if (wdtab.b_active) {
49341056Swilliam 		if ((bp->b_flags & B_ERROR) == 0) {
49441056Swilliam 			du->dk_skip++;		/* Add to successful sectors. */
49541056Swilliam 			if (wdtab.b_errcnt) {
49641056Swilliam 				log(LOG_WARNING, "wd%d%c: ",
49741056Swilliam 						du->dk_unit, partch);
49841056Swilliam 				log(LOG_WARNING,
49941056Swilliam 			"soft %s error, sn %d bn %d error %b retries %d\n",
50041056Swilliam 				    (bp->b_flags & B_READ) ? "read" : "write",
50141056Swilliam 				    wd_errsector, wd_errbn, wd_errstat,
50241056Swilliam 				    WDERR_BITS, wdtab.b_errcnt);
50341056Swilliam 			}
50441056Swilliam 			wdtab.b_errcnt = 0;
50541056Swilliam 
50641056Swilliam 			/* see if more to transfer */
50743592Sdonahn 			/*if (du->dk_skip < (bp->b_bcount + 511) / 512) {*/
508*45550Sbill 			if (du->dk_bc > 0 && wd_haderror == 0) {
50941056Swilliam 				wdstart();
51041056Swilliam 				return;		/* next chunk is started */
511*45550Sbill 			} else if (wd_haderror && wd_sebyse == 0) {
512*45550Sbill 				du->dk_skip = 0;
513*45550Sbill 				wd_haderror = 0;
514*45550Sbill 				wd_sebyse = 1;
515*45550Sbill 				wdstart();
516*45550Sbill 				return;		/* redo xfer sector by sector */
51741056Swilliam 			}
51841056Swilliam 		}
51941056Swilliam 
52041056Swilliam done:
521*45550Sbill 		wd_sebyse = 0;
52241056Swilliam 		/* done with this transfer, with or without error */
52341056Swilliam 		wdtab.b_actf = dp->b_forw;
52441056Swilliam 		wdtab.b_errcnt = 0;
52541056Swilliam 		du->dk_skip = 0;
52641056Swilliam 		dp->b_active = 0;
52741056Swilliam 		dp->b_actf = bp->av_forw;
52841056Swilliam 		dp->b_errcnt = 0;
52941056Swilliam 		bp->b_resid = 0;
53041056Swilliam 		biodone(bp);
53141056Swilliam 	}
53241056Swilliam 	wdtab.b_active = 0;
53341056Swilliam 	if (dp->b_actf)
53441056Swilliam 		wdustart(du);		/* requeue disk if more io to do */
53541056Swilliam 	if (wdtab.b_actf)
53641056Swilliam 		wdstart();		/* start IO on next drive */
53741056Swilliam }
53841056Swilliam 
53941056Swilliam /*
54041056Swilliam  * Initialize a drive.
54141056Swilliam  */
54241056Swilliam wdopen(dev, flags)
54341056Swilliam 	dev_t	dev;
54441056Swilliam 	int	flags;
54541056Swilliam {
54641056Swilliam 	register unsigned int unit;
54741056Swilliam 	register struct buf *bp;
54841056Swilliam 	register struct disk *du;
54941056Swilliam 	struct dkbad *db;
55041056Swilliam 	int i, error = 0;
55141056Swilliam 
55241056Swilliam 	unit = WDUNIT(dev);
55341056Swilliam 	if (unit >= NWD) return (ENXIO) ;
55441056Swilliam 	du = &wddrives[unit];
55541056Swilliam 	if (du->dk_open){
55641056Swilliam 		du->dk_open++ ;
55741056Swilliam 		return(0);	/* already is open, don't mess with it */
55841056Swilliam 	}
55941056Swilliam #ifdef THE_BUG
56041056Swilliam 	if (du->dk_state && DISKSTATE(du->dk_state) <= OPEN)
56141056Swilliam 		return(0);
56241056Swilliam #endif
56341056Swilliam 	du->dk_unit = unit;
56441056Swilliam 	wdutab[unit].b_actf = NULL;
56541056Swilliam 	/*if (flags & O_NDELAY)
56641056Swilliam 		du->dk_state = WANTOPENRAW;
56741056Swilliam 	else*/
56841056Swilliam 		du->dk_state = WANTOPEN;
56941056Swilliam 	/*
57041056Swilliam 	 * Use the default sizes until we've read the label,
57141056Swilliam 	 * or longer if there isn't one there.
57241056Swilliam 	 */
57341056Swilliam 	du->dk_dd = dflt_sizes;
57441056Swilliam 
57541056Swilliam 	/*
57641056Swilliam 	 * Recal, read of disk label will be done in wdcontrol
57741056Swilliam 	 * during first read operation.
57841056Swilliam 	 */
57941056Swilliam 	bp = geteblk(512);
58045549Sbill 	bp->b_dev = dev & 0xff00;
58141056Swilliam 	bp->b_blkno = bp->b_bcount = 0;
58241056Swilliam 	bp->b_flags = B_READ;
58341056Swilliam 	wdstrategy(bp);
58441056Swilliam 	biowait(bp);
58541056Swilliam 	if (bp->b_flags & B_ERROR) {
58641056Swilliam 		u.u_error = 0; 	/* XXX */
58741056Swilliam 		error = ENXIO;
58841056Swilliam 		du->dk_state = CLOSED;
58941056Swilliam 		goto done;
59041056Swilliam 	}
59141056Swilliam 	if (du->dk_state == OPENRAW) {
59241056Swilliam 		du->dk_state = OPENRAW;
59341056Swilliam 		goto done;
59441056Swilliam 	}
59541056Swilliam 	/*
59641056Swilliam 	 * Read bad sector table into memory.
59741056Swilliam 	 */
59841056Swilliam 	i = 0;
59941056Swilliam 	do {
60041056Swilliam 		u.u_error = 0;				/* XXX */
60141056Swilliam 		bp->b_flags = B_BUSY | B_READ;
60241056Swilliam 		bp->b_blkno = du->dk_dd.dk_secperunit - du->dk_dd.dk_nsectors
60341056Swilliam 			+ i;
60441056Swilliam 		if (du->dk_dd.dk_secsize > DEV_BSIZE)
60541056Swilliam 			bp->b_blkno *= du->dk_dd.dk_secsize / DEV_BSIZE;
60641056Swilliam 		else
60741056Swilliam 			bp->b_blkno /= DEV_BSIZE / du->dk_dd.dk_secsize;
60841056Swilliam 		bp->b_bcount = du->dk_dd.dk_secsize;
60941056Swilliam 		bp->b_cylin = du->dk_dd.dk_ncylinders - 1;
61041056Swilliam 		wdstrategy(bp);
61141056Swilliam 		biowait(bp);
61241056Swilliam 	} while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
61341056Swilliam 		i < du->dk_dd.dk_nsectors);
61441056Swilliam 	db = (struct dkbad *)(bp->b_un.b_addr);
615*45550Sbill #define DKBAD_MAGIC 0x4321
61641056Swilliam 	if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 &&
61741056Swilliam 	    db->bt_flag == DKBAD_MAGIC) {
61841056Swilliam 		dkbad[unit] = *db;
61941056Swilliam 		du->dk_state = OPEN;
62041056Swilliam 	} else {
62141056Swilliam 		printf("wd%d: %s bad-sector file\n", unit,
62241056Swilliam 		    (bp->b_flags & B_ERROR) ? "can't read" : "format error in");
62341056Swilliam 		u.u_error = 0;				/* XXX */
62441056Swilliam 		/*error = ENXIO ;*/
62541056Swilliam 		du->dk_state = OPENRAW;
62641056Swilliam 	}
62741056Swilliam done:
62841056Swilliam 	bp->b_flags = B_INVAL | B_AGE;
62941056Swilliam 	brelse(bp);
63041056Swilliam 	if (error == 0)
63141056Swilliam 		du->dk_open = 1;
63241056Swilliam 	return (error);
63341056Swilliam }
63441056Swilliam 
63541056Swilliam /*
63641056Swilliam  * Implement operations other than read/write.
63741056Swilliam  * Called from wdstart or wdintr during opens and formats.
63841056Swilliam  * Uses finite-state-machine to track progress of operation in progress.
63941056Swilliam  * Returns 0 if operation still in progress, 1 if completed.
64041056Swilliam  */
64141056Swilliam wdcontrol(bp)
64241056Swilliam 	register struct buf *bp;
64341056Swilliam {
64441056Swilliam 	register struct disk *du;
64541056Swilliam 	register wdc = IO_WD0; /*XXX*/
64641056Swilliam 	register unit;
64741056Swilliam 	unsigned char  stat;
64841056Swilliam 	int s, cnt;
64941056Swilliam 	extern int bootdev, cyloffset;
65041056Swilliam 
65141056Swilliam 	du = &wddrives[WDUNIT(bp->b_dev)];
65241056Swilliam 	unit = du->dk_unit;
65341056Swilliam 	switch (DISKSTATE(du->dk_state)) {
65441056Swilliam 
65541056Swilliam 	tryagainrecal:
65641056Swilliam 	case WANTOPEN:			/* set SDH, step rate, do restore */
65741056Swilliam #ifdef	WDDEBUG
65841056Swilliam 		dprintf(DDSK,"wd%d: recal ", unit);
65941056Swilliam #endif
66041056Swilliam 		s = splbio();		/* not called from intr level ... */
66141056Swilliam 		outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
66241056Swilliam 		wdtab.b_active = 1;
66341056Swilliam 		outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
66441056Swilliam 		du->dk_state++;
66541056Swilliam 		splx(s);
66641056Swilliam 		return(0);
66741056Swilliam 
66841056Swilliam 	case RECAL:
66941056Swilliam 		if ((stat = inb(wdc+wd_altsts)) & WDCS_ERR) {
67041056Swilliam 			printf("wd%d: recal", du->dk_unit);
67141056Swilliam 			if (unit == 0) {
67241056Swilliam 				printf(": status %b error %b\n",
67341056Swilliam 					stat, WDCS_BITS,
67441056Swilliam 					inb(wdc+wd_error), WDERR_BITS);
67541056Swilliam 				if (++wdtab.b_errcnt < RETRIES)
67641056Swilliam 					goto tryagainrecal;
67741056Swilliam 			}
67841056Swilliam 			goto badopen;
67941056Swilliam 		}
68041056Swilliam 		wdtab.b_errcnt = 0;
68141056Swilliam 		if (ISRAWSTATE(du->dk_state)) {
68241056Swilliam 			du->dk_state = OPENRAW;
68341056Swilliam 			return(1);
68441056Swilliam 		}
68541056Swilliam retry:
68641056Swilliam #ifdef	WDDEBUG
68741056Swilliam 		dprintf(DDSK,"rdlabel ");
68841056Swilliam #endif
689*45550Sbill cyloffset=0;
69041056Swilliam 		/*
69141056Swilliam 		 * Read in sector 0 to get the pack label and geometry.
69241056Swilliam 		 */
69341056Swilliam 		outb(wdc+wd_precomp, 0xff);/* sometimes this is head bit 3 */
69441056Swilliam 		outb(wdc+wd_seccnt, 1);
69541056Swilliam 		outb(wdc+wd_sector, 1);
69641056Swilliam 		/*if (bp->b_dev == bootdev) {
69741056Swilliam 			(wdc+wd_cyl_lo = cyloffset & 0xff;
69841056Swilliam 			(wdc+wd_cyl_hi = cyloffset >> 8;
69941056Swilliam 		} else {
70041056Swilliam 			(wdc+wd_cyl_lo = 0;
70141056Swilliam 			(wdc+wd_cyl_hi = 0;
70241056Swilliam 		}*/
70341056Swilliam 		outb(wdc+wd_cyl_lo, (cyloffset & 0xff));
70441056Swilliam 		outb(wdc+wd_cyl_hi, (cyloffset >> 8));
70541056Swilliam 		outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
70641056Swilliam 		outb(wdc+wd_command, WDCC_READ);
70741056Swilliam 		du->dk_state = RDLABEL;
70841056Swilliam 		return(0);
70941056Swilliam 
71041056Swilliam 	case RDLABEL:
71141056Swilliam 		if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
71241056Swilliam 			if (++wdtab.b_errcnt < RETRIES)
71341056Swilliam 				goto retry;
71441056Swilliam 			printf("wd%d: read label", unit);
71541056Swilliam 			goto badopen;
71641056Swilliam 		}
71741056Swilliam 
71841056Swilliam 		insw(wdc+wd_data, bp->b_un.b_addr, 256);
71941056Swilliam 
72041056Swilliam 		if (((struct disklabel *)
72141056Swilliam 		    (bp->b_un.b_addr + LABELOFFSET))->dk_magic == DISKMAGIC) {
72241056Swilliam 		       du->dk_dd =
72341056Swilliam 			 * (struct disklabel *) (bp->b_un.b_addr + LABELOFFSET);
72441056Swilliam 		} else {
72541056Swilliam 			printf("wd%d: bad disk label\n", du->dk_unit);
72641056Swilliam 			du->dk_state = OPENRAW;
72741056Swilliam 		}
72841056Swilliam 		if (du->dk_state == RDLABEL)
72941056Swilliam 			du->dk_state = RDBADTBL;
73041056Swilliam 		/*
73141056Swilliam 		 * The rest of the initialization can be done
73241056Swilliam 		 * by normal means.
73341056Swilliam 		 */
73441056Swilliam 		return(1);
73541056Swilliam 
73641056Swilliam 	default:
73741056Swilliam 		panic("wdcontrol %x", du->dk_state );
73841056Swilliam 	}
73941056Swilliam 	/* NOTREACHED */
74041056Swilliam 
74141056Swilliam badopen:
74241056Swilliam 	printf(": status %b error %b\n",
74341056Swilliam 		stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
74441056Swilliam 	du->dk_state = OPENRAW;
74541056Swilliam 	return(1);
74641056Swilliam }
74741056Swilliam 
74841056Swilliam wdclose(dev)
74941056Swilliam 	dev_t dev;
75041056Swilliam {	struct disk *du;
75141056Swilliam 
75241056Swilliam 	du = &wddrives[WDUNIT(dev)];
75341056Swilliam 	du->dk_open-- ;
75441056Swilliam 	/*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */
75541056Swilliam }
75641056Swilliam 
75741056Swilliam wdioctl(dev,cmd,addr,flag)
75841056Swilliam 	dev_t dev;
75941056Swilliam 	caddr_t addr;
76041056Swilliam {
76141056Swilliam 	int unit = WDUNIT(dev);
76241056Swilliam 	register struct disk *du;
76341056Swilliam 	int error = 0;
76441056Swilliam 	struct uio auio;
76541056Swilliam 	struct iovec aiov;
76641056Swilliam 	/*int wdformat();*/
76741056Swilliam 
76841056Swilliam 	du = &wddrives[unit];
76941056Swilliam 
77041056Swilliam 	switch (cmd) {
77141056Swilliam 
77241056Swilliam 	case DIOCGDINFO:
77341056Swilliam 		*(struct disklabel *)addr = du->dk_dd;
77441056Swilliam 		break;
77541056Swilliam 
77641056Swilliam 	case DIOCGDINFOP:
77741056Swilliam 		*(struct disklabel **)addr = &(du->dk_dd);
77841056Swilliam 		break;
77941056Swilliam 
78041056Swilliam #ifdef notyet
78141056Swilliam 	case DIOCWFORMAT:
78241056Swilliam 		if ((flag & FWRITE) == 0)
78341056Swilliam 			error = EBADF;
78441056Swilliam 		else {
78541056Swilliam 			register struct format_op *fop;
78641056Swilliam 
78741056Swilliam 			fop = (struct format_op *)addr;
78841056Swilliam 			aiov.iov_base = fop->df_buf;
78941056Swilliam 			aiov.iov_len = fop->df_count;
79041056Swilliam 			auio.uio_iov = &aiov;
79141056Swilliam 			auio.uio_iovcnt = 1;
79241056Swilliam 			auio.uio_resid = fop->df_count;
79341056Swilliam 			auio.uio_segflg = 0;
79441056Swilliam 			auio.uio_offset =
79541056Swilliam 				fop->df_startblk * du->dk_dd.dk_secsize;
79641056Swilliam 			error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE,
79741056Swilliam 				minphys, &auio);
79841056Swilliam 			fop->df_count -= auio.uio_resid;
79941056Swilliam 			fop->df_reg[0] = du->dk_status;
80041056Swilliam 			fop->df_reg[1] = du->dk_error;
80141056Swilliam 		}
80241056Swilliam 		break;
80341056Swilliam #endif
80441056Swilliam 
80541056Swilliam 	default:
80641056Swilliam 		error = ENOTTY;
80741056Swilliam 		break;
80841056Swilliam 	}
80941056Swilliam 	return (error);
81041056Swilliam }
81141056Swilliam 
81241056Swilliam /*wdformat(bp)
81341056Swilliam 	struct buf *bp;
81441056Swilliam {
81541056Swilliam 
81641056Swilliam 	bp->b_flags |= B_FORMAT;
81741056Swilliam 	return (wdstrategy(bp));
81841056Swilliam }*/
81941056Swilliam 
82041056Swilliam /*
82141056Swilliam  * Routines to do raw IO for a unit.
82241056Swilliam  */
82341056Swilliam wdread(dev, uio)			/* character read routine */
82441056Swilliam 	dev_t dev;
82541056Swilliam 	struct uio *uio;
82641056Swilliam {
82741056Swilliam 	int unit = WDUNIT(dev) ;
82841056Swilliam 
82941056Swilliam 	if (unit >= NWD) return(ENXIO);
83041056Swilliam 	return(physio(wdstrategy, &rwdbuf[unit], dev, B_READ, minphys, uio));
83141056Swilliam }
83241056Swilliam 
83341056Swilliam 
83441056Swilliam wdwrite(dev, uio)			/* character write routine */
83541056Swilliam 	dev_t dev;
83641056Swilliam 	struct uio *uio;
83741056Swilliam {
83841056Swilliam 	int unit = WDUNIT(dev) ;
83941056Swilliam 
84041056Swilliam 	if (unit >= NWD) return(ENXIO);
84141056Swilliam 	return(physio(wdstrategy, &rwdbuf[unit], dev, B_WRITE, minphys, uio));
84241056Swilliam }
84341056Swilliam 
84441056Swilliam wdsize(dev)
84541056Swilliam 	dev_t dev;
84641056Swilliam {
84741056Swilliam 	register unit = WDUNIT(dev) ;
84841056Swilliam 	register xunit = minor(dev) & 07;
84941056Swilliam 	register struct disk *du;
85041056Swilliam 	register val ;
85141056Swilliam 
852*45550Sbill 	return(21600);
85341056Swilliam #ifdef notdef
85441056Swilliam 	if (unit >= NWD) return(-1);
85541056Swilliam 	if (wddrives[unit].dk_state == 0) /*{
85641056Swilliam 		val = wdopen (dev, 0) ;
85741056Swilliam 		if (val < 0) return (val) ;
85841056Swilliam 	}*/	return (-1) ;
85941056Swilliam 	du = &wddrives[unit];
86041056Swilliam 	return((int)((u_long)du->dk_dd.dk_partition[xunit].nblocks *
86141056Swilliam 		du->dk_dd.dk_secsize / 512));
86241056Swilliam #endif
86341056Swilliam }
86441056Swilliam 
86541056Swilliam wddump(dev)			/* dump core after a system crash */
86641056Swilliam 	dev_t dev;
86741056Swilliam {
86841056Swilliam #ifdef notyet
86941056Swilliam 	register struct disk *du;	/* disk unit to do the IO */
87041056Swilliam 	register struct wd1010 *wdp = (struct wd1010 *) VA_WD;
87141056Swilliam 	register struct bt_bad *bt_ptr;
87241056Swilliam 	long	num;			/* number of sectors to write */
87341056Swilliam 	int	unit, xunit;
87441056Swilliam 	long	cyloff, blknum, blkcnt;
87541056Swilliam 	long	cylin, head, sector;
87641056Swilliam 	long	secpertrk, secpercyl, nblocks, i;
87741056Swilliam 	register char *addr;
87841056Swilliam 	char	*end;
87941056Swilliam 	extern	int dumplo, totalclusters;
88041056Swilliam 	static  wddoingadump = 0 ;
88141056Swilliam 
88241056Swilliam 	addr = (char *) PA_RAM;		/* starting address */
88341056Swilliam 	/* size of memory to dump */
88441056Swilliam 	num = totalclusters * CLSIZE - PA_RAM / PGSIZE;
88541056Swilliam 	unit = WDUNIT(dev) ;		/* eventually support floppies? */
88641056Swilliam 	xunit = minor(dev) & 7;		/* file system */
88741056Swilliam 	/* check for acceptable drive number */
88841056Swilliam 	if (unit >= NWD) return(ENXIO);
88941056Swilliam 
89041056Swilliam 	du = &wddrives[unit];
89141056Swilliam 	/* was it ever initialized ? */
89241056Swilliam 	if (du->dk_state < OPEN) return (ENXIO) ;
89341056Swilliam 
89441056Swilliam 	/* Convert to disk sectors */
89541056Swilliam 	num = (u_long) num * PGSIZE / du->dk_dd.dk_secsize;
89641056Swilliam 
89741056Swilliam 	/* check if controller active */
89841056Swilliam 	/*if (wdtab.b_active) return(EFAULT); */
89941056Swilliam 	if (wddoingadump) return(EFAULT);
90041056Swilliam 
90141056Swilliam 	secpertrk = du->dk_dd.dk_nsectors;
90241056Swilliam 	secpercyl = du->dk_dd.dk_secpercyl;
90341056Swilliam 	nblocks = du->dk_dd.dk_partition[xunit].nblocks;
90441056Swilliam 	cyloff = du->dk_dd.dk_partition[xunit].cyloff;
90541056Swilliam 
90641056Swilliam 	/* check transfer bounds against partition size */
90741056Swilliam 	if ((dumplo < 0) || ((dumplo + num) >= nblocks))
90841056Swilliam 		return(EINVAL);
90941056Swilliam 
91041056Swilliam 	/*wdtab.b_active = 1;		/* mark controller active for if we
91141056Swilliam 					   panic during the dump */
91241056Swilliam 	wddoingadump = 1  ;  i = 100000 ;
91341056Swilliam 	while ((wdp->wd_status & WDCS_BUSY) && (i-- > 0)) nulldev() ;
91441056Swilliam 	inb(wdc+wd_sdh = du->dk_sdh ;
91541056Swilliam 	inb(wdc+wd_command = WDCC_RESTORE | WD_STEP;
91641056Swilliam 	while (inb(wdc+wd_status & WDCS_BUSY) nulldev() ;
91741056Swilliam 
91841056Swilliam 	blknum = dumplo;
91941056Swilliam 	while (num > 0) {
92041056Swilliam #ifdef notdef
92141056Swilliam 		if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER;
92241056Swilliam 		if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
92341056Swilliam 			blkcnt = secpercyl - (blknum % secpercyl);
92441056Swilliam 			    /* keep transfer within current cylinder */
92541056Swilliam #endif
92641056Swilliam 
92741056Swilliam 		/* compute disk address */
92841056Swilliam 		cylin = blknum / secpercyl;
92941056Swilliam 		head = (blknum % secpercyl) / secpertrk;
93041056Swilliam 		sector = blknum % secpertrk;
93141056Swilliam 		sector++;		/* origin 1 */
93243592Sdonahn 		cylin += cyloff;
93341056Swilliam 
93441056Swilliam 		/*
93541056Swilliam 		 * See if the current block is in the bad block list.
93641056Swilliam 		 * (If we have one.)
93741056Swilliam 		 */
93841056Swilliam 	    		for (bt_ptr = dkbad[unit].bt_bad;
93941056Swilliam 				bt_ptr->bt_cyl != -1; bt_ptr++) {
94041056Swilliam 			if (bt_ptr->bt_cyl > cylin)
94141056Swilliam 				/* Sorted list, and we passed our cylinder.
94241056Swilliam 					quit. */
94341056Swilliam 				break;
94441056Swilliam 			if (bt_ptr->bt_cyl == cylin &&
94541056Swilliam 				bt_ptr->bt_trksec == (head << 8) + sector) {
94641056Swilliam 			/*
94741056Swilliam 			 * Found bad block.  Calculate new block addr.
94841056Swilliam 			 * This starts at the end of the disk (skip the
94941056Swilliam 			 * last track which is used for the bad block list),
95041056Swilliam 			 * and works backwards to the front of the disk.
95141056Swilliam 			 */
95241056Swilliam 				blknum = (du->dk_dd.dk_secperunit)
95341056Swilliam 					- du->dk_dd.dk_nsectors
95441056Swilliam 					- (bt_ptr - dkbad[unit].bt_bad) - 1;
95541056Swilliam 				cylin = blknum / secpercyl;
95641056Swilliam 				head = (blknum % secpercyl) / secpertrk;
95741056Swilliam 				sector = blknum % secpertrk;
95841056Swilliam 				break;
95941056Swilliam 			}
96041056Swilliam 
96141056Swilliam 		/* select drive.     */
96241056Swilliam 		inb(wdc+wd_sdh = du->dk_sdh | (head&07);
96341056Swilliam 		while ((inb(wdc+wd_status & WDCS_READY) == 0) nulldev();
96441056Swilliam 
96541056Swilliam 		/* transfer some blocks */
96641056Swilliam 		inb(wdc+wd_sector = sector;
96741056Swilliam 		inb(wdc+wd_seccnt = 1;
96841056Swilliam 		inb(wdc+wd_cyl_lo = cylin;
96941056Swilliam 		if (du->dk_dd.dk_ntracks > 8) {
97041056Swilliam 			if (head > 7)
97141056Swilliam 				inb(wdc+wd_precomp = 0;	/* set 3rd head bit */
97241056Swilliam 			else
97341056Swilliam 				inb(wdc+wd_precomp = 0xff;	/* set 3rd head bit */
97441056Swilliam 		}
97541056Swilliam 		inb(wdc+wd_cyl_hi = cylin >> 8;
97641056Swilliam #ifdef notdef
97741056Swilliam 		/* lets just talk about this first...*/
97841056Swilliam 		printf ("sdh 0%o sector %d cyl %d addr 0x%x\n",
97941056Swilliam 			wdp->wd_sdh, wdp->wd_sector,
98041056Swilliam 			wdp->wd_cyl_hi*256+wdp->wd_cyl_lo, addr) ;
98141056Swilliam 		for (i=10000; i > 0 ; i--)
98241056Swilliam 			;
98341056Swilliam 		continue;
98441056Swilliam #endif
98541056Swilliam 		inb(wdc+wd_command = WDCC_WRITE;
98641056Swilliam 
98741056Swilliam 		/* Ready to send data?	*/
98841056Swilliam 		while ((inb(wdc+wd_status & WDCS_DRQ) == 0) nulldev();
98941056Swilliam 		if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ;
99041056Swilliam 
99141056Swilliam 		end = (char *)addr + du->dk_dd.dk_secsize;
99241056Swilliam 		for (; addr < end; addr += 8) {
99341056Swilliam 			wdp->wd_data = addr[0];
99441056Swilliam 			wdp->wd_data = addr[1];
99541056Swilliam 			wdp->wd_data = addr[2];
99641056Swilliam 			wdp->wd_data = addr[3];
99741056Swilliam 			wdp->wd_data = addr[4];
99841056Swilliam 			wdp->wd_data = addr[5];
99941056Swilliam 			wdp->wd_data = addr[6];
100041056Swilliam 			wdp->wd_data = addr[7];
100141056Swilliam 		}
100241056Swilliam 		if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ;
100341056Swilliam 		/* Check data request (should be done).         */
100441056Swilliam 		if (inb(wdc+wd_status & WDCS_DRQ) return(EIO) ;
100541056Swilliam 
100641056Swilliam 		/* wait for completion */
100741056Swilliam 		for ( i = 1000000 ; inb(wdc+wd_status & WDCS_BUSY ; i--) {
100841056Swilliam 				if (i < 0) return (EIO) ;
100941056Swilliam 				nulldev () ;
101041056Swilliam 		}
101141056Swilliam 		/* error check the xfer */
101241056Swilliam 		if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ;
101341056Swilliam 		/* update block count */
101441056Swilliam 		num--;
101541056Swilliam 		blknum++ ;
101641056Swilliam #ifdef	WDDEBUG
101741056Swilliam if (num % 100 == 0) printf(".") ;
101841056Swilliam #endif
101941056Swilliam 	}
102041056Swilliam 	return(0);
102141056Swilliam #endif
102241056Swilliam }
102341056Swilliam #endif
1024