xref: /csrg-svn/sys/i386/isa/wd.c (revision 41056)
1*41056Swilliam /*-
2*41056Swilliam  * Copyright (c) 1990 The Regents of the University of California.
3*41056Swilliam  * All rights reserved.
4*41056Swilliam  *
5*41056Swilliam  * This code is derived from software contributed to Berkeley by
6*41056Swilliam  * William Jolitz.
7*41056Swilliam  *
8*41056Swilliam  * %sccs.include.386.c%
9*41056Swilliam  *
10*41056Swilliam  *	@(#)wd.c	5.1 (Berkeley) 04/24/90
11*41056Swilliam  */
12*41056Swilliam #include "wd.h"
13*41056Swilliam #if	NWD > 0
14*41056Swilliam #define WDDEBUG
15*41056Swilliam 
16*41056Swilliam #include "param.h"
17*41056Swilliam #include "dkbad.h"
18*41056Swilliam #include "systm.h"
19*41056Swilliam #include "conf.h"
20*41056Swilliam #include "file.h"
21*41056Swilliam #include "dir.h"
22*41056Swilliam #include "user.h"
23*41056Swilliam #include "ioctl.h"
24*41056Swilliam #include "disk.h"
25*41056Swilliam #include "buf.h"
26*41056Swilliam #include "vm.h"
27*41056Swilliam #include "uio.h"
28*41056Swilliam #include "machine/pte.h"
29*41056Swilliam #include "machine/device.h"
30*41056Swilliam #include "atio.h"
31*41056Swilliam #include "icu.h"
32*41056Swilliam #include "wdreg.h"
33*41056Swilliam #include "syslog.h"
34*41056Swilliam 
35*41056Swilliam #define	RETRIES		5	/* number of retries before giving up */
36*41056Swilliam #define	MAXTRANSFER	256	/* max size of transfer in page clusters */
37*41056Swilliam 
38*41056Swilliam #define WDUNIT(dev)	((minor(dev) & 070) >> 3)
39*41056Swilliam 
40*41056Swilliam #define b_cylin	b_resid		/* cylinder number for doing IO to */
41*41056Swilliam 				/* shares an entry in the buf struct */
42*41056Swilliam 
43*41056Swilliam /*
44*41056Swilliam  * Drive states.  Used for open and format operations.
45*41056Swilliam  * States < OPEN (> 0) are transient, during an open operation.
46*41056Swilliam  * OPENRAW is used for unlabeled disks, and for floppies, to inhibit
47*41056Swilliam  * bad-sector forwarding.
48*41056Swilliam  */
49*41056Swilliam #define RAWDISK		8		/* raw disk operation, no translation*/
50*41056Swilliam #define ISRAWSTATE(s)	(RAWDISK&(s))	/* are we in a raw state? */
51*41056Swilliam #define DISKSTATE(s)	(~RAWDISK&(s))	/* are we in a given state regardless
52*41056Swilliam 					   of raw or cooked mode? */
53*41056Swilliam 
54*41056Swilliam #define	CLOSED		0		/* disk is closed. */
55*41056Swilliam 					/* "cooked" disk states */
56*41056Swilliam #define	WANTOPEN	1		/* open requested, not started */
57*41056Swilliam #define	RECAL		2		/* doing restore */
58*41056Swilliam #define	RDLABEL		3		/* reading pack label */
59*41056Swilliam #define	RDBADTBL	4		/* reading bad-sector table */
60*41056Swilliam #define	OPEN		5		/* done with open */
61*41056Swilliam 
62*41056Swilliam #define	WANTOPENRAW	(WANTOPEN|RAWDISK)	/* raw WANTOPEN */
63*41056Swilliam #define	RECALRAW	(RECAL|RAWDISK)	/* raw open, doing restore */
64*41056Swilliam #define	OPENRAW		(OPEN|RAWDISK)	/* open, but unlabeled disk or floppy */
65*41056Swilliam 
66*41056Swilliam 
67*41056Swilliam /*
68*41056Swilliam  * The structure of a disk drive.
69*41056Swilliam  */
70*41056Swilliam struct	disk {
71*41056Swilliam 	struct disklabel dk_dd;			/* device configuration data */
72*41056Swilliam 	long	dk_bc;				/* byte count left */
73*41056Swilliam 	short	dk_skip;			/* blocks already transferred */
74*41056Swilliam 	char	dk_unit;			/* physical unit number */
75*41056Swilliam 	char	dk_sdh;				/* sdh prototype */
76*41056Swilliam 	char	dk_state;			/* control state */
77*41056Swilliam 	u_char	dk_status;			/* copy of status reg. */
78*41056Swilliam 	u_char	dk_error;			/* copy of error reg. */
79*41056Swilliam 	short	dk_open;			/* open/closed refcnt */
80*41056Swilliam };
81*41056Swilliam 
82*41056Swilliam /*
83*41056Swilliam  * This label is used as a default when initializing a new or raw disk.
84*41056Swilliam  * It really only lets us access the first track until we know more.
85*41056Swilliam  */
86*41056Swilliam struct disklabel dflt_sizes = {
87*41056Swilliam 	DISKMAGIC, DTYPE_ST506,
88*41056Swilliam 	{
89*41056Swilliam 		512,		/* sector size */
90*41056Swilliam 		17,		/* # of sectors per track */
91*41056Swilliam 		15,		/* # of tracks per cylinder */
92*41056Swilliam 		918,		/* # of cylinders per unit */
93*41056Swilliam 		17*15,		/* # of sectors per cylinder */
94*41056Swilliam 		918*15*17,	/* # of sectors per unit */
95*41056Swilliam 		0		/* write precomp cylinder (none) */
96*41056Swilliam 	},
97*41056Swilliam 	7560,	0,	/* A=root filesystem */
98*41056Swilliam 	7560,	56,
99*41056Swilliam 	123930, 0,	/* C=whole disk */
100*41056Swilliam 	0,	0,
101*41056Swilliam 	7560,	861,
102*41056Swilliam 	0,	0,
103*41056Swilliam 	0,	0,
104*41056Swilliam 	101115,	112
105*41056Swilliam };
106*41056Swilliam static	struct	dkbad	dkbad[NWD];
107*41056Swilliam struct	disk	wddrives[NWD] = {0};	/* table of units */
108*41056Swilliam struct	buf	wdtab = {0};
109*41056Swilliam struct	buf	wdutab[NWD] = {0};	/* head of queue per drive */
110*41056Swilliam struct	buf	rwdbuf[NWD] = {0};	/* buffers for raw IO */
111*41056Swilliam long	wdxfer[NWD] = {0};		/* count of transfers */
112*41056Swilliam int	writeprotected[NWD] = { 0 };
113*41056Swilliam int	wdprobe(), wdattach(), wdintr();
114*41056Swilliam struct	driver wddriver = {
115*41056Swilliam 	wdprobe, wdattach, "wd",
116*41056Swilliam };
117*41056Swilliam #include "dbg.h"
118*41056Swilliam 
119*41056Swilliam /*
120*41056Swilliam  * Probe routine
121*41056Swilliam  */
122*41056Swilliam wdprobe(dvp)
123*41056Swilliam 	struct device *dvp;
124*41056Swilliam {
125*41056Swilliam 	register wdc = dvp->ioa;
126*41056Swilliam 
127*41056Swilliam #ifdef lint
128*41056Swilliam 	wdintr(0);
129*41056Swilliam #endif
130*41056Swilliam 	outb(wdc+wd_error, 0x5a) ;	/* error register not writable */
131*41056Swilliam 	/*wdp->wd_cyl_hi = 0xff ;/* only two bits of cylhi are implemented */
132*41056Swilliam 	outb(wdc+wd_cyl_lo, 0xa5) ;	/* but all of cyllo are implemented */
133*41056Swilliam 	if(inb(wdc+wd_error) != 0x5a /*&& wdp->wd_cyl_hi == 3*/
134*41056Swilliam 	   && inb(wdc+wd_cyl_lo) == 0xa5)
135*41056Swilliam 		return(1) ;
136*41056Swilliam 	return (0);
137*41056Swilliam }
138*41056Swilliam 
139*41056Swilliam /*
140*41056Swilliam  * attach each drive if possible.
141*41056Swilliam  */
142*41056Swilliam wdattach(dvp)
143*41056Swilliam 	struct device *dvp;
144*41056Swilliam {
145*41056Swilliam 	int unit = dvp->unit;
146*41056Swilliam 
147*41056Swilliam 	INTREN((IRQ14|4));
148*41056Swilliam 	outb(0x3f6,0);
149*41056Swilliam }
150*41056Swilliam 
151*41056Swilliam /* Read/write routine for a buffer.  Finds the proper unit, range checks
152*41056Swilliam  * arguments, and schedules the transfer.  Does not wait for the transfer
153*41056Swilliam  * to complete.  Multi-page transfers are supported.  All I/O requests must
154*41056Swilliam  * be a multiple of a sector in length.
155*41056Swilliam  */
156*41056Swilliam wdstrategy(bp)
157*41056Swilliam 	register struct buf *bp;	/* IO operation to perform */
158*41056Swilliam {
159*41056Swilliam 	register struct buf *dp;
160*41056Swilliam 	register struct disk *du;	/* Disk unit to do the IO.	*/
161*41056Swilliam 	long nblocks, cyloff, blknum;
162*41056Swilliam 	int	unit = WDUNIT(bp->b_dev), xunit = minor(bp->b_dev) & 7;
163*41056Swilliam 	int	s;
164*41056Swilliam 
165*41056Swilliam 	if ((unit >= NWD) || (bp->b_blkno < 0)) {
166*41056Swilliam 		dprintf(DDSK,"wdstrat: unit = %d, blkno = %d, bcount = %d\n",
167*41056Swilliam 			unit, bp->b_blkno, bp->b_bcount);
168*41056Swilliam 		dprintf(DDSK,"wd:error in wdstrategy\n");
169*41056Swilliam 		bp->b_flags |= B_ERROR;
170*41056Swilliam 		goto bad;
171*41056Swilliam 	}
172*41056Swilliam 	if (writeprotected[unit] && (bp->b_flags & B_READ) == 0) {
173*41056Swilliam 		printf("wd%d: write protected\n", unit);
174*41056Swilliam 		goto bad;
175*41056Swilliam 	}
176*41056Swilliam 	du = &wddrives[unit];
177*41056Swilliam 	if (DISKSTATE(du->dk_state) != OPEN)
178*41056Swilliam 		goto q;
179*41056Swilliam 	/*
180*41056Swilliam 	 * Convert DEV_BSIZE "blocks" to sectors.
181*41056Swilliam 	 * Note: doing the conversions this way limits the partition size
182*41056Swilliam 	 * to about 8 million sectors (1-8 Gb).
183*41056Swilliam 	 */
184*41056Swilliam 	blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.dk_secsize;
185*41056Swilliam 	if (((u_long) bp->b_blkno * DEV_BSIZE % du->dk_dd.dk_secsize != 0) ||
186*41056Swilliam 	    bp->b_bcount >= MAXTRANSFER * CLBYTES /*||
187*41056Swilliam 	    bp->b_bcount % du->dk_dd.dk_secsize*/) {
188*41056Swilliam 		bp->b_flags |= B_ERROR;
189*41056Swilliam printf("wdstrat: blknum %d bcount %d blkno %d ", blknum,
190*41056Swilliam bp->b_bcount, bp->b_blkno);
191*41056Swilliam 		goto bad;
192*41056Swilliam 	}
193*41056Swilliam 	nblocks = du->dk_dd.dk_partition[xunit].nblocks;
194*41056Swilliam 	cyloff = du->dk_dd.dk_partition[xunit].cyloff;
195*41056Swilliam 	if (blknum + (bp->b_bcount / du->dk_dd.dk_secsize) > nblocks) {
196*41056Swilliam 		dprintf(DDSK,"blknum = %d, fssize = %d\n", blknum, nblocks);
197*41056Swilliam 		if (blknum == nblocks)
198*41056Swilliam 			bp->b_resid = bp->b_bcount;
199*41056Swilliam 		else
200*41056Swilliam 			bp->b_flags |= B_ERROR;
201*41056Swilliam 		goto bad;
202*41056Swilliam 	}
203*41056Swilliam 	bp->b_cylin = blknum / du->dk_dd.dk_secpercyl + cyloff;
204*41056Swilliam q:
205*41056Swilliam 	dp = &wdutab[unit];
206*41056Swilliam 	s = splbio();
207*41056Swilliam 	disksort(dp, bp);
208*41056Swilliam 	if (dp->b_active == 0)
209*41056Swilliam 		wdustart(du);		/* start drive if idle */
210*41056Swilliam 	if (wdtab.b_active == 0)
211*41056Swilliam 		wdstart(s);		/* start IO if controller idle */
212*41056Swilliam 	splx(s);
213*41056Swilliam 	return;
214*41056Swilliam 
215*41056Swilliam bad:
216*41056Swilliam 	bp->b_error = EINVAL;
217*41056Swilliam 	biodone(bp);
218*41056Swilliam }
219*41056Swilliam 
220*41056Swilliam /* Routine to queue a read or write command to the controller.  The request is
221*41056Swilliam  * linked into the active list for the controller.  If the controller is idle,
222*41056Swilliam  * the transfer is started.
223*41056Swilliam  */
224*41056Swilliam wdustart(du)
225*41056Swilliam 	register struct disk *du;
226*41056Swilliam {
227*41056Swilliam 	register struct buf *bp, *dp;
228*41056Swilliam 
229*41056Swilliam 	dp = &wdutab[du->dk_unit];
230*41056Swilliam 	if (dp->b_active)
231*41056Swilliam 		return;
232*41056Swilliam 	bp = dp->b_actf;
233*41056Swilliam 	if (bp == NULL)
234*41056Swilliam 		return;
235*41056Swilliam 	dp->b_forw = NULL;
236*41056Swilliam 	if (wdtab.b_actf  == NULL)		/* link unit into active list */
237*41056Swilliam 		wdtab.b_actf = dp;
238*41056Swilliam 	else
239*41056Swilliam 		wdtab.b_actl->b_forw = dp;
240*41056Swilliam 	wdtab.b_actl = dp;
241*41056Swilliam 	dp->b_active = 1;		/* mark the drive as busy */
242*41056Swilliam }
243*41056Swilliam 
244*41056Swilliam /*
245*41056Swilliam  * Controller startup routine.  This does the calculation, and starts
246*41056Swilliam  * a single-sector read or write operation.  Called to start a transfer,
247*41056Swilliam  * or from the interrupt routine to continue a multi-sector transfer.
248*41056Swilliam  * RESTRICTIONS:
249*41056Swilliam  * 1.	The transfer length must be an exact multiple of the sector size.
250*41056Swilliam  */
251*41056Swilliam 
252*41056Swilliam wdstart()
253*41056Swilliam {
254*41056Swilliam 	register struct disk *du;	/* disk unit for IO */
255*41056Swilliam 	register wdc = IO_WD0; /*XXX*/
256*41056Swilliam 	register struct buf *bp;
257*41056Swilliam 	struct buf *dp;
258*41056Swilliam 	register struct bt_bad *bt_ptr;
259*41056Swilliam 	long	blknum, pagcnt, cylin, head, sector;
260*41056Swilliam 	long	secpertrk, secpercyl, addr, i;
261*41056Swilliam 	int	minor_dev, unit, s;
262*41056Swilliam 
263*41056Swilliam loop:
264*41056Swilliam 	dp = wdtab.b_actf;
265*41056Swilliam 	if (dp == NULL)
266*41056Swilliam 		return;
267*41056Swilliam 	bp = dp->b_actf;
268*41056Swilliam 	if (bp == NULL) {
269*41056Swilliam 		wdtab.b_actf = dp->b_forw;
270*41056Swilliam 		goto loop;
271*41056Swilliam 	}
272*41056Swilliam 	unit = WDUNIT(bp->b_dev);
273*41056Swilliam 	du = &wddrives[unit];
274*41056Swilliam 	if (DISKSTATE(du->dk_state) <= RDLABEL) {
275*41056Swilliam 		if (wdcontrol(bp)) {
276*41056Swilliam 			dp->b_actf = bp->av_forw;
277*41056Swilliam 			goto loop;	/* done */
278*41056Swilliam 		}
279*41056Swilliam 		return;
280*41056Swilliam 	}
281*41056Swilliam 	minor_dev = minor(bp->b_dev) & 7;
282*41056Swilliam 	secpertrk = du->dk_dd.dk_nsectors;
283*41056Swilliam 	secpercyl = du->dk_dd.dk_secpercyl;
284*41056Swilliam 	/*
285*41056Swilliam 	 * Convert DEV_BSIZE "blocks" to sectors.
286*41056Swilliam 	 */
287*41056Swilliam 	blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.dk_secsize
288*41056Swilliam 		+ du->dk_skip;
289*41056Swilliam #ifdef	WDDEBUG
290*41056Swilliam 	if (du->dk_skip == 0) {
291*41056Swilliam 		dprintf(DDSK,"\nwdstart %d: %s %d@%d; map ", unit,
292*41056Swilliam 			(bp->b_flags & B_READ) ? "read" : "write",
293*41056Swilliam 			bp->b_bcount, blknum);
294*41056Swilliam 	} else {
295*41056Swilliam 		dprintf(DDSK," %d)", du->dk_skip);
296*41056Swilliam 	}
297*41056Swilliam #endif
298*41056Swilliam 
299*41056Swilliam 	addr = (int) bp->b_un.b_addr;
300*41056Swilliam 	if(du->dk_skip==0) du->dk_bc = bp->b_bcount;
301*41056Swilliam 	cylin = blknum / secpercyl;
302*41056Swilliam 	head = (blknum % secpercyl) / secpertrk;
303*41056Swilliam 	sector = blknum % secpertrk + 1;
304*41056Swilliam 	if (DISKSTATE(du->dk_state) == OPEN)
305*41056Swilliam 		cylin += du->dk_dd.dk_partition[minor_dev].cyloff;
306*41056Swilliam 
307*41056Swilliam 
308*41056Swilliam 	/*
309*41056Swilliam 	 * See if the current block is in the bad block list.
310*41056Swilliam 	 * (If we have one, and not formatting.)
311*41056Swilliam 	 */
312*41056Swilliam #ifdef notyet
313*41056Swilliam 	if (du->dk_state == OPEN)
314*41056Swilliam 	    for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
315*41056Swilliam 		if (bt_ptr->bt_cyl > cylin)
316*41056Swilliam 			/* Sorted list, and we passed our cylinder. quit. */
317*41056Swilliam 			break;
318*41056Swilliam 		if (bt_ptr->bt_cyl == cylin &&
319*41056Swilliam 				bt_ptr->bt_trksec == (head << 8) + sector) {
320*41056Swilliam 			/*
321*41056Swilliam 			 * Found bad block.  Calculate new block addr.
322*41056Swilliam 			 * This starts at the end of the disk (skip the
323*41056Swilliam 			 * last track which is used for the bad block list),
324*41056Swilliam 			 * and works backwards to the front of the disk.
325*41056Swilliam 			 */
326*41056Swilliam #ifdef	WDDEBUG
327*41056Swilliam 			    dprintf(DDSK,"--- badblock code -> Old = %d; ",
328*41056Swilliam 				blknum);
329*41056Swilliam #endif
330*41056Swilliam 			blknum = du->dk_dd.dk_secperunit - du->dk_dd.dk_nsectors
331*41056Swilliam 				- (bt_ptr - dkbad[unit].bt_bad) - 1;
332*41056Swilliam 			cylin = blknum / secpercyl;
333*41056Swilliam 			head = (blknum % secpercyl) / secpertrk;
334*41056Swilliam 			sector = blknum % secpertrk;
335*41056Swilliam #ifdef	WDDEBUG
336*41056Swilliam 			    dprintf(DDSK, "new = %d\n", blknum);
337*41056Swilliam #endif
338*41056Swilliam 			break;
339*41056Swilliam 		}
340*41056Swilliam 	}
341*41056Swilliam #endif
342*41056Swilliam 
343*41056Swilliam 	wdtab.b_active = 1;		/* mark controller active */
344*41056Swilliam 
345*41056Swilliam 	outb(wdc+wd_precomp, 0xff);
346*41056Swilliam 	/*wr(wdc+wd_precomp, du->dk_dd.dk_precompcyl / 4);*/
347*41056Swilliam 	/*if (bp->b_flags & B_FORMAT) {
348*41056Swilliam 		wr(wdc+wd_sector, du->dk_dd.dk_gap3);
349*41056Swilliam 		wr(wdc+wd_seccnt, du->dk_dd.dk_nsectors);
350*41056Swilliam 	} else {*/
351*41056Swilliam 	outb(wdc+wd_seccnt, 1);
352*41056Swilliam 	outb(wdc+wd_sector, sector);
353*41056Swilliam 
354*41056Swilliam 	outb(wdc+wd_cyl_lo, cylin);
355*41056Swilliam 	outb(wdc+wd_cyl_hi, cylin >> 8);
356*41056Swilliam 
357*41056Swilliam 	/* Set up the SDH register (select drive).     */
358*41056Swilliam 	outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
359*41056Swilliam 	while ((inb(wdc+wd_altsts) & WDCS_READY) == 0) ;
360*41056Swilliam 
361*41056Swilliam 	/*if (bp->b_flags & B_FORMAT)
362*41056Swilliam 		wr(wdc+wd_command, WDCC_FORMAT);
363*41056Swilliam 	else*/
364*41056Swilliam 		outb(wdc+wd_command,
365*41056Swilliam 			(bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
366*41056Swilliam #ifdef	WDDEBUG
367*41056Swilliam 	if(du->dk_skip == 0)
368*41056Swilliam 	dprintf(DDSK,"sector %d cylin %d head %d addr %x\n",
369*41056Swilliam 	    sector, cylin, head, addr);
370*41056Swilliam #endif
371*41056Swilliam 
372*41056Swilliam 
373*41056Swilliam 	/* If this is a read operation, just go away until it's done.	*/
374*41056Swilliam 	if (bp->b_flags & B_READ) return;
375*41056Swilliam 
376*41056Swilliam 	/* Ready to send data?	*/
377*41056Swilliam 	while ((inb(wdc+wd_altsts) & WDCS_DRQ) == 0)
378*41056Swilliam 		nulldev();		/* So compiler won't optimize out */
379*41056Swilliam 
380*41056Swilliam 	/* ASSUMES CONTIGUOUS MEMORY */
381*41056Swilliam 	{ register buff_addr;
382*41056Swilliam 
383*41056Swilliam 	buff_addr = addr;
384*41056Swilliam 	buff_addr += (du->dk_skip * 512)/* & CLOFSET*/;
385*41056Swilliam 	outsw (wdc+wd_data, buff_addr, 256);
386*41056Swilliam 	}
387*41056Swilliam 	du->dk_bc -= 512;
388*41056Swilliam }
389*41056Swilliam 
390*41056Swilliam /*
391*41056Swilliam  * these are globally defined so they can be found
392*41056Swilliam  * by the debugger easily in the case of a system crash
393*41056Swilliam  */
394*41056Swilliam daddr_t wd_errsector;
395*41056Swilliam daddr_t wd_errbn;
396*41056Swilliam unsigned char wd_errstat;
397*41056Swilliam 
398*41056Swilliam /* Interrupt routine for the controller.  Acknowledge the interrupt, check for
399*41056Swilliam  * errors on the current operation, mark it done if necessary, and start
400*41056Swilliam  * the next request.  Also check for a partially done transfer, and
401*41056Swilliam  * continue with the next chunk if so.
402*41056Swilliam  */
403*41056Swilliam wdintr()
404*41056Swilliam {
405*41056Swilliam 	register struct	disk *du;
406*41056Swilliam 	register wdc = IO_WD0; /*XXX*/
407*41056Swilliam 	register struct buf *bp, *dp;
408*41056Swilliam 	int status;
409*41056Swilliam 	char partch ;
410*41056Swilliam 
411*41056Swilliam 	/* Shouldn't need this, but it may be a slow controller.	*/
412*41056Swilliam 	while ((status = inb(wdc+wd_altsts)) & WDCS_BUSY)
413*41056Swilliam 		nulldev();
414*41056Swilliam 	if (!wdtab.b_active) {
415*41056Swilliam 		printf("wd: extra interrupt\n");
416*41056Swilliam 		return;
417*41056Swilliam 	}
418*41056Swilliam 
419*41056Swilliam #ifdef	WDDEBUGx
420*41056Swilliam 	dprintf(DDSK,"I ");
421*41056Swilliam #endif
422*41056Swilliam 	dp = wdtab.b_actf;
423*41056Swilliam 	bp = dp->b_actf;
424*41056Swilliam 	du = &wddrives[WDUNIT(bp->b_dev)];
425*41056Swilliam 	partch = "abcdefgh"[minor(bp->b_dev)&7] ;
426*41056Swilliam 	if (DISKSTATE(du->dk_state) <= RDLABEL) {
427*41056Swilliam 		if (wdcontrol(bp))
428*41056Swilliam 			goto done;
429*41056Swilliam 		return;
430*41056Swilliam 	}
431*41056Swilliam 	if (status & (WDCS_ERR | WDCS_ECCCOR)) {
432*41056Swilliam #ifdef	WDDEBUG
433*41056Swilliam 		dprintf(DDSK|DPAUSE,"error %x\n", wd_errstat);
434*41056Swilliam #endif
435*41056Swilliam 		/*if (bp->b_flags & B_FORMAT) {
436*41056Swilliam 			du->dk_status = status;
437*41056Swilliam 			du->dk_error = wdp->wd_error;
438*41056Swilliam 			bp->b_flags |= B_ERROR;
439*41056Swilliam 			goto done;
440*41056Swilliam 		}*/
441*41056Swilliam 
442*41056Swilliam 		wd_errstat = inb(wdc+wd_error);		/* save error status */
443*41056Swilliam 		wd_errsector = (bp->b_cylin * du->dk_dd.dk_secpercyl) +
444*41056Swilliam 			(((unsigned long) bp->b_blkno * DEV_BSIZE /
445*41056Swilliam 			    du->dk_dd.dk_secsize) % du->dk_dd.dk_secpercyl) +
446*41056Swilliam 			du->dk_skip;
447*41056Swilliam 		wd_errbn = bp->b_blkno
448*41056Swilliam 			+ du->dk_skip * du->dk_dd.dk_secsize / DEV_BSIZE ;
449*41056Swilliam 		if (status & WDCS_ERR) {
450*41056Swilliam 			if (++wdtab.b_errcnt < RETRIES)
451*41056Swilliam 				wdtab.b_active = 0;
452*41056Swilliam 			else {
453*41056Swilliam 				printf("wd%d%c: ", du->dk_unit, partch);
454*41056Swilliam 				printf(
455*41056Swilliam 				"hard %s error, sn %d bn %d status %b error %b\n",
456*41056Swilliam 					(bp->b_flags & B_READ)? "read":"write",
457*41056Swilliam 					wd_errsector, wd_errbn, status, WDCS_BITS,
458*41056Swilliam 					wd_errstat, WDERR_BITS);
459*41056Swilliam 				bp->b_flags |= B_ERROR;	/* flag the error */
460*41056Swilliam 			}
461*41056Swilliam 		} else
462*41056Swilliam 			log(LOG_WARNING,"wd%d%c: soft ecc sn %d bn %d\n",
463*41056Swilliam 				du->dk_unit, partch, wd_errsector,
464*41056Swilliam 				wd_errbn);
465*41056Swilliam 	}
466*41056Swilliam 
467*41056Swilliam 	/*
468*41056Swilliam 	 * If this was a successful read operation, fetch the data.
469*41056Swilliam 	 */
470*41056Swilliam 	if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) {
471*41056Swilliam 		int chk, dummy;
472*41056Swilliam 
473*41056Swilliam 		chk = min(256,(du->dk_bc/2));
474*41056Swilliam 		/* Ready to receive data?	*/
475*41056Swilliam 		while ((inb(wdc+wd_status) & WDCS_DRQ) == 0)
476*41056Swilliam 			nulldev();
477*41056Swilliam 
478*41056Swilliam /*dprintf(DDSK,"addr %x\n", (int)bp->b_un.b_addr + du->dk_skip * 512);*/
479*41056Swilliam 		insw(wdc+wd_data,(int)bp->b_un.b_addr + du->dk_skip * 512 ,chk);
480*41056Swilliam 		du->dk_bc -= chk;
481*41056Swilliam 		while(chk++ < 256) insw(wdc+wd_data,&dummy,1);
482*41056Swilliam 	}
483*41056Swilliam 
484*41056Swilliam 	wdxfer[du->dk_unit]++;
485*41056Swilliam 	if (wdtab.b_active) {
486*41056Swilliam 		if ((bp->b_flags & B_ERROR) == 0) {
487*41056Swilliam 			du->dk_skip++;		/* Add to successful sectors. */
488*41056Swilliam 			if (wdtab.b_errcnt) {
489*41056Swilliam 				log(LOG_WARNING, "wd%d%c: ",
490*41056Swilliam 						du->dk_unit, partch);
491*41056Swilliam 				log(LOG_WARNING,
492*41056Swilliam 			"soft %s error, sn %d bn %d error %b retries %d\n",
493*41056Swilliam 				    (bp->b_flags & B_READ) ? "read" : "write",
494*41056Swilliam 				    wd_errsector, wd_errbn, wd_errstat,
495*41056Swilliam 				    WDERR_BITS, wdtab.b_errcnt);
496*41056Swilliam 			}
497*41056Swilliam 			wdtab.b_errcnt = 0;
498*41056Swilliam 
499*41056Swilliam 			/* see if more to transfer */
500*41056Swilliam 			if (du->dk_skip < bp->b_bcount / 512) {
501*41056Swilliam 				wdstart();
502*41056Swilliam 				return;		/* next chunk is started */
503*41056Swilliam 			}
504*41056Swilliam 		}
505*41056Swilliam 
506*41056Swilliam done:
507*41056Swilliam 		/* done with this transfer, with or without error */
508*41056Swilliam 		wdtab.b_actf = dp->b_forw;
509*41056Swilliam 		wdtab.b_errcnt = 0;
510*41056Swilliam 		du->dk_skip = 0;
511*41056Swilliam 		dp->b_active = 0;
512*41056Swilliam 		dp->b_actf = bp->av_forw;
513*41056Swilliam 		dp->b_errcnt = 0;
514*41056Swilliam 		bp->b_resid = 0;
515*41056Swilliam 		biodone(bp);
516*41056Swilliam 	}
517*41056Swilliam 	wdtab.b_active = 0;
518*41056Swilliam 	if (dp->b_actf)
519*41056Swilliam 		wdustart(du);		/* requeue disk if more io to do */
520*41056Swilliam 	if (wdtab.b_actf)
521*41056Swilliam 		wdstart();		/* start IO on next drive */
522*41056Swilliam }
523*41056Swilliam 
524*41056Swilliam /*
525*41056Swilliam  * Initialize a drive.
526*41056Swilliam  */
527*41056Swilliam wdopen(dev, flags)
528*41056Swilliam 	dev_t	dev;
529*41056Swilliam 	int	flags;
530*41056Swilliam {
531*41056Swilliam 	register unsigned int unit;
532*41056Swilliam 	register struct buf *bp;
533*41056Swilliam 	register struct disk *du;
534*41056Swilliam 	struct dkbad *db;
535*41056Swilliam 	int i, error = 0;
536*41056Swilliam 
537*41056Swilliam 	unit = WDUNIT(dev);
538*41056Swilliam dprintf(DDSK,"wdopen %x\n",unit);
539*41056Swilliam 	if (unit >= NWD) return (ENXIO) ;
540*41056Swilliam 	du = &wddrives[unit];
541*41056Swilliam 	if (du->dk_open){
542*41056Swilliam 		du->dk_open++ ;
543*41056Swilliam 		return(0);	/* already is open, don't mess with it */
544*41056Swilliam 	}
545*41056Swilliam #ifdef THE_BUG
546*41056Swilliam 	if (du->dk_state && DISKSTATE(du->dk_state) <= OPEN)
547*41056Swilliam 		return(0);
548*41056Swilliam #endif
549*41056Swilliam 	du->dk_unit = unit;
550*41056Swilliam 	wdutab[unit].b_actf = NULL;
551*41056Swilliam 	/*if (flags & O_NDELAY)
552*41056Swilliam 		du->dk_state = WANTOPENRAW;
553*41056Swilliam 	else*/
554*41056Swilliam 		du->dk_state = WANTOPEN;
555*41056Swilliam 	/*
556*41056Swilliam 	 * Use the default sizes until we've read the label,
557*41056Swilliam 	 * or longer if there isn't one there.
558*41056Swilliam 	 */
559*41056Swilliam 	du->dk_dd = dflt_sizes;
560*41056Swilliam 
561*41056Swilliam 	/*
562*41056Swilliam 	 * Recal, read of disk label will be done in wdcontrol
563*41056Swilliam 	 * during first read operation.
564*41056Swilliam 	 */
565*41056Swilliam 	bp = geteblk(512);
566*41056Swilliam 	bp->b_dev = dev;
567*41056Swilliam 	bp->b_blkno = bp->b_bcount = 0;
568*41056Swilliam 	bp->b_flags = B_READ;
569*41056Swilliam 	wdstrategy(bp);
570*41056Swilliam 	biowait(bp);
571*41056Swilliam 	if (bp->b_flags & B_ERROR) {
572*41056Swilliam 		u.u_error = 0; 	/* XXX */
573*41056Swilliam 		error = ENXIO;
574*41056Swilliam 		du->dk_state = CLOSED;
575*41056Swilliam 		goto done;
576*41056Swilliam 	}
577*41056Swilliam 	if (du->dk_state == OPENRAW) {
578*41056Swilliam 		du->dk_state = OPENRAW;
579*41056Swilliam 		goto done;
580*41056Swilliam 	}
581*41056Swilliam #ifdef notyet
582*41056Swilliam 	/*
583*41056Swilliam 	 * Read bad sector table into memory.
584*41056Swilliam 	 */
585*41056Swilliam 	i = 0;
586*41056Swilliam 	do {
587*41056Swilliam 		u.u_error = 0;				/* XXX */
588*41056Swilliam 		bp->b_flags = B_BUSY | B_READ;
589*41056Swilliam 		bp->b_blkno = du->dk_dd.dk_secperunit - du->dk_dd.dk_nsectors
590*41056Swilliam 			+ i;
591*41056Swilliam 		if (du->dk_dd.dk_secsize > DEV_BSIZE)
592*41056Swilliam 			bp->b_blkno *= du->dk_dd.dk_secsize / DEV_BSIZE;
593*41056Swilliam 		else
594*41056Swilliam 			bp->b_blkno /= DEV_BSIZE / du->dk_dd.dk_secsize;
595*41056Swilliam 		bp->b_bcount = du->dk_dd.dk_secsize;
596*41056Swilliam 		bp->b_cylin = du->dk_dd.dk_ncylinders - 1;
597*41056Swilliam 		wdstrategy(bp);
598*41056Swilliam 		biowait(bp);
599*41056Swilliam 	} while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
600*41056Swilliam 		i < du->dk_dd.dk_nsectors);
601*41056Swilliam 	db = (struct dkbad *)(bp->b_un.b_addr);
602*41056Swilliam 	if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 &&
603*41056Swilliam 	    db->bt_flag == DKBAD_MAGIC) {
604*41056Swilliam 		dkbad[unit] = *db;
605*41056Swilliam 		du->dk_state = OPEN;
606*41056Swilliam 	} else {
607*41056Swilliam 		printf("wd%d: %s bad-sector file\n", unit,
608*41056Swilliam 		    (bp->b_flags & B_ERROR) ? "can't read" : "format error in");
609*41056Swilliam 		u.u_error = 0;				/* XXX */
610*41056Swilliam 		/*error = ENXIO ;*/
611*41056Swilliam 		du->dk_state = OPENRAW;
612*41056Swilliam 	}
613*41056Swilliam #else
614*41056Swilliam 	du->dk_state = OPEN;
615*41056Swilliam #endif
616*41056Swilliam done:
617*41056Swilliam 	bp->b_flags = B_INVAL | B_AGE;
618*41056Swilliam 	brelse(bp);
619*41056Swilliam 	if (error == 0)
620*41056Swilliam 		du->dk_open = 1;
621*41056Swilliam 	return (error);
622*41056Swilliam }
623*41056Swilliam 
624*41056Swilliam /*
625*41056Swilliam  * Implement operations other than read/write.
626*41056Swilliam  * Called from wdstart or wdintr during opens and formats.
627*41056Swilliam  * Uses finite-state-machine to track progress of operation in progress.
628*41056Swilliam  * Returns 0 if operation still in progress, 1 if completed.
629*41056Swilliam  */
630*41056Swilliam wdcontrol(bp)
631*41056Swilliam 	register struct buf *bp;
632*41056Swilliam {
633*41056Swilliam 	register struct disk *du;
634*41056Swilliam 	register wdc = IO_WD0; /*XXX*/
635*41056Swilliam 	register unit;
636*41056Swilliam 	unsigned char  stat;
637*41056Swilliam 	int s, cnt;
638*41056Swilliam 	extern int bootdev, cyloffset;
639*41056Swilliam 
640*41056Swilliam 	cyloffset=290;
641*41056Swilliam 	du = &wddrives[WDUNIT(bp->b_dev)];
642*41056Swilliam 	unit = du->dk_unit;
643*41056Swilliam 	switch (DISKSTATE(du->dk_state)) {
644*41056Swilliam 
645*41056Swilliam 	tryagainrecal:
646*41056Swilliam 	case WANTOPEN:			/* set SDH, step rate, do restore */
647*41056Swilliam #ifdef	WDDEBUG
648*41056Swilliam 		dprintf(DDSK,"wd%d: recal ", unit);
649*41056Swilliam #endif
650*41056Swilliam 		s = splbio();		/* not called from intr level ... */
651*41056Swilliam 		outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
652*41056Swilliam 		wdtab.b_active = 1;
653*41056Swilliam 		outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
654*41056Swilliam 		du->dk_state++;
655*41056Swilliam 		splx(s);
656*41056Swilliam 		return(0);
657*41056Swilliam 
658*41056Swilliam 	case RECAL:
659*41056Swilliam 		if ((stat = inb(wdc+wd_altsts)) & WDCS_ERR) {
660*41056Swilliam 			printf("wd%d: recal", du->dk_unit);
661*41056Swilliam 			if (unit == 0) {
662*41056Swilliam 				printf(": status %b error %b\n",
663*41056Swilliam 					stat, WDCS_BITS,
664*41056Swilliam 					inb(wdc+wd_error), WDERR_BITS);
665*41056Swilliam 				if (++wdtab.b_errcnt < RETRIES)
666*41056Swilliam 					goto tryagainrecal;
667*41056Swilliam 			}
668*41056Swilliam 			goto badopen;
669*41056Swilliam 		}
670*41056Swilliam 		wdtab.b_errcnt = 0;
671*41056Swilliam 		if (ISRAWSTATE(du->dk_state)) {
672*41056Swilliam 			du->dk_state = OPENRAW;
673*41056Swilliam 			return(1);
674*41056Swilliam 		}
675*41056Swilliam retry:
676*41056Swilliam #ifdef	WDDEBUG
677*41056Swilliam 		dprintf(DDSK,"rdlabel ");
678*41056Swilliam #endif
679*41056Swilliam 		/*
680*41056Swilliam 		 * Read in sector 0 to get the pack label and geometry.
681*41056Swilliam 		 */
682*41056Swilliam 		outb(wdc+wd_precomp, 0xff);/* sometimes this is head bit 3 */
683*41056Swilliam 		outb(wdc+wd_seccnt, 1);
684*41056Swilliam 		outb(wdc+wd_sector, 1);
685*41056Swilliam 		/*if (bp->b_dev == bootdev) {
686*41056Swilliam 			(wdc+wd_cyl_lo = cyloffset & 0xff;
687*41056Swilliam 			(wdc+wd_cyl_hi = cyloffset >> 8;
688*41056Swilliam 		} else {
689*41056Swilliam 			(wdc+wd_cyl_lo = 0;
690*41056Swilliam 			(wdc+wd_cyl_hi = 0;
691*41056Swilliam 		}*/
692*41056Swilliam 		outb(wdc+wd_cyl_lo, (cyloffset & 0xff));
693*41056Swilliam 		outb(wdc+wd_cyl_hi, (cyloffset >> 8));
694*41056Swilliam 		outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
695*41056Swilliam 		outb(wdc+wd_command, WDCC_READ);
696*41056Swilliam 		du->dk_state = RDLABEL;
697*41056Swilliam 		return(0);
698*41056Swilliam 
699*41056Swilliam 	case RDLABEL:
700*41056Swilliam 		if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
701*41056Swilliam 			if (++wdtab.b_errcnt < RETRIES)
702*41056Swilliam 				goto retry;
703*41056Swilliam 			printf("wd%d: read label", unit);
704*41056Swilliam 			goto badopen;
705*41056Swilliam 		}
706*41056Swilliam 
707*41056Swilliam 		insw(wdc+wd_data, bp->b_un.b_addr, 256);
708*41056Swilliam 
709*41056Swilliam 		if (((struct disklabel *)
710*41056Swilliam 		    (bp->b_un.b_addr + LABELOFFSET))->dk_magic == DISKMAGIC) {
711*41056Swilliam 		       du->dk_dd =
712*41056Swilliam 			 * (struct disklabel *) (bp->b_un.b_addr + LABELOFFSET);
713*41056Swilliam 		} else {
714*41056Swilliam 			printf("wd%d: bad disk label\n", du->dk_unit);
715*41056Swilliam 			du->dk_state = OPENRAW;
716*41056Swilliam 		}
717*41056Swilliam 		if (du->dk_state == RDLABEL)
718*41056Swilliam 			du->dk_state = RDBADTBL;
719*41056Swilliam 		/*
720*41056Swilliam 		 * The rest of the initialization can be done
721*41056Swilliam 		 * by normal means.
722*41056Swilliam 		 */
723*41056Swilliam 		return(1);
724*41056Swilliam 
725*41056Swilliam 	default:
726*41056Swilliam 		panic("wdcontrol %x", du->dk_state );
727*41056Swilliam 	}
728*41056Swilliam 	/* NOTREACHED */
729*41056Swilliam 
730*41056Swilliam badopen:
731*41056Swilliam 	printf(": status %b error %b\n",
732*41056Swilliam 		stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
733*41056Swilliam 	du->dk_state = OPENRAW;
734*41056Swilliam 	return(1);
735*41056Swilliam }
736*41056Swilliam 
737*41056Swilliam wdclose(dev)
738*41056Swilliam 	dev_t dev;
739*41056Swilliam {	struct disk *du;
740*41056Swilliam 
741*41056Swilliam 	du = &wddrives[WDUNIT(dev)];
742*41056Swilliam 	du->dk_open-- ;
743*41056Swilliam 	/*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */
744*41056Swilliam }
745*41056Swilliam 
746*41056Swilliam wdioctl(dev,cmd,addr,flag)
747*41056Swilliam 	dev_t dev;
748*41056Swilliam 	caddr_t addr;
749*41056Swilliam {
750*41056Swilliam 	int unit = WDUNIT(dev);
751*41056Swilliam 	register struct disk *du;
752*41056Swilliam 	int error = 0;
753*41056Swilliam 	struct uio auio;
754*41056Swilliam 	struct iovec aiov;
755*41056Swilliam 	/*int wdformat();*/
756*41056Swilliam 
757*41056Swilliam 	du = &wddrives[unit];
758*41056Swilliam 
759*41056Swilliam 	switch (cmd) {
760*41056Swilliam 
761*41056Swilliam 	case DIOCGDINFO:
762*41056Swilliam 		*(struct disklabel *)addr = du->dk_dd;
763*41056Swilliam 		break;
764*41056Swilliam 
765*41056Swilliam 	case DIOCGDINFOP:
766*41056Swilliam 		*(struct disklabel **)addr = &(du->dk_dd);
767*41056Swilliam 		break;
768*41056Swilliam 
769*41056Swilliam #ifdef notyet
770*41056Swilliam 	case DIOCWFORMAT:
771*41056Swilliam 		if ((flag & FWRITE) == 0)
772*41056Swilliam 			error = EBADF;
773*41056Swilliam 		else {
774*41056Swilliam 			register struct format_op *fop;
775*41056Swilliam 
776*41056Swilliam 			fop = (struct format_op *)addr;
777*41056Swilliam 			aiov.iov_base = fop->df_buf;
778*41056Swilliam 			aiov.iov_len = fop->df_count;
779*41056Swilliam 			auio.uio_iov = &aiov;
780*41056Swilliam 			auio.uio_iovcnt = 1;
781*41056Swilliam 			auio.uio_resid = fop->df_count;
782*41056Swilliam 			auio.uio_segflg = 0;
783*41056Swilliam 			auio.uio_offset =
784*41056Swilliam 				fop->df_startblk * du->dk_dd.dk_secsize;
785*41056Swilliam 			error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE,
786*41056Swilliam 				minphys, &auio);
787*41056Swilliam 			fop->df_count -= auio.uio_resid;
788*41056Swilliam 			fop->df_reg[0] = du->dk_status;
789*41056Swilliam 			fop->df_reg[1] = du->dk_error;
790*41056Swilliam 		}
791*41056Swilliam 		break;
792*41056Swilliam #endif
793*41056Swilliam 
794*41056Swilliam 	default:
795*41056Swilliam 		error = ENOTTY;
796*41056Swilliam 		break;
797*41056Swilliam 	}
798*41056Swilliam 	return (error);
799*41056Swilliam }
800*41056Swilliam 
801*41056Swilliam /*wdformat(bp)
802*41056Swilliam 	struct buf *bp;
803*41056Swilliam {
804*41056Swilliam 
805*41056Swilliam 	bp->b_flags |= B_FORMAT;
806*41056Swilliam 	return (wdstrategy(bp));
807*41056Swilliam }*/
808*41056Swilliam 
809*41056Swilliam /*
810*41056Swilliam  * Routines to do raw IO for a unit.
811*41056Swilliam  */
812*41056Swilliam wdread(dev, uio)			/* character read routine */
813*41056Swilliam 	dev_t dev;
814*41056Swilliam 	struct uio *uio;
815*41056Swilliam {
816*41056Swilliam 	int unit = WDUNIT(dev) ;
817*41056Swilliam 
818*41056Swilliam 	if (unit >= NWD) return(ENXIO);
819*41056Swilliam 	return(physio(wdstrategy, &rwdbuf[unit], dev, B_READ, minphys, uio));
820*41056Swilliam }
821*41056Swilliam 
822*41056Swilliam 
823*41056Swilliam wdwrite(dev, uio)			/* character write routine */
824*41056Swilliam 	dev_t dev;
825*41056Swilliam 	struct uio *uio;
826*41056Swilliam {
827*41056Swilliam 	int unit = WDUNIT(dev) ;
828*41056Swilliam 
829*41056Swilliam 	if (unit >= NWD) return(ENXIO);
830*41056Swilliam 	return(physio(wdstrategy, &rwdbuf[unit], dev, B_WRITE, minphys, uio));
831*41056Swilliam }
832*41056Swilliam 
833*41056Swilliam wdsize(dev)
834*41056Swilliam 	dev_t dev;
835*41056Swilliam {
836*41056Swilliam 	register unit = WDUNIT(dev) ;
837*41056Swilliam 	register xunit = minor(dev) & 07;
838*41056Swilliam 	register struct disk *du;
839*41056Swilliam 	register val ;
840*41056Swilliam 
841*41056Swilliam 	return(8704);
842*41056Swilliam #ifdef notdef
843*41056Swilliam 	if (unit >= NWD) return(-1);
844*41056Swilliam 	if (wddrives[unit].dk_state == 0) /*{
845*41056Swilliam 		val = wdopen (dev, 0) ;
846*41056Swilliam 		if (val < 0) return (val) ;
847*41056Swilliam 	}*/	return (-1) ;
848*41056Swilliam 	du = &wddrives[unit];
849*41056Swilliam 	return((int)((u_long)du->dk_dd.dk_partition[xunit].nblocks *
850*41056Swilliam 		du->dk_dd.dk_secsize / 512));
851*41056Swilliam #endif
852*41056Swilliam }
853*41056Swilliam 
854*41056Swilliam wddump(dev)			/* dump core after a system crash */
855*41056Swilliam 	dev_t dev;
856*41056Swilliam {
857*41056Swilliam #ifdef notyet
858*41056Swilliam 	register struct disk *du;	/* disk unit to do the IO */
859*41056Swilliam 	register struct wd1010 *wdp = (struct wd1010 *) VA_WD;
860*41056Swilliam 	register struct bt_bad *bt_ptr;
861*41056Swilliam 	long	num;			/* number of sectors to write */
862*41056Swilliam 	int	unit, xunit;
863*41056Swilliam 	long	cyloff, blknum, blkcnt;
864*41056Swilliam 	long	cylin, head, sector;
865*41056Swilliam 	long	secpertrk, secpercyl, nblocks, i;
866*41056Swilliam 	register char *addr;
867*41056Swilliam 	char	*end;
868*41056Swilliam 	extern	int dumplo, totalclusters;
869*41056Swilliam 	static  wddoingadump = 0 ;
870*41056Swilliam 
871*41056Swilliam 	addr = (char *) PA_RAM;		/* starting address */
872*41056Swilliam 	/* size of memory to dump */
873*41056Swilliam 	num = totalclusters * CLSIZE - PA_RAM / PGSIZE;
874*41056Swilliam 	unit = WDUNIT(dev) ;		/* eventually support floppies? */
875*41056Swilliam 	xunit = minor(dev) & 7;		/* file system */
876*41056Swilliam 	/* check for acceptable drive number */
877*41056Swilliam 	if (unit >= NWD) return(ENXIO);
878*41056Swilliam 
879*41056Swilliam 	du = &wddrives[unit];
880*41056Swilliam 	/* was it ever initialized ? */
881*41056Swilliam 	if (du->dk_state < OPEN) return (ENXIO) ;
882*41056Swilliam 
883*41056Swilliam 	/* Convert to disk sectors */
884*41056Swilliam 	num = (u_long) num * PGSIZE / du->dk_dd.dk_secsize;
885*41056Swilliam 
886*41056Swilliam 	/* check if controller active */
887*41056Swilliam 	/*if (wdtab.b_active) return(EFAULT); */
888*41056Swilliam 	if (wddoingadump) return(EFAULT);
889*41056Swilliam 
890*41056Swilliam 	secpertrk = du->dk_dd.dk_nsectors;
891*41056Swilliam 	secpercyl = du->dk_dd.dk_secpercyl;
892*41056Swilliam 	nblocks = du->dk_dd.dk_partition[xunit].nblocks;
893*41056Swilliam 	cyloff = du->dk_dd.dk_partition[xunit].cyloff;
894*41056Swilliam 
895*41056Swilliam 	/* check transfer bounds against partition size */
896*41056Swilliam 	if ((dumplo < 0) || ((dumplo + num) >= nblocks))
897*41056Swilliam 		return(EINVAL);
898*41056Swilliam 
899*41056Swilliam 	/*wdtab.b_active = 1;		/* mark controller active for if we
900*41056Swilliam 					   panic during the dump */
901*41056Swilliam 	wddoingadump = 1  ;  i = 100000 ;
902*41056Swilliam 	while ((wdp->wd_status & WDCS_BUSY) && (i-- > 0)) nulldev() ;
903*41056Swilliam 	inb(wdc+wd_sdh = du->dk_sdh ;
904*41056Swilliam 	inb(wdc+wd_command = WDCC_RESTORE | WD_STEP;
905*41056Swilliam 	while (inb(wdc+wd_status & WDCS_BUSY) nulldev() ;
906*41056Swilliam 
907*41056Swilliam 	blknum = dumplo;
908*41056Swilliam 	while (num > 0) {
909*41056Swilliam #ifdef notdef
910*41056Swilliam 		if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER;
911*41056Swilliam 		if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
912*41056Swilliam 			blkcnt = secpercyl - (blknum % secpercyl);
913*41056Swilliam 			    /* keep transfer within current cylinder */
914*41056Swilliam #endif
915*41056Swilliam 
916*41056Swilliam 		/* compute disk address */
917*41056Swilliam 		cylin = blknum / secpercyl;
918*41056Swilliam 		head = (blknum % secpercyl) / secpertrk;
919*41056Swilliam 		sector = blknum % secpertrk;
920*41056Swilliam 		sector++;		/* origin 1 */
921*41056Swilliam 		cylin += cyloff + 290;
922*41056Swilliam 
923*41056Swilliam 		/*
924*41056Swilliam 		 * See if the current block is in the bad block list.
925*41056Swilliam 		 * (If we have one.)
926*41056Swilliam 		 */
927*41056Swilliam 	    		for (bt_ptr = dkbad[unit].bt_bad;
928*41056Swilliam 				bt_ptr->bt_cyl != -1; bt_ptr++) {
929*41056Swilliam 			if (bt_ptr->bt_cyl > cylin)
930*41056Swilliam 				/* Sorted list, and we passed our cylinder.
931*41056Swilliam 					quit. */
932*41056Swilliam 				break;
933*41056Swilliam 			if (bt_ptr->bt_cyl == cylin &&
934*41056Swilliam 				bt_ptr->bt_trksec == (head << 8) + sector) {
935*41056Swilliam 			/*
936*41056Swilliam 			 * Found bad block.  Calculate new block addr.
937*41056Swilliam 			 * This starts at the end of the disk (skip the
938*41056Swilliam 			 * last track which is used for the bad block list),
939*41056Swilliam 			 * and works backwards to the front of the disk.
940*41056Swilliam 			 */
941*41056Swilliam 				blknum = (du->dk_dd.dk_secperunit)
942*41056Swilliam 					- du->dk_dd.dk_nsectors
943*41056Swilliam 					- (bt_ptr - dkbad[unit].bt_bad) - 1;
944*41056Swilliam 				cylin = blknum / secpercyl;
945*41056Swilliam 				head = (blknum % secpercyl) / secpertrk;
946*41056Swilliam 				sector = blknum % secpertrk;
947*41056Swilliam 				break;
948*41056Swilliam 			}
949*41056Swilliam 
950*41056Swilliam 		/* select drive.     */
951*41056Swilliam 		inb(wdc+wd_sdh = du->dk_sdh | (head&07);
952*41056Swilliam 		while ((inb(wdc+wd_status & WDCS_READY) == 0) nulldev();
953*41056Swilliam 
954*41056Swilliam 		/* transfer some blocks */
955*41056Swilliam 		inb(wdc+wd_sector = sector;
956*41056Swilliam 		inb(wdc+wd_seccnt = 1;
957*41056Swilliam 		inb(wdc+wd_cyl_lo = cylin;
958*41056Swilliam 		if (du->dk_dd.dk_ntracks > 8) {
959*41056Swilliam 			if (head > 7)
960*41056Swilliam 				inb(wdc+wd_precomp = 0;	/* set 3rd head bit */
961*41056Swilliam 			else
962*41056Swilliam 				inb(wdc+wd_precomp = 0xff;	/* set 3rd head bit */
963*41056Swilliam 		}
964*41056Swilliam 		inb(wdc+wd_cyl_hi = cylin >> 8;
965*41056Swilliam #ifdef notdef
966*41056Swilliam 		/* lets just talk about this first...*/
967*41056Swilliam 		printf ("sdh 0%o sector %d cyl %d addr 0x%x\n",
968*41056Swilliam 			wdp->wd_sdh, wdp->wd_sector,
969*41056Swilliam 			wdp->wd_cyl_hi*256+wdp->wd_cyl_lo, addr) ;
970*41056Swilliam 		for (i=10000; i > 0 ; i--)
971*41056Swilliam 			;
972*41056Swilliam 		continue;
973*41056Swilliam #endif
974*41056Swilliam 		inb(wdc+wd_command = WDCC_WRITE;
975*41056Swilliam 
976*41056Swilliam 		/* Ready to send data?	*/
977*41056Swilliam 		while ((inb(wdc+wd_status & WDCS_DRQ) == 0) nulldev();
978*41056Swilliam 		if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ;
979*41056Swilliam 
980*41056Swilliam 		end = (char *)addr + du->dk_dd.dk_secsize;
981*41056Swilliam 		for (; addr < end; addr += 8) {
982*41056Swilliam 			wdp->wd_data = addr[0];
983*41056Swilliam 			wdp->wd_data = addr[1];
984*41056Swilliam 			wdp->wd_data = addr[2];
985*41056Swilliam 			wdp->wd_data = addr[3];
986*41056Swilliam 			wdp->wd_data = addr[4];
987*41056Swilliam 			wdp->wd_data = addr[5];
988*41056Swilliam 			wdp->wd_data = addr[6];
989*41056Swilliam 			wdp->wd_data = addr[7];
990*41056Swilliam 		}
991*41056Swilliam 		if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ;
992*41056Swilliam 		/* Check data request (should be done).         */
993*41056Swilliam 		if (inb(wdc+wd_status & WDCS_DRQ) return(EIO) ;
994*41056Swilliam 
995*41056Swilliam 		/* wait for completion */
996*41056Swilliam 		for ( i = 1000000 ; inb(wdc+wd_status & WDCS_BUSY ; i--) {
997*41056Swilliam 				if (i < 0) return (EIO) ;
998*41056Swilliam 				nulldev () ;
999*41056Swilliam 		}
1000*41056Swilliam 		/* error check the xfer */
1001*41056Swilliam 		if (inb(wdc+wd_status & WDCS_ERR) return(EIO) ;
1002*41056Swilliam 		/* update block count */
1003*41056Swilliam 		num--;
1004*41056Swilliam 		blknum++ ;
1005*41056Swilliam #ifdef	WDDEBUG
1006*41056Swilliam if (num % 100 == 0) printf(".") ;
1007*41056Swilliam #endif
1008*41056Swilliam 	}
1009*41056Swilliam 	return(0);
1010*41056Swilliam #endif
1011*41056Swilliam }
1012*41056Swilliam #endif
1013