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