xref: /csrg-svn/sys/tahoe/vba/vd.c (revision 30519)
1 /*	vd.c	1.14	87/02/18	*/
2 
3 #include "dk.h"
4 #if NVD > 0
5 /*
6  * Versabus VDDC/SMDE driver.
7  */
8 #include "param.h"
9 #include "buf.h"
10 #include "cmap.h"
11 #include "conf.h"
12 #include "dir.h"
13 #include "dkstat.h"
14 #include "disklabel.h"
15 #include "map.h"
16 #include "file.h"
17 #include "systm.h"
18 #include "user.h"
19 #include "vmmac.h"
20 #include "proc.h"
21 #include "uio.h"
22 #include "syslog.h"
23 #include "kernel.h"
24 #include "ioctl.h"
25 
26 #include "../tahoe/cpu.h"
27 #include "../tahoe/mtpr.h"
28 #include "../tahoe/pte.h"
29 
30 #include "../tahoevba/vbavar.h"
31 #include "../tahoevba/vdreg.h"
32 
33 #define	COMPAT_42
34 
35 #define	VDMAXIO		(MAXBPTE*NBPG)
36 
37 #define vdunit(dev)	(minor(dev) >> 3)
38 #define vdpart(dev)	(minor(dev) & 0x07)
39 #define	vdminor(unit,part)	(((unit) << 3) | (part))
40 
41 struct	vba_ctlr *vdminfo[NVD];
42 struct  vba_device *vddinfo[NDK];
43 int	vdprobe(), vdslave(), vdattach(), vddgo();
44 long	vdaddr[] = { 0xffff2000, 0xffff2100, 0xffff2200, 0xffff2300, 0 };
45 struct	vba_driver vddriver =
46   { vdprobe, vdslave, vdattach, vddgo, vdaddr, "dk", vddinfo, "vd", vdminfo };
47 
48 /*
49  * Per-controller state.
50  */
51 struct vdsoftc {
52 	u_short	vd_flags;
53 #define	VD_INIT		0x1	/* controller initialized */
54 #define	VD_STARTED	0x2	/* start command issued */
55 #define	VD_DOSEEKS	0x4	/* should overlap seeks */
56 	u_short	vd_type;	/* controller type */
57 	u_short	vd_wticks;	/* timeout */
58 	u_short	vd_offcyl;	/* off cylinder bitmask */
59 	struct	mdcb vd_mdcb;	/* master command block */
60 	u_long	vd_mdcbphys;	/* physical address of vd_mdcb */
61 	struct	dcb vd_dcb;	/* i/o command block */
62 	u_long	vd_dcbphys;	/* physical address of vd_dcb */
63 	struct	pte *vd_map;	/* i/o page map */
64 	caddr_t	vd_utl;		/* mapped i/o space */
65 	caddr_t	vd_rawbuf;	/* buffer for raw+swap i/o */
66 } vdsoftc[NVD];
67 
68 /*
69  * Per-drive state.
70  */
71 struct	dksoftc {
72 	u_short	dk_state;	/* open fsm */
73 	u_short	dk_openpart;	/* units open on this drive */
74 	u_short	dk_bshift;	/* shift for * (DEV_BSIZE / sectorsize) XXX */
75 	u_short	dk_curdaddr;	/* last selected track & sector */
76 	u_int	dk_curcyl;	/* last selected cylinder */
77 	struct	dcb dk_dcb;	/* seek command block */
78 	u_long	dk_dcbphys;	/* physical address of dk_dcb */
79 } dksoftc[NDK];
80 
81 /*
82  * Drive states.  Used during steps of open/initialization.
83  * States < OPEN (> 0) are transient, during an open operation.
84  * OPENRAW is used for unabeled disks, to allow format operations.
85  */
86 #define	CLOSED		0		/* disk is closed */
87 #define	WANTOPEN	1		/* open requested, not started */
88 #define	WANTOPENRAW	2		/* open requested, no label */
89 #define	RDLABEL		3		/* reading pack label */
90 #define	OPEN		4		/* intialized and ready */
91 #define	OPENRAW		5		/* open, no label */
92 
93 struct	buf rdkbuf[NDK];	/* raw i/o buffer headers */
94 struct	buf dkutab[NDK];	/* i/o queue headers */
95 struct	disklabel dklabel[NDK];	/* pack labels */
96 
97 #define b_cylin	b_resid
98 #define	b_daddr	b_error
99 
100 int	vdwstart, vdwatch();
101 
102 /*
103  * See if the controller is really there; if so, initialize it.
104  */
105 vdprobe(reg, vm)
106 	caddr_t reg;
107 	struct vba_ctlr *vm;
108 {
109 	register br, cvec;		/* must be r12, r11 */
110 	register struct vddevice *vdaddr = (struct vddevice *)reg;
111 	struct vdsoftc *vd;
112 
113 #ifdef lint
114 	br = 0; cvec = br; br = cvec;
115 	vdintr(0);
116 #endif
117 	if (badaddr((caddr_t)reg, 2))
118 		return (0);
119 	vd = &vdsoftc[vm->um_ctlr];
120 	vdaddr->vdreset = 0xffffffff;
121 	DELAY(1000000);
122 	if (vdaddr->vdreset != (unsigned)0xffffffff) {
123 		vd->vd_type = VDTYPE_VDDC;
124 		vd->vd_flags &= ~VD_DOSEEKS;
125 		DELAY(1000000);
126 	} else {
127 		vd->vd_type = VDTYPE_SMDE;
128 		vd->vd_flags |= VD_DOSEEKS;
129 		vdaddr->vdrstclr = 0;
130 		DELAY(3000000);
131 		vdaddr->vdcsr = 0;
132 		vdaddr->vdtcf_mdcb = AM_ENPDA;
133 		vdaddr->vdtcf_dcb = AM_ENPDA;
134 		vdaddr->vdtcf_trail = AM_ENPDA;
135 		vdaddr->vdtcf_data = AM_ENPDA;
136 		vdaddr->vdccf = CCF_SEN | CCF_DER | CCF_STS |
137 		    XMD_32BIT | BSZ_16WRD |
138 		    CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR;
139 	}
140 	vd->vd_mdcbphys = vtoph((struct proc *)0, (unsigned)&vd->vd_mdcb);
141 	vd->vd_dcbphys = vtoph((struct proc *)0, (unsigned)&vd->vd_dcb);
142 	vm->um_addr = reg;		/* XXX */
143 	if (!vdcmd(vm, VDOP_INIT, 10) || !vdcmd(vm, VDOP_DIAG, 10)) {
144 		printf("vd%d: %s cmd failed\n", vm->um_ctlr,
145 		    vd->vd_dcb.opcode == VDOP_INIT ? "init" : "diag");
146 		return (0);
147 	}
148 	/*
149 	 * Allocate page tables and i/o buffer.
150 	 */
151 	vbmapalloc(btoc(VDMAXIO)+1, &vd->vd_map, &vd->vd_utl);
152 	vd->vd_rawbuf = calloc(VDMAXIO);
153 	br = 0x17, cvec = 0xe0 + vm->um_ctlr;	/* XXX */
154 	return (sizeof (struct vddevice));
155 }
156 
157 /*
158  * See if a drive is really there.
159  *
160  * Can't read pack label here as various data structures
161  * aren't setup for doing a read in a straightforward
162  * manner.  Instead just probe for the drive and leave
163  * the pack label stuff to the attach routine.
164  */
165 vdslave(vi, addr)
166 	register struct vba_device *vi;
167 	struct vddevice *vdaddr;
168 {
169 	register struct disklabel *lp = &dklabel[vi->ui_unit];
170 	struct vdsoftc *vd = &vdsoftc[vi->ui_ctlr];
171 
172 	if ((vd->vd_flags&VD_INIT) == 0) {
173 		printf("vd%d: %s controller\n", vi->ui_ctlr,
174 		    vd->vd_type == VDTYPE_VDDC ? "VDDC" : "SMDE");
175 		vd->vd_flags |= VD_INIT;
176 	}
177 
178 	/*
179 	 * Initialize label enough to do a reset on
180 	 * the drive.  The remainder of the default
181 	 * label values will be filled in in vdinit
182 	 * at attach time.
183 	 */
184 	lp->d_secsize = DEV_BSIZE / 2;		/* XXX */
185 	lp->d_nsectors = 32;
186 	lp->d_ntracks = 24;
187 	lp->d_ncylinders = 711;
188 	lp->d_secpercyl = 32*24;
189 	return (vdreset_drive(vi));
190 }
191 
192 /*
193  * Read pack label.
194  */
195 vdattach(vi)
196 	register struct vba_device *vi;
197 {
198 	register int unit = vi->ui_unit;
199 	register struct dksoftc *dk = &dksoftc[unit];
200 	register struct disklabel *lp;
201 
202 	if (vdwstart == 0) {
203 		timeout(vdwatch, (caddr_t)0, hz);
204 		vdwstart++;
205 	}
206 	/*
207 	 * Try to initialize device and read pack label.
208 	 */
209 	if (vdinit(vdminor(unit, 0), 0) != 0) {
210 		printf(": unknown drive type");
211 		return;
212 	}
213 	/*
214 	 * Initialize invariant portion of
215 	 * dcb used for overlapped seeks.
216 	 */
217 	dk->dk_dcb.opcode = VDOP_SEEK;
218 	dk->dk_dcb.intflg = DCBINT_NONE | DCBINT_PBA;
219 	dk->dk_dcb.devselect = vi->ui_slave;
220 	dk->dk_dcb.trailcnt = sizeof (trseek) / sizeof (long);
221 	dk->dk_dcb.trail.sktrail.skaddr.sector = 0;
222 	dk->dk_dcbphys = vtoph((struct proc *)0, (unsigned)&dk->dk_dcb);
223 	lp = &dklabel[unit];
224 	printf(": %s <ntrak %d, ncyl %d, nsec %d>",
225 	    lp->d_typename, lp->d_ntracks, lp->d_ncylinders, lp->d_nsectors);
226 	/*
227 	 * (60 / rpm) / (sectors per track * (bytes per sector / 2))
228 	 */
229 	if (vi->ui_dk >= 0)
230 		dk_mspw[vi->ui_dk] = 120.0 /
231 		    (lp->d_rpm * lp->d_nsectors * lp->d_secsize);
232 #ifdef notyet
233 	addwap(makedev(VDMAJOR, vdminor(unit, 0)), &dklabel[unit]);
234 #endif
235 }
236 
237 /*ARGSUSED*/
238 vdopen(dev, flags)
239 	dev_t dev;
240 	int flags;
241 {
242 	register unit = vdunit(dev);
243 	register struct disklabel *lp;
244 	register struct dksoftc *dk;
245 	register struct partition *pp;
246 	struct vba_device *vi;
247 	int s, error, part = vdpart(dev);
248 	daddr_t start, end;
249 
250 	if (unit >= NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0)
251 		return (ENXIO);
252 	lp = &dklabel[unit];
253 	dk = &dksoftc[unit];
254 
255 	s = spl7();
256 	while (dk->dk_state != OPEN && dk->dk_state != OPENRAW &&
257 	    dk->dk_state != CLOSED)
258 		sleep((caddr_t)dk, PZERO+1);
259 	splx(s);
260 	if (dk->dk_state != OPEN && dk->dk_state != OPENRAW)
261 		if (error = vdinit(dev, flags))
262 			return (error);
263 	/*
264 	 * Warn if a partion is opened
265 	 * that overlaps another partition which is open
266 	 * unless one is the "raw" partition (whole disk).
267 	 */
268 #define	RAWPART		2		/* 'c' partition */	/* XXX */
269 	if ((dk->dk_openpart & (1 << part)) == 0 &&
270 	    part != RAWPART) {
271 		pp = &lp->d_partitions[part];
272 		start = pp->p_offset;
273 		end = pp->p_offset + pp->p_size;
274 		for (pp = lp->d_partitions;
275 		     pp < &lp->d_partitions[lp->d_npartitions]; pp++) {
276 			if (pp->p_offset + pp->p_size <= start ||
277 			    pp->p_offset >= end)
278 				continue;
279 			if (pp - lp->d_partitions == RAWPART)
280 				continue;
281 			if (dk->dk_openpart & (1 << (pp - lp->d_partitions)))
282 				log(LOG_WARNING,
283 				    "dk%d%c: overlaps open partition (%c)\n",
284 				    unit, part + 'a',
285 				    pp - lp->d_partitions + 'a');
286 		}
287 	}
288 	if (part >= lp->d_npartitions)
289 		return (ENXIO);
290 	dk->dk_openpart |= 1 << part;
291 	return (0);
292 }
293 
294 vdclose(dev, flags)
295 	dev_t dev;
296 	int flags;
297 {
298 	register int unit = vdunit(dev);
299 	register struct dksoftc *dk = &dksoftc[unit];
300 
301 	dk->dk_openpart &= ~(1 << vdpart(dev));
302 #ifdef notdef
303 	/*
304 	 * Should wait for i/o to complete on this partition
305 	 * even if others are open, but wait for work on blkflush().
306 	 */
307 	if (dk->dk_openpart == 0) {
308 		struct vba_device *vi = vddinfo[unit];
309 		int s;
310 
311 		s = spl7();
312 		/* can't sleep on b_actf, it might be async. */
313 		while (vi->ui_tab.b_actf)
314 			sleep((caddr_t)&vi->ui_tab.b_actf, PZERO-1);
315 		splx(s);
316 		dk->dk_state = CLOSED;
317 	}
318 #endif
319 }
320 
321 vdinit(dev, flags)
322 	dev_t dev;
323 	int flags;
324 {
325 	register struct buf *bp = NULL;
326 	register struct disklabel *lp;
327 	register struct dksoftc *dk;
328 	struct vba_device *vi;
329 	struct disklabel *dlp;
330 	int unit = vdunit(dev), error = 0;
331 	extern int cold;
332 
333 	dk = &dksoftc[unit];
334 	if (flags & O_NDELAY) {
335 		dk->dk_state = OPENRAW;
336 		goto done;
337 	}
338 
339 	/*
340 	 * Initialize portion of the label
341 	 * not set up in the slave routine.
342 	 */
343 	dk->dk_bshift = 1;		/* DEV_BSIZE / 512 */
344 	dk->dk_state = RDLABEL;
345 	lp = &dklabel[unit];
346 	lp->d_secperunit = 0x1fffffff;
347 	lp->d_npartitions = 1;
348 	lp->d_partitions[0].p_size = 0x1fffffff;
349 	lp->d_partitions[0].p_offset = 0;
350 
351 	bp = geteblk(DEV_BSIZE);		/* max sector size */
352 	bp->b_dev = dev;
353 	bp->b_blkno = LABELSECTOR;
354 	bp->b_bcount = DEV_BSIZE;
355 	bp->b_flags = B_BUSY | B_READ;
356 	bp->b_cylin = LABELSECTOR / lp->d_secpercyl;
357 	vdstrategy(bp);
358 	biowait(bp);
359 	if (bp->b_flags & B_ERROR) {
360 		error = u.u_error;		/* XXX */
361 		u.u_error = 0;
362 		dk->dk_state = CLOSED;
363 		goto done;
364 	}
365 	vi = vddinfo[unit];
366 	dlp = (struct disklabel *)(bp->b_un.b_addr + LABELOFFSET);
367 	if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC &&
368 	    dkcksum(dlp) == 0) {
369 		*lp = *dlp;
370 		/*
371 		 * Now that we have the label, configure
372 		 * the correct drive parameters.
373 		 */
374 		if (!vdreset_drive(vi))
375 			dk->dk_state = CLOSED;
376 		else
377 			dk->dk_state = OPEN;
378 	} else {
379 		if (cold)
380 			printf(": no disk label");
381 		else
382 			log(LOG_ERR, "dk%d: no disk label\n", vi->ui_unit);
383 #ifdef COMPAT_42
384 		if (!vdmaptype(vi, lp)) {
385 			error = ENXIO;
386 			dk->dk_state = CLOSED;
387 		} else
388 			dk->dk_state = OPEN;
389 #else
390 		dk->dk_state = OPENRAW;
391 #endif
392 	}
393 done:
394 	/*
395 	 * If open, calculate scaling shift for
396 	 * mapping DEV_BSIZE blocks to drive sectors.
397 	 */
398 	if (dk->dk_state == OPEN || dk->dk_state == OPENRAW) {
399 		int mul = DEV_BSIZE / lp->d_secsize;
400 		dk->dk_bshift = 0;
401 		while ((mul >>= 1) > 0)
402 			dk->dk_bshift++;
403 	}
404 	if (bp) {
405 		bp->b_flags = B_INVAL | B_AGE;
406 		brelse(bp);
407 	}
408 	wakeup((caddr_t)dk);
409 	return (error);
410 }
411 
412 /*ARGSUSED*/
413 vddgo(vm)
414 	struct vba_device *vm;
415 {
416 
417 }
418 
419 vdstrategy(bp)
420 	register struct buf *bp;
421 {
422 	register struct vba_device *vi;
423 	register struct disklabel *lp;
424 	register struct dksoftc *dk;
425 	register int unit;
426 	struct buf *dp;
427 	daddr_t sz, sn, maxsz;
428 	int part, s;
429 
430 	sz = bp->b_bcount;
431 	sz = (sz + DEV_BSIZE - 1) >> DEV_BSHIFT;
432 	unit = vdunit(bp->b_dev);
433 	if (unit > NDK) {
434 		bp->b_error = ENXIO;
435 		goto bad;
436 	}
437 	vi = vddinfo[unit];
438 	lp = &dklabel[unit];
439 	if (vi == 0 || vi->ui_alive == 0) {
440 		bp->b_error = ENXIO;
441 		goto bad;
442 	}
443 	dk = &dksoftc[unit];
444 	if (dk->dk_state < OPEN)
445 		goto q;
446 	part = vdpart(bp->b_dev);
447 	if ((dk->dk_openpart & (1 << part)) == 0) {
448 		bp->b_error = ENODEV;
449 		goto bad;
450 	}
451 	maxsz = lp->d_partitions[part].p_size;
452 	sn = bp->b_blkno << dk->dk_bshift;
453 	if (sn < 0 || sn + sz > maxsz) {
454 		if (sn == maxsz) {
455 			bp->b_resid = bp->b_bcount;
456 			goto done;
457 		}
458 		bp->b_error = EINVAL;
459 		goto bad;
460 	}
461 	bp->b_cylin = (sn + lp->d_partitions[part].p_offset) / lp->d_secpercyl;
462 q:
463 	vbasetup(bp, lp->d_secsize);
464 	s = spl7();
465 	dp = &dkutab[vi->ui_unit];
466 	disksort(dp, bp);
467 	if (!dp->b_active) {
468 		(void) vdustart(vi);
469 		bp = &vi->ui_mi->um_tab;
470 		if (bp->b_actf && !bp->b_active)
471 			vdstart(vi->ui_mi);
472 	}
473 	splx(s);
474 	return;
475 bad:
476 	bp->b_flags |= B_ERROR;
477 done:
478 	biodone(bp);
479 	return;
480 }
481 
482 vdustart(vi)
483 	register struct vba_device *vi;
484 {
485 	register struct buf *bp, *dp;
486 	register struct vba_ctlr *vm;
487 	register int unit = vi->ui_unit;
488 	register struct dksoftc *dk;
489 	register struct vdsoftc *vd;
490 	struct disklabel *lp;
491 
492 	dk_busy &= ~(1<<vi->ui_dk);
493 	dp = &dkutab[unit];
494 	/*
495 	 * If queue empty, nothing to do.
496 	 */
497 	if ((bp = dp->b_actf) == NULL)
498 		return;
499 	/*
500 	 * If drive is off-cylinder, mark unit to force
501 	 * overlap seek with next transfer on this controller.
502 	 */
503 	vd = &vdsoftc[vi->ui_ctlr];
504 	dk = &dksoftc[unit];
505 	if (bp->b_cylin != dk->dk_curcyl && vd->vd_flags&VD_DOSEEKS) {
506 		int sn = bp->b_blkno << dk->dk_bshift;
507 		lp = &dklabel[unit];
508 		bp->b_daddr = (sn % lp->d_secpercyl) / lp->d_nsectors;
509 		if (bp->b_daddr != dk->dk_curdaddr)
510 			vd->vd_offcyl |= 1 << vi->ui_slave;
511 	}
512 	/*
513 	 * If controller is not busy, place request on the
514 	 * controller's ready queue (unless its already there).
515 	 */
516 	if (!dp->b_active) {
517 		dp->b_forw = NULL;
518 		vm = vi->ui_mi;
519 		if (vm->um_tab.b_actf == NULL)
520 			vm->um_tab.b_actf = dp;
521 		else
522 			vm->um_tab.b_actl->b_forw = dp;
523 		vm->um_tab.b_actl = dp;
524 		dp->b_active++;
525 	}
526 }
527 
528 /*
529  * Start next transfer on a controller.
530  */
531 vdstart(vm)
532 	register struct vba_ctlr *vm;
533 {
534 	register struct buf *bp;
535 	register struct vba_device *vi;
536 	register struct vdsoftc *vd;
537 	register struct dksoftc *dk;
538 	register struct disklabel *lp;
539 	register int slave;
540 	register struct dcb **dcbp;
541 	struct mdcb *mdcb;
542 	struct buf *dp;
543 	int sn, tn;
544 
545 loop:
546 	/*
547 	 * Pull a request off the controller queue.
548 	 */
549 	if ((dp = vm->um_tab.b_actf) == NULL)
550 		return;
551 	if ((bp = dp->b_actf) == NULL) {
552 		vm->um_tab.b_actf = dp->b_forw;
553 		goto loop;
554 	}
555 
556 	/*
557 	 * Mark controller busy, and determine
558 	 * destination of this request.
559 	 */
560 	vm->um_tab.b_active++;
561 	vi = vddinfo[vdunit(bp->b_dev)];
562 	dk = &dksoftc[vi->ui_unit];
563 	sn = bp->b_blkno << dk->dk_bshift;
564 	lp = &dklabel[vi->ui_unit];
565 	sn %= lp->d_secpercyl;
566 	tn = sn / lp->d_nsectors;
567 	sn %= lp->d_nsectors;
568 
569 	/*
570 	 * Construct dcb for read/write command.
571 	 */
572 	vd = &vdsoftc[vm->um_ctlr];
573 	slave = vi->ui_slave;
574 	vd->vd_dcb.opcode = (bp->b_flags & B_READ) ? VDOP_RD : VDOP_WD;
575 	vd->vd_dcb.intflg = DCBINT_DONE;
576 	vd->vd_dcb.devselect = slave;
577 	vd->vd_dcb.operrsta = 0;
578 	vd->vd_dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
579 	vd->vd_dcb.trailcnt = sizeof (trrw) / sizeof (long);
580 	vd->vd_dcb.trail.rwtrail.memadr = (char *)
581 	    vbastart(bp, vd->vd_rawbuf, (long *)vd->vd_map, vd->vd_utl);
582 	vd->vd_dcb.trail.rwtrail.wcount = (bp->b_bcount+1) >> 1;
583 	vd->vd_dcb.trail.rwtrail.disk.cylinder = bp->b_cylin;
584 	vd->vd_dcb.trail.rwtrail.disk.track = tn;
585 	vd->vd_dcb.trail.rwtrail.disk.sector = sn;
586 
587 	/*
588 	 * Look for any seeks to be performed on other drives on this
589 	 * controller.  If overlapped seeks exist, insert seek commands
590 	 * on the controller's command queue before the transfer.
591 	 */
592 	dcbp = &vd->vd_mdcb.mdcb_head;
593 	if (vd->vd_offcyl &~ (1<<slave)) {
594 		register struct dksoftc *tdk;
595 		register struct buf *tp;
596 
597 		for (dp = dp->b_forw; dp != NULL; dp = dp->b_forw) {
598 			if ((tp = dp->b_actf) == NULL)
599 				continue;
600 			slave = (vi = vddinfo[vdunit(tp->b_dev)])->ui_slave;
601 			if ((vd->vd_offcyl & (1<<slave)) == 0)
602 				continue;
603 			vd->vd_offcyl &= ~(1 << slave);
604 			tdk = &dksoftc[vi->ui_unit];
605 			if (tdk->dk_curcyl != tp->b_cylin) {
606 				tdk->dk_curcyl = tp->b_cylin;
607 				dk_seek[vi->ui_dk]++;
608 			}
609 			tdk->dk_curdaddr = tp->b_daddr;
610 			tdk->dk_dcb.operrsta = 0;
611 			tdk->dk_dcb.trail.sktrail.skaddr.cylinder = tp->b_cylin;
612 			tdk->dk_dcb.trail.sktrail.skaddr.track = tp->b_daddr>>8;
613 			tdk->dk_dcb.trail.sktrail.skaddr.sector =
614 			    tp->b_daddr & 0xff;
615 			*dcbp = (struct dcb *)tdk->dk_dcbphys;
616 			dcbp = &tdk->dk_dcb.nxtdcb;
617 		}
618 	} else {
619 		dk->dk_curcyl = bp->b_cylin;
620 		dk->dk_curdaddr = (tn << 8) | sn;
621 		vd->vd_offcyl = 0;
622 	}
623 	*dcbp = (struct dcb *)vd->vd_dcbphys;
624 
625 	/*
626 	 * Initiate operation.
627 	 */
628 	bp->b_daddr = 0;		/* init overloaded field */
629 	if (vi->ui_dk >= 0) {
630 		dk_busy |= 1<<vi->ui_dk;
631 		dk_xfer[vi->ui_dk]++;
632 		dk_wds[vi->ui_dk] += bp->b_bcount>>6;
633 	}
634 	vd->vd_mdcb.mdcb_status = 0;
635 	VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type);
636 }
637 
638 #define	DONTCARE (DCBS_DSE|DCBS_DSL|DCBS_TOP|DCBS_TOM|DCBS_FAIL|DCBS_DONE)
639 /*
640  * Handle a disk interrupt.
641  */
642 vdintr(ctlr)
643 	register ctlr;
644 {
645 	register struct buf *bp, *dp;
646 	register struct vba_ctlr *vm = vdminfo[ctlr];
647 	register struct vba_device *vi;
648 	register struct vdsoftc *vd = &vdsoftc[ctlr];
649 	register status;
650 
651 	vd->vd_wticks = 0;
652 	if (!vm->um_tab.b_active) {
653 		printf("vd%d: stray interrupt\n", ctlr);
654 		return;
655 	}
656 	/*
657 	 * Get device and block structures, and a pointer
658 	 * to the vba_device for the drive.
659 	 */
660 	dp = vm->um_tab.b_actf;
661 	bp = dp->b_actf;
662 	vi = vddinfo[vdunit(bp->b_dev)];
663 	dk_busy &= ~(1<<vi->ui_dk);
664 	/*
665 	 * Check for and process errors on
666 	 * either the drive or the controller.
667 	 */
668 	uncache(&vd->vd_dcb.operrsta);
669 	status = vd->vd_dcb.operrsta;
670 	if (status & VDERR_HARD) {
671 		if (status & DCBS_WPT) {
672 			/*
673 			 * Give up on write locked devices immediately.
674 			 */
675 			printf("dk%d: write locked\n", vdunit(bp->b_dev));
676 			bp->b_flags |= B_ERROR;
677 		} else if (status & VDERR_SOFT) {
678 			if (status & VDERR_DRIVE) {
679 				if (!vdreset_drive(vi))
680 					vi->ui_alive = 0;
681 			} else if (status & VDERR_CTLR)
682 				vdreset_ctlr(vm);
683 			/*
684 			 * Retry transfer once, unless reset failed.
685 			 */
686 			if (!vi->ui_alive || bp->b_errcnt++ >= 2)
687 				goto hard;
688 			vm->um_tab.b_active = 0;	/* force retry */
689 		} else  {
690 	hard:
691 			bp->b_flags |= B_ERROR;
692 			/* NEED TO ADJUST b_blkno to failed sector */
693 			harderr(bp, "dk");
694 			printf("status %x (%b)", status,
695 			   status &~ DONTCARE, VDERRBITS);
696 			if (vd->vd_type == VDTYPE_SMDE) {
697 				uncache(&vd->vd_dcb.err_code);
698 				printf(" ecode %x", vd->vd_dcb.err_code);
699 			}
700 			printf("\n");
701 		}
702 	} else if (status & DCBS_SOFT)
703 		vdsofterr(vd, bp, &vd->vd_dcb);
704 	if (vm->um_tab.b_active) {
705 		vm->um_tab.b_active = 0;
706 		vm->um_tab.b_errcnt = 0;
707 		vm->um_tab.b_actf = dp->b_forw;
708 		dp->b_active = 0;
709 		dp->b_errcnt = 0;
710 		dp->b_actf = bp->av_forw;
711 		bp->b_resid = 0;
712 		vbadone(bp, vd->vd_rawbuf, (long *)vd->vd_map, vd->vd_utl);
713 		biodone(bp);
714 		/*
715 		 * If this unit has more work to do,
716 		 * then start it up right away.
717 		 */
718 		if (dp->b_actf)
719 			vdustart(vi);
720 	}
721 	/*
722 	 * If there are devices ready to
723 	 * transfer, start the controller.
724 	 */
725 	if (vm->um_tab.b_actf)
726 		vdstart(vm);
727 }
728 
729 vdsofterr(vd, bp, dcb)
730 	struct vdsoftc *vd;
731 	register struct buf *bp;
732 	register struct dcb *dcb;
733 {
734 	int unit = vdunit(bp->b_dev), status = dcb->operrsta;
735 	char part = 'a' + vdpart(bp->b_dev);
736 
737 	if (status != (DCBS_DCE|DCBS_CCD|DCBS_SOFT|DCBS_ERR)) {
738 		if (vd->vd_type == VDTYPE_SMDE)
739 			uncache(&dcb->err_code);
740 		log(LOG_WARNING, "dk%d%c: soft error sn%d status %b ecode %x\n",
741 		    unit, part, bp->b_blkno, status, VDERRBITS, dcb->err_code);
742 	} else
743 		log(LOG_WARNING, "dk%d%c: soft ecc sn%d\n",
744 		    unit, part, bp->b_blkno);
745 }
746 
747 vdread(dev, uio)
748 	dev_t dev;
749 	struct uio *uio;
750 {
751 	register int unit = vdunit(dev);
752 
753 	if (unit >= NDK)
754 		return (ENXIO);
755 	return (physio(vdstrategy, &rdkbuf[unit], dev, B_READ, minphys, uio));
756 }
757 
758 vdwrite(dev, uio)
759 	dev_t dev;
760 	struct uio *uio;
761 {
762 	register int unit = vdunit(dev);
763 
764 	if (unit >= NDK)
765 		return (ENXIO);
766 	return (physio(vdstrategy, &rdkbuf[unit], dev, B_WRITE, minphys, uio));
767 }
768 
769 vdioctl(dev, cmd, data, flag)
770 	dev_t dev;
771 	int cmd;
772 	caddr_t data;
773 	int flag;
774 {
775 	int unit = vdunit(dev);
776 	register struct disklabel *lp = &dklabel[unit];
777 	int error = 0;
778 
779 	switch (cmd) {
780 
781 	case DIOCGDINFO:
782 		*(struct disklabel *)data = *lp;
783 		break;
784 
785 	case DIOCGDINFOP:
786 		*(struct disklabel **)data = lp;
787 		break;
788 
789 	case DIOCSDINFO:
790 		if ((flag & FWRITE) == 0)
791 			error = EBADF;
792 		else
793 			*lp = *(struct disklabel *)data;
794 		break;
795 
796 	case DIOCWDINFO: {
797 		struct buf *bp;
798 		struct disklabel *dlp;
799 
800 		if ((flag & FWRITE) == 0) {
801 			error = EBADF;
802 			break;
803 		}
804 		*lp = *(struct disklabel *)data;
805 		bp = geteblk(lp->d_secsize);
806 		bp->b_dev = dev;
807 		bp->b_blkno = LABELSECTOR;
808 		bp->b_bcount = lp->d_secsize;
809 		bp->b_flags = B_READ;
810 		dlp = (struct disklabel *)(bp->b_un.b_addr + LABELOFFSET);
811 		vdstrategy(bp);
812 		biowait(bp);
813 		if (bp->b_flags & B_ERROR) {
814 			error = u.u_error;		/* XXX */
815 			u.u_error = 0;
816 			goto bad;
817 		}
818 		*dlp = *lp;
819 		bp->b_flags = B_WRITE;
820 		vdstrategy(bp);
821 		biowait(bp);
822 		if (bp->b_flags & B_ERROR) {
823 			error = u.u_error;		/* XXX */
824 			u.u_error = 0;
825 		}
826 bad:
827 		brelse(bp);
828 		break;
829 	}
830 
831 	default:
832 		error = ENOTTY;
833 		break;
834 	}
835 	return (0);
836 }
837 
838 /*
839  * Watch for lost interrupts.
840  */
841 vdwatch()
842 {
843 	register struct vdsoftc *vd;
844 	register struct vba_ctlr *vm;
845 	register int ctlr, unit;
846 
847 	timeout(vdwatch, (caddr_t)0, hz);
848 	for (ctlr = 0; ctlr < NVD; ctlr++) {
849 		vm = vdminfo[ctlr];
850 		if (vm == 0 || vm->um_alive == 0)
851 			continue;
852 		vd = &vdsoftc[ctlr];
853 		if (!vm->um_tab.b_active) {
854 			for (unit = 0; unit < NDK; unit++)
855 				if (dkutab[unit].b_active &&
856 				    vddinfo[unit]->ui_mi == vm)
857 					goto active;
858 			vd->vd_wticks = 0;
859 			continue;
860 		}
861 active:
862 		vd->vd_wticks++;
863 		if (vd->vd_wticks >= 20) {
864 			vd->vd_wticks = 0;
865 			printf("vd%d: lost interrupt\n", ctlr);
866 			/* abort pending dcb's and restart controller */
867 		}
868 	}
869 }
870 
871 #define	DBSIZE	64	/* controller limit with 1K sectors */
872 /*
873  * Crash dump.
874  */
875 vddump(dev)
876 	dev_t dev;
877 {
878 	register struct vba_device *vi;
879 	register struct vba_ctlr *vm;
880 	register struct disklabel *lp;
881 	register struct vdsoftc *vd;
882 	struct dksoftc *dk;
883 	int part, unit, num;
884 	caddr_t start;
885 
886 	start = 0;
887 	unit = vdunit(dev);
888 	if (unit > NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0)
889 		return (ENXIO);
890 	dk = &dksoftc[unit];
891 	if (dk->dk_state != OPEN && dk->dk_state != OPENRAW)
892 		return (ENXIO);
893 	lp = &dklabel[unit];
894 	part = vdpart(dev);
895 	if (part >= lp->d_npartitions)
896 		return (ENXIO);
897 	vm = vdminfo[vi->ui_ctlr];
898 	vdreset_ctlr(vm);
899 	if (dumplo < 0)
900 		return (EINVAL);
901 	/*
902 	 * Dumplo and maxfree are in pages;
903 	 * dumplo will change soon (XXX).
904 	 */
905 	num = maxfree * (NBPG / lp->d_secsize);
906 	dumplo *= NBPG / lp->d_secsize;		/* XXX */
907 	if (dumplo + num >= lp->d_partitions[vdpart(dev)].p_size)
908 		num = lp->d_partitions[vdpart(dev)].p_size - dumplo;
909 	vd = &vdsoftc[vm->um_ctlr];
910 	vd->vd_dcb.intflg = DCBINT_NONE;
911 	vd->vd_dcb.opcode = VDOP_WD;
912 	vd->vd_dcb.devselect = vi->ui_slave;
913 	vd->vd_dcb.trailcnt = sizeof (trrw) / sizeof (long);
914 	while (num > 0) {
915 		int nsec, cn, sn, tn;
916 
917 		nsec = MIN(num, DBSIZE);
918 		sn = dumplo + (unsigned)start / lp->d_secsize;
919 		cn = (sn + lp->d_partitions[vdpart(dev)].p_offset) /
920 		    lp->d_secpercyl;
921 		sn %= lp->d_secpercyl;
922 		tn = sn / lp->d_nsectors;
923 		sn %= lp->d_nsectors;
924 		vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys;
925 		vd->vd_dcb.trail.rwtrail.memadr = start;
926 		vd->vd_dcb.trail.rwtrail.wcount = (nsec * lp->d_secsize) >> 1;
927 		vd->vd_dcb.trail.rwtrail.disk.cylinder = cn;
928 		vd->vd_dcb.trail.rwtrail.disk.track = tn;
929 		vd->vd_dcb.trail.rwtrail.disk.sector = sn;
930 		vd->vd_dcb.operrsta = 0;
931 		VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type);
932 		if (!vdpoll(vm, 5)) {
933 			printf(" during dump\n");
934 			return (EIO);
935 		}
936 		if (vd->vd_dcb.operrsta & VDERR_HARD) {
937 			printf("dk%d: hard error, status=%b\n", unit,
938 			    vd->vd_dcb.operrsta, VDERRBITS);
939 			return (EIO);
940 		}
941 		start += nsec * lp->d_secsize;
942 		num -= nsec;
943 	}
944 	return (0);
945 }
946 
947 vdsize(dev)
948 	dev_t dev;
949 {
950 	register int unit = vdunit(dev);
951 	register struct dksoftc *dk;
952 	struct vba_device *vi;
953 	struct disklabel *lp;
954 
955 	if (unit >= NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0 ||
956 	    (dk = &dksoftc[unit])->dk_state != OPEN)
957 		return (-1);
958 	lp = &dklabel[unit];
959 	return ((int)lp->d_partitions[vdpart(dev)].p_size >> dk->dk_bshift);
960 }
961 
962 /*
963  * Perform a controller reset.
964  */
965 vdreset_ctlr(vm)
966 	register struct vba_ctlr *vm;
967 {
968 	register struct vddevice *vdaddr = (struct vddevice *)vm->um_addr;
969 	register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr];
970 	register int unit;
971 	struct vba_device *vi;
972 
973 	VDRESET(vdaddr, vd->vd_type);
974 	if (vd->vd_type == VDTYPE_SMDE) {
975 		vdaddr->vdcsr = 0;
976 		vdaddr->vdtcf_mdcb = AM_ENPDA;
977 		vdaddr->vdtcf_dcb = AM_ENPDA;
978 		vdaddr->vdtcf_trail = AM_ENPDA;
979 		vdaddr->vdtcf_data = AM_ENPDA;
980 		vdaddr->vdccf = CCF_STS | XMD_32BIT | BSZ_16WRD |
981 		    CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR;
982 	}
983 	if (!vdcmd(vm, VDOP_INIT, 10) || !vdcmd(vm, VDOP_DIAG, 10)) {
984 		printf("%s cmd failed\n",
985 		    vd->vd_dcb.opcode == VDOP_INIT ? "init" : "diag");
986 		return;
987 	}
988 	for (unit = 0; unit < NDK; unit++)
989 		if ((vi = vddinfo[unit])->ui_mi == vm && vi->ui_alive)
990 			(void) vdreset_drive(vi);
991 }
992 
993 vdreset_drive(vi)
994 	register struct vba_device *vi;
995 {
996 	register struct disklabel *lp = &dklabel[vi->ui_unit];
997 	struct vba_ctlr *vm = vdminfo[vi->ui_ctlr];
998 	struct vddevice *vdaddr = (struct vddevice *)vm->um_addr;
999 	struct vdsoftc *vd = &vdsoftc[vi->ui_ctlr];
1000 
1001 top:
1002 	vd->vd_dcb.opcode = VDOP_CONFIG;		/* command */
1003 	vd->vd_dcb.intflg = DCBINT_NONE;
1004 	vd->vd_dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
1005 	vd->vd_dcb.operrsta = 0;
1006 	vd->vd_dcb.devselect = vi->ui_slave;
1007 	vd->vd_dcb.trail.rstrail.ncyl = lp->d_ncylinders;
1008 	vd->vd_dcb.trail.rstrail.nsurfaces = lp->d_ntracks;
1009 	if (vd->vd_type == VDTYPE_SMDE) {
1010 		vd->vd_dcb.trailcnt = sizeof (treset) / sizeof (long);
1011 		vd->vd_dcb.trail.rstrail.nsectors = lp->d_nsectors;
1012 		vd->vd_dcb.trail.rstrail.slip_sec = lp->d_trackskew;
1013 		vd->vd_dcb.trail.rstrail.recovery = 0x18f;
1014 	} else
1015 		vd->vd_dcb.trailcnt = 2;		/* XXX */
1016 	vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys;
1017 	vd->vd_mdcb.mdcb_status = 0;
1018 	VDGO(vdaddr, vd->vd_mdcbphys, vd->vd_type);
1019 	if (!vdpoll(vm, 5)) {
1020 		printf(" during config\n");
1021 		return (0);
1022 	}
1023 	if (vd->vd_dcb.operrsta & VDERR_HARD) {
1024 		if (vd->vd_type == VDTYPE_SMDE &&
1025 		    (vdaddr->vdstatus[vi->ui_slave]&STA_US) == 0)
1026 			return (0);
1027 		if ((vd->vd_dcb.operrsta & (DCBS_OCYL|DCBS_NRDY)) == 0)
1028 			printf("dk%d: config error\n", vi->ui_unit);
1029 		else if ((vd->vd_flags&VD_STARTED) == 0) {
1030 			int started;
1031 
1032 			printf("vd%d: starting drives, wait ... ", vm->um_ctlr);
1033 			vd->vd_flags |= VD_STARTED;
1034 			started = (vdcmd(vm, VDOP_START, 10) == 1);
1035 			DELAY(62000000);
1036 			printf("\n");
1037 			if (started)
1038 				goto top;
1039 		}
1040 		return (0);
1041 	}
1042 	return (1);
1043 }
1044 
1045 /*
1046  * Perform a command w/o trailer.
1047  */
1048 vdcmd(vm, cmd, t)
1049 	register struct vba_ctlr *vm;
1050 {
1051 	register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr];
1052 
1053 	vd->vd_dcb.opcode = cmd;		/* command */
1054 	vd->vd_dcb.intflg = DCBINT_NONE;
1055 	vd->vd_dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
1056 	vd->vd_dcb.operrsta = 0;
1057 	vd->vd_dcb.devselect = 0;
1058 	vd->vd_dcb.trailcnt = 0;
1059 	vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys;
1060 	vd->vd_mdcb.mdcb_status = 0;
1061 	VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type);
1062 	if (!vdpoll(vm, t)) {
1063 		printf(" during init\n");
1064 		return (0);
1065 	}
1066 	return ((vd->vd_dcb.operrsta&VDERR_HARD) == 0);
1067 }
1068 
1069 /*
1070  * Poll controller until operation
1071  * completes or timeout expires.
1072  */
1073 vdpoll(vm, t)
1074 	register struct vba_ctlr *vm;
1075 	register int t;
1076 {
1077 	register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr];
1078 	register struct vddevice *vdaddr = (struct vddevice *)vm->um_addr;
1079 
1080 	t *= 1000;
1081 	for (;;) {
1082 		uncache(&vd->vd_dcb.operrsta);
1083 		if (vd->vd_dcb.operrsta & (DCBS_DONE|DCBS_ABORT))
1084 			break;
1085 		if (--t <= 0) {
1086 			printf("vd%d: controller timeout", vm->um_ctlr);
1087 			VDABORT(vdaddr, vd->vd_type);
1088 			DELAY(30000);
1089 			return (0);
1090 		}
1091 		DELAY(1000);
1092 	}
1093 	if (vd->vd_type == VDTYPE_SMDE) {
1094 		do {
1095 			DELAY(50);
1096 			uncache(&vdaddr->vdcsr);
1097 		} while (vdaddr->vdcsr & CS_GO);
1098 		DELAY(300);
1099 	}
1100 	DELAY(200);
1101 	uncache(&vd->vd_dcb.operrsta);
1102 	return (1);
1103 }
1104 
1105 #ifdef COMPAT_42
1106 struct	vdst {
1107 	int	nsec;		/* sectors/track */
1108 	int	ntrack;		/* tracks/cylinder */
1109 	int	ncyl;		/* cylinders */
1110 	char	*name;		/* type name */
1111 	struct {
1112 		int	off;	/* partition offset in sectors */
1113 		int	size;	/* partition size in sectors */
1114 	} parts[3];
1115 } vdst[] = {
1116 	{ 48, 24, 711, "xsd", {0,61056}, {61056,61056}, {122112,691200} },
1117 	{ 44, 20, 842, "egl", {0,52800}, {52800,66000}, {118800,617760} },
1118 	{ 64, 10, 823, "fuj", {0,38400}, {38400,48000}, { 86400,437120} },
1119 	{ 32, 24, 711, "xfd", {0,40704}, {40704,40704}, { 81408,460800} },
1120 	{ 32, 19, 823, "smd", {0,40128}, {40128,27360}, { 67488,429856} },
1121 	{ 32, 10, 823, "fsd", {0,19200}, {19200,24000}, { 43200,218560} }
1122 };
1123 #define	NVDST	(sizeof (vdst) / sizeof (vdst[0]))
1124 
1125 /*
1126  * Construct a label for an unlabeled pack.  We
1127  * deduce the drive type by reading from the last
1128  * track on successively smaller drives until we
1129  * don't get an error.
1130  */
1131 vdmaptype(vi, lp)
1132 	register struct vba_device *vi;
1133 	register struct disklabel *lp;
1134 {
1135 	register struct vdsoftc *vd;
1136 	register struct vdst *p;
1137 	struct vba_ctlr *vm = vdminfo[vi->ui_ctlr];
1138 	int i;
1139 
1140 	vd = &vdsoftc[vi->ui_ctlr];
1141 	for (p = vdst; p < &vdst[NVDST]; p++) {
1142 		if (vd->vd_type == VDTYPE_VDDC && p->nsec != 32)
1143 			continue;
1144 		lp->d_nsectors = p->nsec;
1145 		lp->d_ntracks = p->ntrack;
1146 		lp->d_ncylinders = p->ncyl;
1147 		if (!vdreset_drive(vi))
1148 			return (0);
1149 		vd->vd_dcb.opcode = VDOP_RD;
1150 		vd->vd_dcb.intflg = DCBINT_NONE;
1151 		vd->vd_dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
1152 		vd->vd_dcb.devselect = vi->ui_slave;
1153 		vd->vd_dcb.trailcnt = sizeof (trrw) / sizeof (long);
1154 		vd->vd_dcb.trail.rwtrail.memadr = (char *)
1155 		    vtoph((struct proc *)0, (unsigned)vd->vd_rawbuf);
1156 		vd->vd_dcb.trail.rwtrail.wcount = 512 / sizeof(short);
1157 		vd->vd_dcb.operrsta = 0;
1158 		vd->vd_dcb.trail.rwtrail.disk.cylinder = p->ncyl - 2;
1159 		vd->vd_dcb.trail.rwtrail.disk.track = p->ntrack - 1;
1160 		vd->vd_dcb.trail.rwtrail.disk.sector = p->nsec - 1;
1161 		vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys;
1162 		vd->vd_mdcb.mdcb_status = 0;
1163 		VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type);
1164 		if (!vdpoll(vm, 60))
1165 			printf(" during probe\n");
1166 		if ((vd->vd_dcb.operrsta & VDERR_HARD) == 0)
1167 			break;
1168 	}
1169 	if (p >= &vdst[NVDST]) {
1170 		printf("dk%d: unknown drive type\n", vi->ui_unit);
1171 		return (0);
1172 	}
1173 	for (i = 0; i < 3; i++) {
1174 		lp->d_partitions[i].p_offset = p->parts[i].off;
1175 		lp->d_partitions[i].p_size = p->parts[i].size;
1176 	}
1177 	lp->d_npartitions = 3;
1178 	lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1179 	lp->d_rpm = 3600;
1180 	lp->d_secsize = 512;
1181 	bcopy(p->name, lp->d_typename, 4);
1182 	return (1);
1183 }
1184 #endif COMPAT_42
1185 #endif
1186