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