xref: /csrg-svn/sys/i386/isa/wd.c (revision 49568)
1*49568Swilliam /* TODO:peel out buffer at low ipl,
2*49568Swilliam    speed improvement, rewrite to clean code from garbage artifacts */
3*49568Swilliam 
441056Swilliam /*-
541056Swilliam  * Copyright (c) 1990 The Regents of the University of California.
641056Swilliam  * All rights reserved.
741056Swilliam  *
841056Swilliam  * This code is derived from software contributed to Berkeley by
941056Swilliam  * William Jolitz.
1041056Swilliam  *
11*49568Swilliam  * %sccs.include.redist%
1241056Swilliam  *
13*49568Swilliam  *	@(#)wd.c	7.1 (Berkeley) 05/09/91
1441056Swilliam  */
1543592Sdonahn 
1641056Swilliam #include "wd.h"
1741056Swilliam #if	NWD > 0
1841056Swilliam 
1941056Swilliam #include "param.h"
2041056Swilliam #include "dkbad.h"
2141056Swilliam #include "systm.h"
2241056Swilliam #include "conf.h"
2341056Swilliam #include "file.h"
24*49568Swilliam #include "stat.h"
2541056Swilliam #include "ioctl.h"
26*49568Swilliam #include "disklabel.h"
2741056Swilliam #include "buf.h"
2841056Swilliam #include "uio.h"
29*49568Swilliam #include "i386/isa/isa_device.h"
30*49568Swilliam #include "i386/isa/icu.h"
31*49568Swilliam #include "i386/isa/wdreg.h"
3241056Swilliam #include "syslog.h"
33*49568Swilliam #include "vm/vm.h"
3441056Swilliam 
3541056Swilliam #define	RETRIES		5	/* number of retries before giving up */
36*49568Swilliam #define	MAXTRANSFER	32	/* max size of transfer in page clusters */
3741056Swilliam 
38*49568Swilliam #define wdctlr(dev)	((minor(dev) & 0x80) >> 7)
39*49568Swilliam #define wdunit(dev)	((minor(dev) & 0x60) >> 5)
40*49568Swilliam #define wdpart(dev)	((minor(dev) & 0x1f))
4141056Swilliam 
4241056Swilliam #define b_cylin	b_resid		/* cylinder number for doing IO to */
4341056Swilliam 				/* shares an entry in the buf struct */
4441056Swilliam 
4541056Swilliam /*
4641056Swilliam  * Drive states.  Used for open and format operations.
4741056Swilliam  * States < OPEN (> 0) are transient, during an open operation.
4841056Swilliam  * OPENRAW is used for unlabeled disks, and for floppies, to inhibit
4941056Swilliam  * bad-sector forwarding.
5041056Swilliam  */
5141056Swilliam #define RAWDISK		8		/* raw disk operation, no translation*/
5241056Swilliam #define ISRAWSTATE(s)	(RAWDISK&(s))	/* are we in a raw state? */
5341056Swilliam #define DISKSTATE(s)	(~RAWDISK&(s))	/* are we in a given state regardless
5441056Swilliam 					   of raw or cooked mode? */
5541056Swilliam 
5641056Swilliam #define	CLOSED		0		/* disk is closed. */
5741056Swilliam 					/* "cooked" disk states */
5841056Swilliam #define	WANTOPEN	1		/* open requested, not started */
5941056Swilliam #define	RECAL		2		/* doing restore */
6041056Swilliam #define	RDLABEL		3		/* reading pack label */
6141056Swilliam #define	RDBADTBL	4		/* reading bad-sector table */
6241056Swilliam #define	OPEN		5		/* done with open */
6341056Swilliam 
6441056Swilliam #define	WANTOPENRAW	(WANTOPEN|RAWDISK)	/* raw WANTOPEN */
6541056Swilliam #define	RECALRAW	(RECAL|RAWDISK)	/* raw open, doing restore */
6641056Swilliam #define	OPENRAW		(OPEN|RAWDISK)	/* open, but unlabeled disk or floppy */
6741056Swilliam 
6841056Swilliam 
6941056Swilliam /*
7041056Swilliam  * The structure of a disk drive.
7141056Swilliam  */
7241056Swilliam struct	disk {
73*49568Swilliam 	struct disklabel dk_dd;	/* device configuration data */
74*49568Swilliam 	long	dk_bc;		/* byte count left */
75*49568Swilliam 	short	dk_skip;	/* blocks already transferred */
76*49568Swilliam 	char	dk_unit;	/* physical unit number */
77*49568Swilliam 	char	dk_state;	/* control state */
78*49568Swilliam 	u_char	dk_status;	/* copy of status reg. */
79*49568Swilliam 	u_char	dk_error;	/* copy of error reg. */
80*49568Swilliam 	short	dk_open;	/* open/closed refcnt */
81*49568Swilliam         u_long  dk_copenpart;   /* character units open on this drive */
82*49568Swilliam         u_long  dk_bopenpart;   /* block units open on this drive */
83*49568Swilliam         u_long  dk_openpart;    /* all units open on this drive */
84*49568Swilliam 	short	dk_wlabel;	/* label writable? */
8541056Swilliam };
8641056Swilliam 
8741056Swilliam /*
8841056Swilliam  * This label is used as a default when initializing a new or raw disk.
8941056Swilliam  * It really only lets us access the first track until we know more.
9041056Swilliam  */
9141056Swilliam struct disklabel dflt_sizes = {
92*49568Swilliam 	DISKMAGIC, DTYPE_ST506, 0, "default", "",
9341056Swilliam 		512,		/* sector size */
94*49568Swilliam 		17,		/* # of sectors per track */
95*49568Swilliam 		8,		/* # of tracks per cylinder */
96*49568Swilliam 		766,		/* # of cylinders per unit */
97*49568Swilliam 		17*8,		/* # of sectors per cylinder */
98*49568Swilliam 		766*8*17,	/* # of sectors per unit */
99*49568Swilliam 		0,		/* # of spare sectors per track */
100*49568Swilliam 		0,		/* # of spare sectors per cylinder */
101*49568Swilliam 		0,		/* # of alt. cylinders per unit */
102*49568Swilliam 		3600,		/* rotational speed */
103*49568Swilliam 		1,		/* hardware sector interleave */
104*49568Swilliam 		0,		/* sector 0 skew, per track */
105*49568Swilliam 		0,		/* sector 0 skew, per cylinder */
106*49568Swilliam 		0,		/* head switch time, usec */
107*49568Swilliam 		0,		/* track-to-track seek, usec */
108*49568Swilliam 		0,		/* generic flags */
109*49568Swilliam 		0,0,0,0,0,
110*49568Swilliam 		0,0,0,0,0,
111*49568Swilliam 		DISKMAGIC,
112*49568Swilliam 		0,
113*49568Swilliam 		8,
114*49568Swilliam 		8192,
115*49568Swilliam 		8192,
116*49568Swilliam 
117*49568Swilliam 	{{21600,	0, 0,0,0,0},	/* A=root filesystem */
118*49568Swilliam 	{21600,	40, 0,0,0,0},
119*49568Swilliam 	{660890, 0, 0,0,0,0},	/* C=whole disk */
120*49568Swilliam 	{216000,	80, 0,0,0,0},
121*49568Swilliam 	{0,	0, 0,0,0,0},
122*49568Swilliam 	{0,	0, 0,0,0,0},
123*49568Swilliam 	{0,	0, 0,0,0,0},
124*49568Swilliam 	{399600,	480, 0,0,0,0}}
12541056Swilliam };
12645550Sbill 
12741056Swilliam static	struct	dkbad	dkbad[NWD];
12841056Swilliam struct	disk	wddrives[NWD] = {0};	/* table of units */
12941056Swilliam struct	buf	wdtab = {0};
13041056Swilliam struct	buf	wdutab[NWD] = {0};	/* head of queue per drive */
13141056Swilliam struct	buf	rwdbuf[NWD] = {0};	/* buffers for raw IO */
13241056Swilliam long	wdxfer[NWD] = {0};		/* count of transfers */
13341056Swilliam int	writeprotected[NWD] = { 0 };
13441056Swilliam int	wdprobe(), wdattach(), wdintr();
13545552Sbill struct	isa_driver wddriver = {
13641056Swilliam 	wdprobe, wdattach, "wd",
13741056Swilliam };
13841056Swilliam 
13945552Sbill static wdc;
14041056Swilliam /*
14141056Swilliam  * Probe routine
14241056Swilliam  */
14341056Swilliam wdprobe(dvp)
14445552Sbill 	struct isa_device *dvp;
14541056Swilliam {
14645552Sbill wdc = dvp->id_iobase;
14741056Swilliam 
14841056Swilliam #ifdef lint
14941056Swilliam 	wdintr(0);
15041056Swilliam #endif
151*49568Swilliam 	/* XXX sorry, needs to be better */
15241056Swilliam 	outb(wdc+wd_error, 0x5a) ;	/* error register not writable */
15341056Swilliam 	outb(wdc+wd_cyl_lo, 0xa5) ;	/* but all of cyllo are implemented */
154*49568Swilliam 	if(inb(wdc+wd_error) != 0x5a && inb(wdc+wd_cyl_lo) == 0xa5)
15541056Swilliam 		return(1) ;
15641056Swilliam 	return (0);
15741056Swilliam }
15841056Swilliam 
15941056Swilliam /*
16041056Swilliam  * attach each drive if possible.
16141056Swilliam  */
16241056Swilliam wdattach(dvp)
16345552Sbill 	struct isa_device *dvp;
16441056Swilliam {
16545552Sbill 	int unit = dvp->id_unit;
16641056Swilliam 
16745552Sbill 	outb(wdc+wd_ctlr,12);
16845552Sbill 	DELAY(1000);
16945552Sbill 	outb(wdc+wd_ctlr,8);
17041056Swilliam }
17141056Swilliam 
17241056Swilliam /* Read/write routine for a buffer.  Finds the proper unit, range checks
17341056Swilliam  * arguments, and schedules the transfer.  Does not wait for the transfer
17441056Swilliam  * to complete.  Multi-page transfers are supported.  All I/O requests must
17541056Swilliam  * be a multiple of a sector in length.
17641056Swilliam  */
17741056Swilliam wdstrategy(bp)
17841056Swilliam 	register struct buf *bp;	/* IO operation to perform */
17941056Swilliam {
18041056Swilliam 	register struct buf *dp;
18141056Swilliam 	register struct disk *du;	/* Disk unit to do the IO.	*/
182*49568Swilliam 	register struct partition *p;
183*49568Swilliam 	long maxsz, sz;
184*49568Swilliam 	int	unit = wdunit(bp->b_dev);
18541056Swilliam 	int	s;
18641056Swilliam 
18741056Swilliam 	if ((unit >= NWD) || (bp->b_blkno < 0)) {
18843592Sdonahn 		printf("wdstrat: unit = %d, blkno = %d, bcount = %d\n",
18941056Swilliam 			unit, bp->b_blkno, bp->b_bcount);
19043592Sdonahn 		pg("wd:error in wdstrategy");
19141056Swilliam 		bp->b_flags |= B_ERROR;
19241056Swilliam 		goto bad;
19341056Swilliam 	}
19441056Swilliam 	if (writeprotected[unit] && (bp->b_flags & B_READ) == 0) {
19541056Swilliam 		printf("wd%d: write protected\n", unit);
19641056Swilliam 		goto bad;
19741056Swilliam 	}
19841056Swilliam 	du = &wddrives[unit];
19941056Swilliam 	if (DISKSTATE(du->dk_state) != OPEN)
20041056Swilliam 		goto q;
201*49568Swilliam #ifdef old
20241056Swilliam 	/*
20341056Swilliam 	 * Convert DEV_BSIZE "blocks" to sectors.
20441056Swilliam 	 * Note: doing the conversions this way limits the partition size
20541056Swilliam 	 * to about 8 million sectors (1-8 Gb).
20641056Swilliam 	 */
207*49568Swilliam 	blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize;
208*49568Swilliam 	if (((u_long) bp->b_blkno * DEV_BSIZE % du->dk_dd.d_secsize != 0) ||
20943592Sdonahn 	    bp->b_bcount >= MAXTRANSFER * CLBYTES) {
21041056Swilliam 		bp->b_flags |= B_ERROR;
21141056Swilliam 		goto bad;
21241056Swilliam 	}
213*49568Swilliam 	nblocks = du->dk_dd.d_partitions[part].p_size;
214*49568Swilliam 	cyloff = du->dk_dd.d_partitions[part].p_offset;
215*49568Swilliam 	if (blknum + (bp->b_bcount / du->dk_dd.d_secsize) > nblocks) {
21641056Swilliam 		if (blknum == nblocks)
21741056Swilliam 			bp->b_resid = bp->b_bcount;
21841056Swilliam 		else
21941056Swilliam 			bp->b_flags |= B_ERROR;
22041056Swilliam 		goto bad;
22141056Swilliam 	}
222*49568Swilliam 	bp->b_cylin = blknum / du->dk_dd.d_secpercyl + cyloff;
223*49568Swilliam #else
224*49568Swilliam         /*
225*49568Swilliam          * Determine the size of the transfer, and make sure it is
226*49568Swilliam          * within the boundaries of the partition.
227*49568Swilliam          */
228*49568Swilliam         p = &du->dk_dd.d_partitions[wdpart(bp->b_dev)];
229*49568Swilliam         maxsz = p->p_size;
230*49568Swilliam         sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
231*49568Swilliam         if (bp->b_blkno + p->p_offset <= LABELSECTOR &&
232*49568Swilliam #if LABELSECTOR != 0
233*49568Swilliam             bp->b_blkno + p->p_offset + sz > LABELSECTOR &&
234*49568Swilliam #endif
235*49568Swilliam             (bp->b_flags & B_READ) == 0 && du->dk_wlabel == 0) {
236*49568Swilliam                 bp->b_error = EROFS;
237*49568Swilliam                 goto bad;
238*49568Swilliam         }
239*49568Swilliam         if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) {
240*49568Swilliam                 /* if exactly at end of disk, return an EOF */
241*49568Swilliam                 if (bp->b_blkno == maxsz) {
242*49568Swilliam                         bp->b_resid = bp->b_bcount;
243*49568Swilliam                         biodone(bp);
244*49568Swilliam                         return;
245*49568Swilliam                 }
246*49568Swilliam                 /* or truncate if part of it fits */
247*49568Swilliam                 sz = maxsz - bp->b_blkno;
248*49568Swilliam                 if (sz <= 0)
249*49568Swilliam                         goto bad;
250*49568Swilliam                 bp->b_bcount = sz << DEV_BSHIFT;
251*49568Swilliam         }
252*49568Swilliam         bp->b_cylin = (bp->b_blkno + p->p_offset) / du->dk_dd.d_secpercyl;
253*49568Swilliam #endif
25441056Swilliam q:
25541056Swilliam 	dp = &wdutab[unit];
25645550Sbill 	s = splhigh();
25741056Swilliam 	disksort(dp, bp);
25841056Swilliam 	if (dp->b_active == 0)
25941056Swilliam 		wdustart(du);		/* start drive if idle */
26041056Swilliam 	if (wdtab.b_active == 0)
26141056Swilliam 		wdstart(s);		/* start IO if controller idle */
26241056Swilliam 	splx(s);
26341056Swilliam 	return;
26441056Swilliam 
26541056Swilliam bad:
26641056Swilliam 	bp->b_error = EINVAL;
26741056Swilliam 	biodone(bp);
26841056Swilliam }
26941056Swilliam 
27041056Swilliam /* Routine to queue a read or write command to the controller.  The request is
27141056Swilliam  * linked into the active list for the controller.  If the controller is idle,
27241056Swilliam  * the transfer is started.
27341056Swilliam  */
27441056Swilliam wdustart(du)
27541056Swilliam 	register struct disk *du;
27641056Swilliam {
27741056Swilliam 	register struct buf *bp, *dp;
27841056Swilliam 
27941056Swilliam 	dp = &wdutab[du->dk_unit];
28041056Swilliam 	if (dp->b_active)
28141056Swilliam 		return;
28241056Swilliam 	bp = dp->b_actf;
28341056Swilliam 	if (bp == NULL)
28441056Swilliam 		return;
28541056Swilliam 	dp->b_forw = NULL;
28641056Swilliam 	if (wdtab.b_actf  == NULL)		/* link unit into active list */
28741056Swilliam 		wdtab.b_actf = dp;
28841056Swilliam 	else
28941056Swilliam 		wdtab.b_actl->b_forw = dp;
29041056Swilliam 	wdtab.b_actl = dp;
29141056Swilliam 	dp->b_active = 1;		/* mark the drive as busy */
29241056Swilliam }
29341056Swilliam 
29441056Swilliam /*
29541056Swilliam  * Controller startup routine.  This does the calculation, and starts
29641056Swilliam  * a single-sector read or write operation.  Called to start a transfer,
29741056Swilliam  * or from the interrupt routine to continue a multi-sector transfer.
29841056Swilliam  * RESTRICTIONS:
29941056Swilliam  * 1.	The transfer length must be an exact multiple of the sector size.
30041056Swilliam  */
30141056Swilliam 
30245550Sbill static wd_sebyse;
30345550Sbill 
30441056Swilliam wdstart()
30541056Swilliam {
30641056Swilliam 	register struct disk *du;	/* disk unit for IO */
30741056Swilliam 	register struct buf *bp;
30841056Swilliam 	struct buf *dp;
30941056Swilliam 	register struct bt_bad *bt_ptr;
31041056Swilliam 	long	blknum, pagcnt, cylin, head, sector;
31141056Swilliam 	long	secpertrk, secpercyl, addr, i;
312*49568Swilliam 	int	unit, s;
31341056Swilliam 
31441056Swilliam loop:
31541056Swilliam 	dp = wdtab.b_actf;
31641056Swilliam 	if (dp == NULL)
31741056Swilliam 		return;
31841056Swilliam 	bp = dp->b_actf;
31941056Swilliam 	if (bp == NULL) {
32041056Swilliam 		wdtab.b_actf = dp->b_forw;
32141056Swilliam 		goto loop;
32241056Swilliam 	}
323*49568Swilliam 	unit = wdunit(bp->b_dev);
32441056Swilliam 	du = &wddrives[unit];
32541056Swilliam 	if (DISKSTATE(du->dk_state) <= RDLABEL) {
32641056Swilliam 		if (wdcontrol(bp)) {
32741056Swilliam 			dp->b_actf = bp->av_forw;
32841056Swilliam 			goto loop;	/* done */
32941056Swilliam 		}
33041056Swilliam 		return;
33141056Swilliam 	}
332*49568Swilliam 	secpertrk = du->dk_dd.d_nsectors;
333*49568Swilliam 	secpercyl = du->dk_dd.d_secpercyl;
33441056Swilliam 	/*
33541056Swilliam 	 * Convert DEV_BSIZE "blocks" to sectors.
33641056Swilliam 	 */
337*49568Swilliam 	blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize
33841056Swilliam 		+ du->dk_skip;
33941056Swilliam #ifdef	WDDEBUG
34041056Swilliam 	if (du->dk_skip == 0) {
34141056Swilliam 		dprintf(DDSK,"\nwdstart %d: %s %d@%d; map ", unit,
34241056Swilliam 			(bp->b_flags & B_READ) ? "read" : "write",
34341056Swilliam 			bp->b_bcount, blknum);
34441056Swilliam 	} else {
34545550Sbill 		dprintf(DDSK," %d)%x", du->dk_skip, inb(wdc+wd_altsts));
34641056Swilliam 	}
34741056Swilliam #endif
34841056Swilliam 
34941056Swilliam 	addr = (int) bp->b_un.b_addr;
35041056Swilliam 	if(du->dk_skip==0) du->dk_bc = bp->b_bcount;
35141056Swilliam 	cylin = blknum / secpercyl;
35241056Swilliam 	head = (blknum % secpercyl) / secpertrk;
35345550Sbill 	sector = blknum % secpertrk;
35441056Swilliam 	if (DISKSTATE(du->dk_state) == OPEN)
355*49568Swilliam 		cylin += du->dk_dd.d_partitions[wdpart(bp->b_dev)].p_offset
356*49568Swilliam 				/ secpercyl;
35741056Swilliam 
35841056Swilliam 	/*
35941056Swilliam 	 * See if the current block is in the bad block list.
36041056Swilliam 	 * (If we have one, and not formatting.)
36141056Swilliam 	 */
36245550Sbill 	if (DISKSTATE(du->dk_state) == OPEN && wd_sebyse)
36341056Swilliam 	    for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
36441056Swilliam 		if (bt_ptr->bt_cyl > cylin)
36541056Swilliam 			/* Sorted list, and we passed our cylinder. quit. */
36641056Swilliam 			break;
36741056Swilliam 		if (bt_ptr->bt_cyl == cylin &&
36841056Swilliam 				bt_ptr->bt_trksec == (head << 8) + sector) {
36941056Swilliam 			/*
37041056Swilliam 			 * Found bad block.  Calculate new block addr.
37141056Swilliam 			 * This starts at the end of the disk (skip the
37241056Swilliam 			 * last track which is used for the bad block list),
37341056Swilliam 			 * and works backwards to the front of the disk.
37441056Swilliam 			 */
37541056Swilliam #ifdef	WDDEBUG
37641056Swilliam 			    dprintf(DDSK,"--- badblock code -> Old = %d; ",
37741056Swilliam 				blknum);
37841056Swilliam #endif
379*49568Swilliam 			blknum = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors
38041056Swilliam 				- (bt_ptr - dkbad[unit].bt_bad) - 1;
38141056Swilliam 			cylin = blknum / secpercyl;
38241056Swilliam 			head = (blknum % secpercyl) / secpertrk;
38341056Swilliam 			sector = blknum % secpertrk;
38441056Swilliam #ifdef	WDDEBUG
38541056Swilliam 			    dprintf(DDSK, "new = %d\n", blknum);
38641056Swilliam #endif
38741056Swilliam 			break;
38841056Swilliam 		}
38941056Swilliam 	}
39045550Sbill 	sector += 1;	/* sectors begin with 1, not 0 */
39141056Swilliam 
39241056Swilliam 	wdtab.b_active = 1;		/* mark controller active */
39341056Swilliam 
39445550Sbill 	if(du->dk_skip==0 || wd_sebyse) {
39545550Sbill 	if(wdtab.b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += 512;
39645550Sbill 	while ((inb(wdc+wd_status) & WDCS_BUSY) != 0) ;
39745550Sbill 	/*while ((inb(wdc+wd_status) & WDCS_DRQ)) inb(wdc+wd_data);*/
39841056Swilliam 	outb(wdc+wd_precomp, 0xff);
39941056Swilliam 	/*wr(wdc+wd_precomp, du->dk_dd.dk_precompcyl / 4);*/
40041056Swilliam 	/*if (bp->b_flags & B_FORMAT) {
40141056Swilliam 		wr(wdc+wd_sector, du->dk_dd.dk_gap3);
40241056Swilliam 		wr(wdc+wd_seccnt, du->dk_dd.dk_nsectors);
40341056Swilliam 	} else {*/
40445550Sbill 	if(wd_sebyse)
40545550Sbill 		outb(wdc+wd_seccnt, 1);
40645550Sbill 	else
40745550Sbill 		outb(wdc+wd_seccnt, ((du->dk_bc +511) / 512));
40841056Swilliam 	outb(wdc+wd_sector, sector);
40941056Swilliam 
41041056Swilliam 	outb(wdc+wd_cyl_lo, cylin);
41141056Swilliam 	outb(wdc+wd_cyl_hi, cylin >> 8);
41241056Swilliam 
41341056Swilliam 	/* Set up the SDH register (select drive).     */
41441056Swilliam 	outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
41545551Sbill 	while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
41641056Swilliam 
41741056Swilliam 	/*if (bp->b_flags & B_FORMAT)
41841056Swilliam 		wr(wdc+wd_command, WDCC_FORMAT);
41941056Swilliam 	else*/
42041056Swilliam 		outb(wdc+wd_command,
42141056Swilliam 			(bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
42241056Swilliam #ifdef	WDDEBUG
42345550Sbill 	dprintf(DDSK,"sector %d cylin %d head %d addr %x sts %x\n",
42445550Sbill 	    sector, cylin, head, addr, inb(wdc+wd_altsts));
42541056Swilliam #endif
42645550Sbill }
42741056Swilliam 
42841056Swilliam 	/* If this is a read operation, just go away until it's done.	*/
42941056Swilliam 	if (bp->b_flags & B_READ) return;
43041056Swilliam 
43141056Swilliam 	/* Ready to send data?	*/
432*49568Swilliam 	while ((inb(wdc+wd_status) & WDCS_DRQ) == 0);
43341056Swilliam 
43441056Swilliam 	/* ASSUMES CONTIGUOUS MEMORY */
43545550Sbill 	outsw (wdc+wd_data, addr+du->dk_skip*512, 256);
43641056Swilliam 	du->dk_bc -= 512;
43741056Swilliam }
43841056Swilliam 
43941056Swilliam /*
44041056Swilliam  * these are globally defined so they can be found
44141056Swilliam  * by the debugger easily in the case of a system crash
44241056Swilliam  */
44341056Swilliam daddr_t wd_errsector;
44441056Swilliam daddr_t wd_errbn;
44541056Swilliam unsigned char wd_errstat;
44641056Swilliam 
44741056Swilliam /* Interrupt routine for the controller.  Acknowledge the interrupt, check for
44841056Swilliam  * errors on the current operation, mark it done if necessary, and start
44941056Swilliam  * the next request.  Also check for a partially done transfer, and
45041056Swilliam  * continue with the next chunk if so.
45141056Swilliam  */
452*49568Swilliam wdintr(unit)
45341056Swilliam {
45441056Swilliam 	register struct	disk *du;
45541056Swilliam 	register struct buf *bp, *dp;
45641056Swilliam 	int status;
45741056Swilliam 	char partch ;
458*49568Swilliam 	static wd_haderror;
45941056Swilliam 
46041056Swilliam 	/* Shouldn't need this, but it may be a slow controller.	*/
461*49568Swilliam 	while ((status = inb(wdc+wd_status)) & WDCS_BUSY) ;
46241056Swilliam 	if (!wdtab.b_active) {
46341056Swilliam 		printf("wd: extra interrupt\n");
46441056Swilliam 		return;
46541056Swilliam 	}
46641056Swilliam 
46745549Sbill #ifdef	WDDEBUG
46841056Swilliam 	dprintf(DDSK,"I ");
46941056Swilliam #endif
47041056Swilliam 	dp = wdtab.b_actf;
47141056Swilliam 	bp = dp->b_actf;
472*49568Swilliam 	du = &wddrives[wdunit(bp->b_dev)];
473*49568Swilliam 	partch = wdpart(bp->b_dev) + 'a';
47441056Swilliam 	if (DISKSTATE(du->dk_state) <= RDLABEL) {
47541056Swilliam 		if (wdcontrol(bp))
47641056Swilliam 			goto done;
47741056Swilliam 		return;
47841056Swilliam 	}
47941056Swilliam 	if (status & (WDCS_ERR | WDCS_ECCCOR)) {
48045550Sbill 		wd_errstat = inb(wdc+wd_error);		/* save error status */
48141056Swilliam #ifdef	WDDEBUG
48245550Sbill 		printf("status %x error %x\n", status, wd_errstat);
48341056Swilliam #endif
48445550Sbill 		if(wd_sebyse == 0) {
48545550Sbill 			wd_haderror = 1;
48645550Sbill 			goto outt;
48745550Sbill 		}
48841056Swilliam 		/*if (bp->b_flags & B_FORMAT) {
48941056Swilliam 			du->dk_status = status;
49041056Swilliam 			du->dk_error = wdp->wd_error;
49141056Swilliam 			bp->b_flags |= B_ERROR;
49241056Swilliam 			goto done;
49341056Swilliam 		}*/
49441056Swilliam 
495*49568Swilliam 		wd_errsector = (bp->b_cylin * du->dk_dd.d_secpercyl) +
49641056Swilliam 			(((unsigned long) bp->b_blkno * DEV_BSIZE /
497*49568Swilliam 			    du->dk_dd.d_secsize) % du->dk_dd.d_secpercyl) +
49841056Swilliam 			du->dk_skip;
49941056Swilliam 		wd_errbn = bp->b_blkno
500*49568Swilliam 			+ du->dk_skip * du->dk_dd.d_secsize / DEV_BSIZE ;
50141056Swilliam 		if (status & WDCS_ERR) {
50245550Sbill 			if (++wdtab.b_errcnt < RETRIES) {
50341056Swilliam 				wdtab.b_active = 0;
50445550Sbill 			} else {
50541056Swilliam 				printf("wd%d%c: ", du->dk_unit, partch);
50641056Swilliam 				printf(
50741056Swilliam 				"hard %s error, sn %d bn %d status %b error %b\n",
50841056Swilliam 					(bp->b_flags & B_READ)? "read":"write",
50941056Swilliam 					wd_errsector, wd_errbn, status, WDCS_BITS,
51041056Swilliam 					wd_errstat, WDERR_BITS);
51141056Swilliam 				bp->b_flags |= B_ERROR;	/* flag the error */
51241056Swilliam 			}
51341056Swilliam 		} else
51441056Swilliam 			log(LOG_WARNING,"wd%d%c: soft ecc sn %d bn %d\n",
51541056Swilliam 				du->dk_unit, partch, wd_errsector,
51641056Swilliam 				wd_errbn);
51741056Swilliam 	}
51845550Sbill outt:
51941056Swilliam 
52041056Swilliam 	/*
52141056Swilliam 	 * If this was a successful read operation, fetch the data.
52241056Swilliam 	 */
52341056Swilliam 	if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) {
52441056Swilliam 		int chk, dummy;
52541056Swilliam 
52645550Sbill 		chk = min(256,du->dk_bc/2);
52741056Swilliam 		/* Ready to receive data?	*/
528*49568Swilliam 		while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
52941056Swilliam 
53041056Swilliam /*dprintf(DDSK,"addr %x\n", (int)bp->b_un.b_addr + du->dk_skip * 512);*/
53141056Swilliam 		insw(wdc+wd_data,(int)bp->b_un.b_addr + du->dk_skip * 512 ,chk);
53243592Sdonahn 		du->dk_bc -= 2*chk;
53345550Sbill 		while (chk++ < 256) insw (wdc+wd_data,&dummy,1);
53441056Swilliam 	}
53541056Swilliam 
53641056Swilliam 	wdxfer[du->dk_unit]++;
53741056Swilliam 	if (wdtab.b_active) {
53841056Swilliam 		if ((bp->b_flags & B_ERROR) == 0) {
53941056Swilliam 			du->dk_skip++;		/* Add to successful sectors. */
54041056Swilliam 			if (wdtab.b_errcnt) {
54141056Swilliam 				log(LOG_WARNING, "wd%d%c: ",
54241056Swilliam 						du->dk_unit, partch);
54341056Swilliam 				log(LOG_WARNING,
54441056Swilliam 			"soft %s error, sn %d bn %d error %b retries %d\n",
54541056Swilliam 				    (bp->b_flags & B_READ) ? "read" : "write",
54641056Swilliam 				    wd_errsector, wd_errbn, wd_errstat,
54741056Swilliam 				    WDERR_BITS, wdtab.b_errcnt);
54841056Swilliam 			}
54941056Swilliam 			wdtab.b_errcnt = 0;
55041056Swilliam 
55141056Swilliam 			/* see if more to transfer */
55243592Sdonahn 			/*if (du->dk_skip < (bp->b_bcount + 511) / 512) {*/
55345550Sbill 			if (du->dk_bc > 0 && wd_haderror == 0) {
55441056Swilliam 				wdstart();
55541056Swilliam 				return;		/* next chunk is started */
55645550Sbill 			} else if (wd_haderror && wd_sebyse == 0) {
55745550Sbill 				du->dk_skip = 0;
55845550Sbill 				wd_haderror = 0;
55945550Sbill 				wd_sebyse = 1;
56045550Sbill 				wdstart();
56145550Sbill 				return;		/* redo xfer sector by sector */
56241056Swilliam 			}
56341056Swilliam 		}
56441056Swilliam 
56541056Swilliam done:
56645550Sbill 		wd_sebyse = 0;
56741056Swilliam 		/* done with this transfer, with or without error */
56841056Swilliam 		wdtab.b_actf = dp->b_forw;
56941056Swilliam 		wdtab.b_errcnt = 0;
57041056Swilliam 		du->dk_skip = 0;
57141056Swilliam 		dp->b_active = 0;
57241056Swilliam 		dp->b_actf = bp->av_forw;
57341056Swilliam 		dp->b_errcnt = 0;
57441056Swilliam 		bp->b_resid = 0;
57541056Swilliam 		biodone(bp);
57641056Swilliam 	}
57741056Swilliam 	wdtab.b_active = 0;
57841056Swilliam 	if (dp->b_actf)
57941056Swilliam 		wdustart(du);		/* requeue disk if more io to do */
58041056Swilliam 	if (wdtab.b_actf)
58141056Swilliam 		wdstart();		/* start IO on next drive */
58241056Swilliam }
58341056Swilliam 
58441056Swilliam /*
58541056Swilliam  * Initialize a drive.
58641056Swilliam  */
587*49568Swilliam wdopen(dev, flags, fmt)
588*49568Swilliam         dev_t dev;
589*49568Swilliam         int flags, fmt;
59041056Swilliam {
59141056Swilliam 	register unsigned int unit;
59241056Swilliam 	register struct buf *bp;
59341056Swilliam 	register struct disk *du;
594*49568Swilliam         int part = wdpart(dev), mask = 1 << part;
595*49568Swilliam         struct partition *pp;
59641056Swilliam 	struct dkbad *db;
59741056Swilliam 	int i, error = 0;
59841056Swilliam 
599*49568Swilliam 	unit = wdunit(dev);
60041056Swilliam 	if (unit >= NWD) return (ENXIO) ;
60141056Swilliam 	du = &wddrives[unit];
602*49568Swilliam #ifdef notdef
60341056Swilliam 	if (du->dk_open){
60441056Swilliam 		du->dk_open++ ;
60541056Swilliam 		return(0);	/* already is open, don't mess with it */
60641056Swilliam 	}
60741056Swilliam #endif
60841056Swilliam 	du->dk_unit = unit;
60941056Swilliam 	wdutab[unit].b_actf = NULL;
61041056Swilliam 	/*if (flags & O_NDELAY)
61141056Swilliam 		du->dk_state = WANTOPENRAW;
61241056Swilliam 	else*/
61341056Swilliam 		du->dk_state = WANTOPEN;
61441056Swilliam 	/*
61541056Swilliam 	 * Use the default sizes until we've read the label,
61641056Swilliam 	 * or longer if there isn't one there.
61741056Swilliam 	 */
61841056Swilliam 	du->dk_dd = dflt_sizes;
61941056Swilliam 
62041056Swilliam 	/*
62141056Swilliam 	 * Recal, read of disk label will be done in wdcontrol
62241056Swilliam 	 * during first read operation.
62341056Swilliam 	 */
62441056Swilliam 	bp = geteblk(512);
62545549Sbill 	bp->b_dev = dev & 0xff00;
626*49568Swilliam 	bp->b_bcount = 0;
627*49568Swilliam 	bp->b_blkno = LABELSECTOR;
62841056Swilliam 	bp->b_flags = B_READ;
62941056Swilliam 	wdstrategy(bp);
63041056Swilliam 	biowait(bp);
63141056Swilliam 	if (bp->b_flags & B_ERROR) {
63241056Swilliam 		error = ENXIO;
63341056Swilliam 		du->dk_state = CLOSED;
63441056Swilliam 		goto done;
63541056Swilliam 	}
63641056Swilliam 	if (du->dk_state == OPENRAW) {
63741056Swilliam 		du->dk_state = OPENRAW;
63841056Swilliam 		goto done;
63941056Swilliam 	}
64041056Swilliam 	/*
64141056Swilliam 	 * Read bad sector table into memory.
64241056Swilliam 	 */
64341056Swilliam 	i = 0;
64441056Swilliam 	do {
64541056Swilliam 		bp->b_flags = B_BUSY | B_READ;
646*49568Swilliam 		bp->b_blkno = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors
64741056Swilliam 			+ i;
648*49568Swilliam 		if (du->dk_dd.d_secsize > DEV_BSIZE)
649*49568Swilliam 			bp->b_blkno *= du->dk_dd.d_secsize / DEV_BSIZE;
65041056Swilliam 		else
651*49568Swilliam 			bp->b_blkno /= DEV_BSIZE / du->dk_dd.d_secsize;
652*49568Swilliam 		bp->b_bcount = du->dk_dd.d_secsize;
653*49568Swilliam 		bp->b_cylin = du->dk_dd.d_ncylinders - 1;
65441056Swilliam 		wdstrategy(bp);
65541056Swilliam 		biowait(bp);
65641056Swilliam 	} while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
657*49568Swilliam 		i < du->dk_dd.d_nsectors);
65841056Swilliam 	db = (struct dkbad *)(bp->b_un.b_addr);
65945550Sbill #define DKBAD_MAGIC 0x4321
66041056Swilliam 	if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 &&
66141056Swilliam 	    db->bt_flag == DKBAD_MAGIC) {
66241056Swilliam 		dkbad[unit] = *db;
66341056Swilliam 		du->dk_state = OPEN;
66441056Swilliam 	} else {
66541056Swilliam 		printf("wd%d: %s bad-sector file\n", unit,
66641056Swilliam 		    (bp->b_flags & B_ERROR) ? "can't read" : "format error in");
667*49568Swilliam 		error = ENXIO ;
66841056Swilliam 		du->dk_state = OPENRAW;
66941056Swilliam 	}
67041056Swilliam done:
67141056Swilliam 	bp->b_flags = B_INVAL | B_AGE;
67241056Swilliam 	brelse(bp);
67341056Swilliam 	if (error == 0)
67441056Swilliam 		du->dk_open = 1;
675*49568Swilliam 
676*49568Swilliam         /*
677*49568Swilliam          * Warn if a partion is opened
678*49568Swilliam          * that overlaps another partition which is open
679*49568Swilliam          * unless one is the "raw" partition (whole disk).
680*49568Swilliam          */
681*49568Swilliam #define RAWPART         8               /* 'x' partition */     /* XXX */
682*49568Swilliam         if ((du->dk_openpart & mask) == 0 && part != RAWPART) {
683*49568Swilliam 		int	start, end;
684*49568Swilliam 
685*49568Swilliam                 pp = &du->dk_dd.d_partitions[part];
686*49568Swilliam                 start = pp->p_offset;
687*49568Swilliam                 end = pp->p_offset + pp->p_size;
688*49568Swilliam                 for (pp = du->dk_dd.d_partitions;
689*49568Swilliam                      pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
690*49568Swilliam 			pp++) {
691*49568Swilliam                         if (pp->p_offset + pp->p_size <= start ||
692*49568Swilliam                             pp->p_offset >= end)
693*49568Swilliam                                 continue;
694*49568Swilliam                         if (pp - du->dk_dd.d_partitions == RAWPART)
695*49568Swilliam                                 continue;
696*49568Swilliam                         if (du->dk_openpart & (1 << (pp -
697*49568Swilliam 					du->dk_dd.d_partitions)))
698*49568Swilliam                                 log(LOG_WARNING,
699*49568Swilliam                                     "wd%d%c: overlaps open partition (%c)\n",
700*49568Swilliam                                     unit, part + 'a',
701*49568Swilliam                                     pp - du->dk_dd.d_partitions + 'a');
702*49568Swilliam                 }
703*49568Swilliam         }
704*49568Swilliam         if (part >= du->dk_dd.d_npartitions)
705*49568Swilliam                 return (ENXIO);
706*49568Swilliam         du->dk_openpart |= mask;
707*49568Swilliam         switch (fmt) {
708*49568Swilliam         case S_IFCHR:
709*49568Swilliam                 du->dk_copenpart |= mask;
710*49568Swilliam                 break;
711*49568Swilliam         case S_IFBLK:
712*49568Swilliam                 du->dk_bopenpart |= mask;
713*49568Swilliam                 break;
714*49568Swilliam         }
71541056Swilliam 	return (error);
71641056Swilliam }
71741056Swilliam 
71841056Swilliam /*
71941056Swilliam  * Implement operations other than read/write.
72041056Swilliam  * Called from wdstart or wdintr during opens and formats.
72141056Swilliam  * Uses finite-state-machine to track progress of operation in progress.
72241056Swilliam  * Returns 0 if operation still in progress, 1 if completed.
72341056Swilliam  */
72441056Swilliam wdcontrol(bp)
72541056Swilliam 	register struct buf *bp;
72641056Swilliam {
72741056Swilliam 	register struct disk *du;
72841056Swilliam 	register unit;
72941056Swilliam 	unsigned char  stat;
73041056Swilliam 	int s, cnt;
73141056Swilliam 	extern int bootdev, cyloffset;
73241056Swilliam 
733*49568Swilliam 	du = &wddrives[wdunit(bp->b_dev)];
73441056Swilliam 	unit = du->dk_unit;
73541056Swilliam 	switch (DISKSTATE(du->dk_state)) {
73641056Swilliam 
73741056Swilliam 	tryagainrecal:
73841056Swilliam 	case WANTOPEN:			/* set SDH, step rate, do restore */
73941056Swilliam #ifdef	WDDEBUG
74041056Swilliam 		dprintf(DDSK,"wd%d: recal ", unit);
74141056Swilliam #endif
74241056Swilliam 		s = splbio();		/* not called from intr level ... */
74341056Swilliam 		outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
74441056Swilliam 		wdtab.b_active = 1;
74541056Swilliam 		outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
74641056Swilliam 		du->dk_state++;
74741056Swilliam 		splx(s);
74841056Swilliam 		return(0);
74941056Swilliam 
75041056Swilliam 	case RECAL:
75145551Sbill 		if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
75241056Swilliam 			printf("wd%d: recal", du->dk_unit);
75341056Swilliam 			if (unit == 0) {
75441056Swilliam 				printf(": status %b error %b\n",
75541056Swilliam 					stat, WDCS_BITS,
75641056Swilliam 					inb(wdc+wd_error), WDERR_BITS);
75741056Swilliam 				if (++wdtab.b_errcnt < RETRIES)
75841056Swilliam 					goto tryagainrecal;
75941056Swilliam 			}
76041056Swilliam 			goto badopen;
76141056Swilliam 		}
762*49568Swilliam 
763*49568Swilliam 		/* some compaq controllers require this ... */
764*49568Swilliam 		wdsetctlr(bp->b_dev, du);
765*49568Swilliam 
76641056Swilliam 		wdtab.b_errcnt = 0;
76741056Swilliam 		if (ISRAWSTATE(du->dk_state)) {
76841056Swilliam 			du->dk_state = OPENRAW;
76941056Swilliam 			return(1);
77041056Swilliam 		}
77141056Swilliam retry:
77241056Swilliam #ifdef	WDDEBUG
77341056Swilliam 		dprintf(DDSK,"rdlabel ");
77441056Swilliam #endif
775*49568Swilliam if( cyloffset < 0 || cyloffset > 8192) cyloffset=0;
77641056Swilliam 		/*
777*49568Swilliam 		 * Read in sector LABELSECTOR to get the pack label
778*49568Swilliam 		 * and geometry.
77941056Swilliam 		 */
78041056Swilliam 		outb(wdc+wd_precomp, 0xff);/* sometimes this is head bit 3 */
78141056Swilliam 		outb(wdc+wd_seccnt, 1);
782*49568Swilliam 		outb(wdc+wd_sector, LABELSECTOR+1);
78341056Swilliam 		/*if (bp->b_dev == bootdev) {
78441056Swilliam 			(wdc+wd_cyl_lo = cyloffset & 0xff;
78541056Swilliam 			(wdc+wd_cyl_hi = cyloffset >> 8;
78641056Swilliam 		} else {
78741056Swilliam 			(wdc+wd_cyl_lo = 0;
78841056Swilliam 			(wdc+wd_cyl_hi = 0;
78941056Swilliam 		}*/
79041056Swilliam 		outb(wdc+wd_cyl_lo, (cyloffset & 0xff));
79141056Swilliam 		outb(wdc+wd_cyl_hi, (cyloffset >> 8));
79241056Swilliam 		outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
79341056Swilliam 		outb(wdc+wd_command, WDCC_READ);
79441056Swilliam 		du->dk_state = RDLABEL;
79541056Swilliam 		return(0);
79641056Swilliam 
79741056Swilliam 	case RDLABEL:
79841056Swilliam 		if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
79941056Swilliam 			if (++wdtab.b_errcnt < RETRIES)
80041056Swilliam 				goto retry;
80141056Swilliam 			printf("wd%d: read label", unit);
80241056Swilliam 			goto badopen;
80341056Swilliam 		}
80441056Swilliam 
80541056Swilliam 		insw(wdc+wd_data, bp->b_un.b_addr, 256);
80641056Swilliam 
80741056Swilliam 		if (((struct disklabel *)
808*49568Swilliam 		    (bp->b_un.b_addr + LABELOFFSET))->d_magic == DISKMAGIC) {
80941056Swilliam 		       du->dk_dd =
81041056Swilliam 			 * (struct disklabel *) (bp->b_un.b_addr + LABELOFFSET);
81141056Swilliam 		} else {
81241056Swilliam 			printf("wd%d: bad disk label\n", du->dk_unit);
81341056Swilliam 			du->dk_state = OPENRAW;
81441056Swilliam 		}
81545552Sbill 
81645552Sbill 		s = splbio();		/* not called from intr level ... */
817*49568Swilliam 		while ((stat = inb(wdc+wd_status)) & WDCS_BUSY);
818*49568Swilliam 
819*49568Swilliam 		wdsetctlr(bp->b_dev, du);
820*49568Swilliam 
82145552Sbill 		outb(wdc+wd_seccnt, 0);
82245552Sbill 		splx(s);
82345552Sbill 
82441056Swilliam 		if (du->dk_state == RDLABEL)
82541056Swilliam 			du->dk_state = RDBADTBL;
82641056Swilliam 		/*
82741056Swilliam 		 * The rest of the initialization can be done
82841056Swilliam 		 * by normal means.
82941056Swilliam 		 */
83041056Swilliam 		return(1);
83141056Swilliam 
83241056Swilliam 	default:
833*49568Swilliam 		panic("wdcontrol");
83441056Swilliam 	}
83541056Swilliam 	/* NOTREACHED */
83641056Swilliam 
83741056Swilliam badopen:
83841056Swilliam 	printf(": status %b error %b\n",
83941056Swilliam 		stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
84041056Swilliam 	du->dk_state = OPENRAW;
84141056Swilliam 	return(1);
84241056Swilliam }
84341056Swilliam 
844*49568Swilliam wdsetctlr(dev, du) dev_t dev; struct disk *du; {
845*49568Swilliam 	int stat;
84641056Swilliam 
847*49568Swilliam 	outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders);
848*49568Swilliam 	outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders)>>8);
849*49568Swilliam 	outb(wdc+wd_sdh, WDSD_IBM | (wdunit(dev) << 4) + du->dk_dd.d_ntracks-1);
850*49568Swilliam 	outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
851*49568Swilliam 	outb(wdc+wd_command, 0x91);
852*49568Swilliam 
853*49568Swilliam 	while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) ;
854*49568Swilliam 	stat = inb(wdc+wd_error);
855*49568Swilliam 	return(stat);
856*49568Swilliam }
857*49568Swilliam 
858*49568Swilliam /* ARGSUSED */
859*49568Swilliam wdclose(dev, flags, fmt)
860*49568Swilliam         dev_t dev;
861*49568Swilliam         int flags, fmt;
862*49568Swilliam {
863*49568Swilliam 	register struct disk *du;
864*49568Swilliam 
865*49568Swilliam 	du = &wddrives[wdunit(dev)];
86641056Swilliam 	du->dk_open-- ;
86741056Swilliam 	/*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */
86841056Swilliam }
86941056Swilliam 
87041056Swilliam wdioctl(dev,cmd,addr,flag)
87141056Swilliam 	dev_t dev;
87241056Swilliam 	caddr_t addr;
87341056Swilliam {
874*49568Swilliam 	int unit = wdunit(dev);
87541056Swilliam 	register struct disk *du;
87641056Swilliam 	int error = 0;
87741056Swilliam 	struct uio auio;
87841056Swilliam 	struct iovec aiov;
87941056Swilliam 	/*int wdformat();*/
88041056Swilliam 
88141056Swilliam 	du = &wddrives[unit];
88241056Swilliam 
88341056Swilliam 	switch (cmd) {
88441056Swilliam 
88541056Swilliam 	case DIOCGDINFO:
88641056Swilliam 		*(struct disklabel *)addr = du->dk_dd;
88741056Swilliam 		break;
88841056Swilliam 
889*49568Swilliam         case DIOCGPART:
890*49568Swilliam                 ((struct partinfo *)addr)->disklab = &du->dk_dd;
891*49568Swilliam                 ((struct partinfo *)addr)->part =
892*49568Swilliam                     &du->dk_dd.d_partitions[wdpart(dev)];
893*49568Swilliam                 break;
894*49568Swilliam 
895*49568Swilliam         case DIOCSDINFO:
896*49568Swilliam                 if ((flag & FWRITE) == 0)
897*49568Swilliam                         error = EBADF;
898*49568Swilliam                 else
899*49568Swilliam                         error = setdisklabel(&du->dk_dd,
900*49568Swilliam 					(struct disklabel *)addr,
901*49568Swilliam                          0 /*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/);
902*49568Swilliam                 /*if (error == 0 && dk->dk_state == OPENRAW &&
903*49568Swilliam                     vdreset_drive(vddinfo[unit]))
904*49568Swilliam                         dk->dk_state = OPEN;*/
905*49568Swilliam 		wdsetctlr(dev, du);
906*49568Swilliam                 break;
907*49568Swilliam 
908*49568Swilliam         case DIOCWLABEL:
909*49568Swilliam                 if ((flag & FWRITE) == 0)
910*49568Swilliam                         error = EBADF;
911*49568Swilliam                 else
912*49568Swilliam                         du->dk_wlabel = *(int *)addr;
913*49568Swilliam                 break;
914*49568Swilliam 
915*49568Swilliam         case DIOCWDINFO:
916*49568Swilliam                 if ((flag & FWRITE) == 0)
917*49568Swilliam                         error = EBADF;
918*49568Swilliam                 else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr,
919*49568Swilliam                   0/*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/)) == 0) {
920*49568Swilliam                         int wlab;
921*49568Swilliam 
922*49568Swilliam                         /*if (error == 0 && dk->dk_state == OPENRAW &&
923*49568Swilliam                             vdreset_drive(vddinfo[unit]))
924*49568Swilliam                                 dk->dk_state = OPEN; */
925*49568Swilliam 			wdsetctlr(dev, du);
926*49568Swilliam 
927*49568Swilliam                         /* simulate opening partition 0 so write succeeds */
928*49568Swilliam                         /* dk->dk_openpart |= (1 << 0);            /* XXX */
929*49568Swilliam                         wlab = du->dk_wlabel;
930*49568Swilliam                         du->dk_wlabel = 1;
931*49568Swilliam                         error = writedisklabel(dev, wdstrategy, &du->dk_dd,wdpart(dev));
932*49568Swilliam                         /*dk->dk_openpart = dk->dk_copenpart | dk->dk_bopenpart;*/
933*49568Swilliam                         du->dk_wlabel = wlab;
934*49568Swilliam                 }
935*49568Swilliam                 break;
936*49568Swilliam 
937*49568Swilliam #ifdef notyet
93841056Swilliam 	case DIOCGDINFOP:
93941056Swilliam 		*(struct disklabel **)addr = &(du->dk_dd);
94041056Swilliam 		break;
94141056Swilliam 
94241056Swilliam 	case DIOCWFORMAT:
94341056Swilliam 		if ((flag & FWRITE) == 0)
94441056Swilliam 			error = EBADF;
94541056Swilliam 		else {
94641056Swilliam 			register struct format_op *fop;
94741056Swilliam 
94841056Swilliam 			fop = (struct format_op *)addr;
94941056Swilliam 			aiov.iov_base = fop->df_buf;
95041056Swilliam 			aiov.iov_len = fop->df_count;
95141056Swilliam 			auio.uio_iov = &aiov;
95241056Swilliam 			auio.uio_iovcnt = 1;
95341056Swilliam 			auio.uio_resid = fop->df_count;
95441056Swilliam 			auio.uio_segflg = 0;
95541056Swilliam 			auio.uio_offset =
956*49568Swilliam 				fop->df_startblk * du->dk_dd.d_secsize;
95741056Swilliam 			error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE,
95841056Swilliam 				minphys, &auio);
95941056Swilliam 			fop->df_count -= auio.uio_resid;
96041056Swilliam 			fop->df_reg[0] = du->dk_status;
96141056Swilliam 			fop->df_reg[1] = du->dk_error;
96241056Swilliam 		}
96341056Swilliam 		break;
96441056Swilliam #endif
96541056Swilliam 
96641056Swilliam 	default:
96741056Swilliam 		error = ENOTTY;
96841056Swilliam 		break;
96941056Swilliam 	}
97041056Swilliam 	return (error);
97141056Swilliam }
97241056Swilliam 
97341056Swilliam /*wdformat(bp)
97441056Swilliam 	struct buf *bp;
97541056Swilliam {
97641056Swilliam 
97741056Swilliam 	bp->b_flags |= B_FORMAT;
97841056Swilliam 	return (wdstrategy(bp));
97941056Swilliam }*/
98041056Swilliam 
98141056Swilliam /*
98241056Swilliam  * Routines to do raw IO for a unit.
98341056Swilliam  */
98441056Swilliam wdread(dev, uio)			/* character read routine */
98541056Swilliam 	dev_t dev;
98641056Swilliam 	struct uio *uio;
98741056Swilliam {
988*49568Swilliam 	int unit = wdunit(dev) ;
98941056Swilliam 
99041056Swilliam 	if (unit >= NWD) return(ENXIO);
99141056Swilliam 	return(physio(wdstrategy, &rwdbuf[unit], dev, B_READ, minphys, uio));
99241056Swilliam }
99341056Swilliam 
99441056Swilliam 
99541056Swilliam wdwrite(dev, uio)			/* character write routine */
99641056Swilliam 	dev_t dev;
99741056Swilliam 	struct uio *uio;
99841056Swilliam {
999*49568Swilliam 	int unit = wdunit(dev) ;
100041056Swilliam 
100141056Swilliam 	if (unit >= NWD) return(ENXIO);
100241056Swilliam 	return(physio(wdstrategy, &rwdbuf[unit], dev, B_WRITE, minphys, uio));
100341056Swilliam }
100441056Swilliam 
100541056Swilliam wdsize(dev)
100641056Swilliam 	dev_t dev;
100741056Swilliam {
1008*49568Swilliam 	register unit = wdunit(dev);
1009*49568Swilliam 	register part = wdpart(dev);
101041056Swilliam 	register struct disk *du;
101141056Swilliam 	register val ;
101241056Swilliam 
101341056Swilliam 	if (unit >= NWD) return(-1);
1014*49568Swilliam 	if (wddrives[unit].dk_state == 0) {
1015*49568Swilliam 		val = wdopen (dev, 0);
1016*49568Swilliam 		if (val < 0)
1017*49568Swilliam 			return (-1);
1018*49568Swilliam 	}
101941056Swilliam 	du = &wddrives[unit];
1020*49568Swilliam 	return((int)((u_long)du->dk_dd.d_partitions[part].p_size *
1021*49568Swilliam 		du->dk_dd.d_secsize / 512));
102241056Swilliam }
102341056Swilliam 
1024*49568Swilliam extern        char *vmmap;            /* poor name! */
1025*49568Swilliam 
102641056Swilliam wddump(dev)			/* dump core after a system crash */
102741056Swilliam 	dev_t dev;
102841056Swilliam {
102941056Swilliam 	register struct disk *du;	/* disk unit to do the IO */
103041056Swilliam 	register struct bt_bad *bt_ptr;
103141056Swilliam 	long	num;			/* number of sectors to write */
1032*49568Swilliam 	int	unit, part;
103341056Swilliam 	long	cyloff, blknum, blkcnt;
1034*49568Swilliam 	long	cylin, head, sector, stat;
103541056Swilliam 	long	secpertrk, secpercyl, nblocks, i;
1036*49568Swilliam 	char *addr;
1037*49568Swilliam 	extern	int Maxmem;
103841056Swilliam 	static  wddoingadump = 0 ;
1039*49568Swilliam 	extern CMAP1;
1040*49568Swilliam 	extern char CADDR1[];
104141056Swilliam 
1042*49568Swilliam 
1043*49568Swilliam #ifdef ARGO
1044*49568Swilliam outb(0x461,0);	/* disable failsafe timer */
1045*49568Swilliam #endif
1046*49568Swilliam 	addr = (char *) 0;		/* starting address */
104741056Swilliam 	/* size of memory to dump */
1048*49568Swilliam 	num = Maxmem;
1049*49568Swilliam 	unit = wdunit(dev);		/* eventually support floppies? */
1050*49568Swilliam 	part = wdpart(dev);		/* file system */
105141056Swilliam 	/* check for acceptable drive number */
105241056Swilliam 	if (unit >= NWD) return(ENXIO);
105341056Swilliam 
105441056Swilliam 	du = &wddrives[unit];
105541056Swilliam 	/* was it ever initialized ? */
105641056Swilliam 	if (du->dk_state < OPEN) return (ENXIO) ;
105741056Swilliam 
105841056Swilliam 	/* Convert to disk sectors */
1059*49568Swilliam 	num = (u_long) num * NBPG / du->dk_dd.d_secsize;
106041056Swilliam 
106141056Swilliam 	/* check if controller active */
106241056Swilliam 	/*if (wdtab.b_active) return(EFAULT); */
106341056Swilliam 	if (wddoingadump) return(EFAULT);
106441056Swilliam 
1065*49568Swilliam 	secpertrk = du->dk_dd.d_nsectors;
1066*49568Swilliam 	secpercyl = du->dk_dd.d_secpercyl;
1067*49568Swilliam 	nblocks = du->dk_dd.d_partitions[part].p_size;
1068*49568Swilliam 	cyloff = du->dk_dd.d_partitions[part].p_offset / secpercyl;
106941056Swilliam 
1070*49568Swilliam /*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/
107141056Swilliam 	/* check transfer bounds against partition size */
1072*49568Swilliam 	if ((dumplo < 0) || ((dumplo + num) > nblocks))
107341056Swilliam 		return(EINVAL);
107441056Swilliam 
107541056Swilliam 	/*wdtab.b_active = 1;		/* mark controller active for if we
107641056Swilliam 					   panic during the dump */
107741056Swilliam 	wddoingadump = 1  ;  i = 100000 ;
1078*49568Swilliam 	while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ;
1079*49568Swilliam 	outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
1080*49568Swilliam 	outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
1081*49568Swilliam 	while (inb(wdc+wd_status) & WDCS_BUSY) ;
1082*49568Swilliam 
1083*49568Swilliam 	/* some compaq controllers require this ... */
1084*49568Swilliam 	wdsetctlr(dev, du);
108541056Swilliam 
108641056Swilliam 	blknum = dumplo;
108741056Swilliam 	while (num > 0) {
108841056Swilliam #ifdef notdef
108941056Swilliam 		if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER;
109041056Swilliam 		if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
109141056Swilliam 			blkcnt = secpercyl - (blknum % secpercyl);
109241056Swilliam 			    /* keep transfer within current cylinder */
109341056Swilliam #endif
1094*49568Swilliam 		pmap_enter(pmap_kernel(), vmmap, addr, VM_PROT_READ, TRUE);
109541056Swilliam 
109641056Swilliam 		/* compute disk address */
109741056Swilliam 		cylin = blknum / secpercyl;
109841056Swilliam 		head = (blknum % secpercyl) / secpertrk;
109941056Swilliam 		sector = blknum % secpertrk;
110043592Sdonahn 		cylin += cyloff;
110141056Swilliam 
1102*49568Swilliam #ifdef notyet
110341056Swilliam 		/*
110441056Swilliam 		 * See if the current block is in the bad block list.
110541056Swilliam 		 * (If we have one.)
110641056Swilliam 		 */
110741056Swilliam 	    		for (bt_ptr = dkbad[unit].bt_bad;
110841056Swilliam 				bt_ptr->bt_cyl != -1; bt_ptr++) {
110941056Swilliam 			if (bt_ptr->bt_cyl > cylin)
111041056Swilliam 				/* Sorted list, and we passed our cylinder.
111141056Swilliam 					quit. */
111241056Swilliam 				break;
111341056Swilliam 			if (bt_ptr->bt_cyl == cylin &&
111441056Swilliam 				bt_ptr->bt_trksec == (head << 8) + sector) {
111541056Swilliam 			/*
111641056Swilliam 			 * Found bad block.  Calculate new block addr.
111741056Swilliam 			 * This starts at the end of the disk (skip the
111841056Swilliam 			 * last track which is used for the bad block list),
111941056Swilliam 			 * and works backwards to the front of the disk.
112041056Swilliam 			 */
1121*49568Swilliam 				blknum = (du->dk_dd.d_secperunit)
1122*49568Swilliam 					- du->dk_dd.d_nsectors
112341056Swilliam 					- (bt_ptr - dkbad[unit].bt_bad) - 1;
112441056Swilliam 				cylin = blknum / secpercyl;
112541056Swilliam 				head = (blknum % secpercyl) / secpertrk;
112641056Swilliam 				sector = blknum % secpertrk;
112741056Swilliam 				break;
112841056Swilliam 			}
112941056Swilliam 
1130*49568Swilliam #endif
1131*49568Swilliam 		sector++;		/* origin 1 */
1132*49568Swilliam 
113341056Swilliam 		/* select drive.     */
1134*49568Swilliam 		outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
1135*49568Swilliam 		while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
113641056Swilliam 
113741056Swilliam 		/* transfer some blocks */
1138*49568Swilliam 		outb(wdc+wd_sector, sector);
1139*49568Swilliam 		outb(wdc+wd_seccnt,1);
1140*49568Swilliam 		outb(wdc+wd_cyl_lo, cylin);
1141*49568Swilliam 		outb(wdc+wd_cyl_hi, cylin >> 8);
114241056Swilliam #ifdef notdef
114341056Swilliam 		/* lets just talk about this first...*/
1144*49568Swilliam 		pg ("sdh 0%o sector %d cyl %d addr 0x%x",
1145*49568Swilliam 			inb(wdc+wd_sdh), inb(wdc+wd_sector),
1146*49568Swilliam 			inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr) ;
114741056Swilliam #endif
1148*49568Swilliam #ifdef ODYSSEUS
1149*49568Swilliam if(cylin < 46 || cylin > 91)pg("oops");
1150*49568Swilliam #endif
1151*49568Swilliam #ifdef PRIAM
1152*49568Swilliam if(cylin < 40 || cylin > 79)pg("oops");
1153*49568Swilliam #endif
1154*49568Swilliam 		outb(wdc+wd_command, WDCC_WRITE);
115541056Swilliam 
115641056Swilliam 		/* Ready to send data?	*/
1157*49568Swilliam 		while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
1158*49568Swilliam 		if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
115941056Swilliam 
1160*49568Swilliam 		outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256);
1161*49568Swilliam 		(int) addr += 512;
1162*49568Swilliam 
1163*49568Swilliam 		if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
116441056Swilliam 		/* Check data request (should be done).         */
1165*49568Swilliam 		if (inb(wdc+wd_status) & WDCS_DRQ) return(EIO) ;
116641056Swilliam 
116741056Swilliam 		/* wait for completion */
1168*49568Swilliam 		for ( i = 1000000 ; inb(wdc+wd_status) & WDCS_BUSY ; i--) {
116941056Swilliam 				if (i < 0) return (EIO) ;
117041056Swilliam 		}
117141056Swilliam 		/* error check the xfer */
1172*49568Swilliam 		if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
117341056Swilliam 		/* update block count */
117441056Swilliam 		num--;
117541056Swilliam 		blknum++ ;
117641056Swilliam if (num % 100 == 0) printf(".") ;
117741056Swilliam 	}
117841056Swilliam 	return(0);
117941056Swilliam }
118041056Swilliam #endif
1181