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