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