xref: /csrg-svn/sys/tahoe/vba/vd.c (revision 34396)
1 /*	vd.c	1.25	88/05/21	*/
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 #include "stat.h"
26 
27 #include "../tahoe/cpu.h"
28 #include "../tahoe/mtpr.h"
29 #include "../tahoe/pte.h"
30 
31 #include "../tahoevba/vbavar.h"
32 #include "../tahoevba/vdreg.h"
33 
34 #ifndef	COMPAT_42
35 #define	COMPAT_42
36 #endif
37 #define	B_FORMAT	B_XXX		/* XXX */
38 
39 #define vdunit(dev)	(minor(dev) >> 3)
40 #define vdpart(dev)	(minor(dev) & 0x07)
41 #define	vdminor(unit,part)	(((unit) << 3) | (part))
42 
43 struct	vba_ctlr *vdminfo[NVD];
44 struct  vba_device *vddinfo[NDK];
45 int	vdprobe(), vdslave(), vdattach(), vddgo(), vdstrategy();
46 long	vdaddr[] = { 0xffff2000, 0xffff2100, 0xffff2200, 0xffff2300, 0 };
47 struct	vba_driver vddriver =
48   { vdprobe, vdslave, vdattach, vddgo, vdaddr, "dk", vddinfo, "vd", vdminfo };
49 
50 /*
51  * Per-controller state.
52  */
53 struct vdsoftc {
54 	u_short	vd_flags;
55 #define	VD_INIT		0x1	/* controller initialized */
56 #define	VD_STARTED	0x2	/* start command issued */
57 #define	VD_DOSEEKS	0x4	/* should overlap seeks */
58 #define	VD_SCATGATH	0x8	/* can do scatter-gather commands (correctly) */
59 	u_short	vd_type;	/* controller type */
60 	u_short	vd_wticks;	/* timeout */
61 	struct	mdcb vd_mdcb;	/* master command block */
62 	u_long	vd_mdcbphys;	/* physical address of vd_mdcb */
63 	struct	dcb vd_dcb;	/* i/o command block */
64 	u_long	vd_dcbphys;	/* physical address of vd_dcb */
65 	struct	vb_buf vd_rbuf;	/* vba resources */
66 } vdsoftc[NVD];
67 
68 #define	VDMAXTIME	20	/* max time for operation, sec. */
69 
70 /*
71  * Per-drive state.
72  */
73 struct	dksoftc {
74 	int	dk_state;	/* open fsm */
75 #ifndef SECSIZE
76 	u_short	dk_bshift;	/* shift for * (DEV_BSIZE / sectorsize) XXX */
77 #endif SECSIZE
78 	int	dk_wlabel;	/* label sector is currently writable */
79 	u_long	dk_copenpart;	/* character units open on this drive */
80 	u_long	dk_bopenpart;	/* block units open on this drive */
81 	u_long	dk_openpart;	/* all units open on this drive */
82 	u_int	dk_curcyl;	/* last selected cylinder */
83 	struct	skdcb dk_dcb;	/* seek command block */
84 	u_long	dk_dcbphys;	/* physical address of dk_dcb */
85 	int	df_reg[3];	/* for formatting, in-out parameters */
86 } dksoftc[NDK];
87 
88 /*
89  * Drive states.  Used during steps of open/initialization.
90  * States < OPEN (> 0) are transient, during an open operation.
91  * OPENRAW is used for unlabeled disks, to allow format operations.
92  */
93 #define	CLOSED		0		/* disk is closed */
94 #define	WANTOPEN	1		/* open requested, not started */
95 #define	WANTOPENRAW	2		/* open requested, no label */
96 #define	RDLABEL		3		/* reading pack label */
97 #define	OPEN		4		/* intialized and ready */
98 #define	OPENRAW		5		/* open, no label */
99 
100 struct	buf dkutab[NDK];	/* i/o queue headers */
101 struct	disklabel dklabel[NDK];	/* pack labels */
102 
103 #define b_cylin	b_resid
104 #define	b_track	b_error		/* used for seek commands */
105 #define	b_seekf	b_forw		/* second queue on um_tab */
106 #define	b_seekl	b_back		/* second queue on um_tab */
107 
108 int	vdwstart, vdwatch();
109 
110 /*
111  * See if the controller is really there; if so, initialize it.
112  */
113 vdprobe(reg, vm)
114 	caddr_t reg;
115 	struct vba_ctlr *vm;
116 {
117 	register br, cvec;		/* must be r12, r11 */
118 	register struct vddevice *vdaddr = (struct vddevice *)reg;
119 	struct vdsoftc *vd;
120 	int s;
121 
122 #ifdef lint
123 	br = 0; cvec = br; br = cvec;
124 	vdintr(0);
125 #endif
126 	if (badaddr((caddr_t)reg, 2))
127 		return (0);
128 	vd = &vdsoftc[vm->um_ctlr];
129 	vdaddr->vdreset = 0xffffffff;
130 	DELAY(1000000);
131 	if (vdaddr->vdreset != (unsigned)0xffffffff) {
132 		vd->vd_type = VDTYPE_VDDC;
133 		vd->vd_flags &= ~VD_DOSEEKS;
134 		DELAY(1000000);
135 	} else {
136 		vd->vd_type = VDTYPE_SMDE;
137 		vd->vd_flags |= VD_DOSEEKS;
138 		vdaddr->vdrstclr = 0;
139 		DELAY(3000000);
140 		vdaddr->vdcsr = 0;
141 		vdaddr->vdtcf_mdcb = AM_ENPDA;
142 		vdaddr->vdtcf_dcb = AM_ENPDA;
143 		vdaddr->vdtcf_trail = AM_ENPDA;
144 		vdaddr->vdtcf_data = AM_ENPDA;
145 		vdaddr->vdccf = CCF_SEN | CCF_DER | CCF_STS |
146 		    XMD_32BIT | BSZ_16WRD |
147 		    CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR;
148 	}
149 	vd->vd_mdcbphys = vtoph((struct proc *)0, (unsigned)&vd->vd_mdcb);
150 	vd->vd_dcbphys = vtoph((struct proc *)0, (unsigned)&vd->vd_dcb);
151 	vm->um_addr = reg;		/* XXX */
152 	s = spl7();
153 	if (!vdcmd(vm, VDOP_INIT, 10) || !vdcmd(vm, VDOP_DIAG, 10)) {
154 		printf("vd%d: %s cmd failed\n", vm->um_ctlr,
155 		    vd->vd_dcb.opcode == VDOP_INIT ? "init" : "diag");
156 		splx(s);
157 		return (0);
158 	}
159 	if (vd->vd_type == VDTYPE_SMDE) {
160 		vd->vd_dcb.trail.idtrail.date = 0;
161 		if (vdcmd(vm, VDOP_IDENT, 10)) {
162 			uncache(&vd->vd_dcb.trail.idtrail.date);
163 			if (vd->vd_dcb.trail.idtrail.date != 0)
164 				vd->vd_flags |= VD_SCATGATH;
165 		}
166 	}
167 	splx(s);
168 	/*
169 	 * Allocate page tables and i/o buffer.
170 	 */
171 	if (vbainit(&vd->vd_rbuf, MAXPHYS,
172 	    vd->vd_type == VDTYPE_VDDC ? VB_24BIT : VB_32BIT) == 0) {
173 		printf("vd%d: vbainit failed\n", vm->um_ctlr);
174 		return (0);
175 	}
176 	br = 0x17, cvec = 0xe0 + vm->um_ctlr;	/* XXX */
177 	return (sizeof (struct vddevice));
178 }
179 
180 /*
181  * See if a drive is really there.
182  *
183  * Can't read pack label here as various data structures
184  * aren't setup for doing a read in a straightforward
185  * manner.  Instead just probe for the drive and leave
186  * the pack label stuff to the attach routine.
187  */
188 /* ARGSUSED */
189 vdslave(vi, vdaddr)
190 	register struct vba_device *vi;
191 	struct vddevice *vdaddr;
192 {
193 	register struct disklabel *lp = &dklabel[vi->ui_unit];
194 	register struct dksoftc *dk = &dksoftc[vi->ui_unit];
195 	struct vdsoftc *vd = &vdsoftc[vi->ui_ctlr];
196 
197 	if ((vd->vd_flags&VD_INIT) == 0) {
198 		printf("vd%d: %s controller%s\n", vi->ui_ctlr,
199 		    vd->vd_type == VDTYPE_VDDC ? "VDDC" : "SMDE",
200 		    (vd->vd_flags & VD_SCATGATH) ? " with scatter-gather" : "");
201 		vd->vd_flags |= VD_INIT;
202 	}
203 
204 	/*
205 	 * Initialize label enough to do a reset on
206 	 * the drive.  The remainder of the default
207 	 * label values will be filled in in vdinit
208 	 * at attach time.
209 	 */
210 	if (vd->vd_type == VDTYPE_SMDE)
211 		lp->d_secsize = VD_MAXSECSIZE;
212 	else
213 		lp->d_secsize = VDDC_SECSIZE;
214 	lp->d_nsectors = 66;		/* only used on smd-e */
215 	lp->d_ntracks = 23;
216 	lp->d_ncylinders = 850;
217 	lp->d_secpercyl = 66*23;
218 
219 	/*
220 	 * Initialize invariant portion of
221 	 * dcb used for overlapped seeks.
222 	 */
223 	dk->dk_dcb.opcode = VDOP_SEEK;
224 	dk->dk_dcb.intflg = DCBINT_NONE | DCBINT_PBA;
225 	dk->dk_dcb.devselect = vi->ui_slave;
226 	dk->dk_dcb.trailcnt = sizeof (struct trseek) / sizeof (long);
227 	dk->dk_dcb.trail.sktrail.skaddr.sector = 0;
228 	dk->dk_dcbphys = vtoph((struct proc *)0, (unsigned)&dk->dk_dcb);
229 #ifndef SECSIZE
230 	vd_setsecsize(dk, lp);
231 #endif
232 	return (vdreset_drive(vi));
233 }
234 
235 vdattach(vi)
236 	register struct vba_device *vi;
237 {
238 	register int unit = vi->ui_unit;
239 	register struct disklabel *lp = &dklabel[unit];
240 
241 	/*
242 	 * Try to initialize device and read pack label.
243 	 */
244 	if (vdinit(vdminor(unit, 0), 0) != 0) {
245 		printf(": unknown drive type");
246 		return;
247 	}
248 	if (dksoftc[unit].dk_state == OPEN)
249 		printf(": %s <secsize %d, ntrak %d, ncyl %d, nsec %d>",
250 		    lp->d_typename, lp->d_secsize,
251 		    lp->d_ntracks, lp->d_ncylinders, lp->d_nsectors);
252 	/*
253 	 * (60 / rpm) / (sectors per track * (bytes per sector / 2))
254 	 */
255 	if (vi->ui_dk >= 0)
256 		dk_mspw[vi->ui_dk] = 120.0 /
257 		    (lp->d_rpm * lp->d_nsectors * lp->d_secsize);
258 #ifdef notyet
259 	addswap(makedev(VDMAJOR, vdminor(unit, 0)), lp);
260 #endif
261 }
262 
263 vdopen(dev, flags, fmt)
264 	dev_t dev;
265 	int flags, fmt;
266 {
267 	register unit = vdunit(dev);
268 	register struct disklabel *lp;
269 	register struct dksoftc *dk;
270 	register struct partition *pp;
271 	struct vba_device *vi;
272 	int s, error, part = vdpart(dev), mask = 1 << part;
273 	daddr_t start, end;
274 
275 	if (unit >= NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0)
276 		return (ENXIO);
277 	lp = &dklabel[unit];
278 	dk = &dksoftc[unit];
279 
280 	s = spl7();
281 	while (dk->dk_state != OPEN && dk->dk_state != OPENRAW &&
282 	    dk->dk_state != CLOSED)
283 		sleep((caddr_t)dk, PZERO+1);
284 	splx(s);
285 	if (dk->dk_state != OPEN && dk->dk_state != OPENRAW)
286 		if (error = vdinit(dev, flags))
287 			return (error);
288 
289 	if (vdwstart == 0) {
290 		timeout(vdwatch, (caddr_t)0, hz);
291 		vdwstart++;
292 	}
293 	/*
294 	 * Warn if a partion is opened
295 	 * that overlaps another partition which is open
296 	 * unless one is the "raw" partition (whole disk).
297 	 */
298 #define	RAWPART		8		/* 'x' partition */	/* XXX */
299 	if ((dk->dk_openpart & mask) == 0 && part != RAWPART) {
300 		pp = &lp->d_partitions[part];
301 		start = pp->p_offset;
302 		end = pp->p_offset + pp->p_size;
303 		for (pp = lp->d_partitions;
304 		     pp < &lp->d_partitions[lp->d_npartitions]; pp++) {
305 			if (pp->p_offset + pp->p_size <= start ||
306 			    pp->p_offset >= end)
307 				continue;
308 			if (pp - lp->d_partitions == RAWPART)
309 				continue;
310 			if (dk->dk_openpart & (1 << (pp - lp->d_partitions)))
311 				log(LOG_WARNING,
312 				    "dk%d%c: overlaps open partition (%c)\n",
313 				    unit, part + 'a',
314 				    pp - lp->d_partitions + 'a');
315 		}
316 	}
317 	if (part >= lp->d_npartitions)
318 		return (ENXIO);
319 	dk->dk_openpart |= mask;
320 	switch (fmt) {
321 	case S_IFCHR:
322 		dk->dk_copenpart |= mask;
323 		break;
324 	case S_IFBLK:
325 		dk->dk_bopenpart |= mask;
326 		break;
327 	}
328 	return (0);
329 }
330 
331 vdclose(dev, flags, fmt)
332 	dev_t dev;
333 	int flags, fmt;
334 {
335 	register int unit = vdunit(dev);
336 	register struct dksoftc *dk = &dksoftc[unit];
337 	int part = vdpart(dev), mask = 1 << part;
338 
339 	switch (fmt) {
340 	case S_IFCHR:
341 		dk->dk_copenpart &= ~mask;
342 		break;
343 	case S_IFBLK:
344 		dk->dk_bopenpart &= ~mask;
345 		break;
346 	}
347 	if (((dk->dk_copenpart | dk->dk_bopenpart) & mask) == 0)
348 		dk->dk_openpart &= ~mask;
349 	/*
350 	 * Should wait for i/o to complete on this partition
351 	 * even if others are open, but wait for work on blkflush().
352 	 */
353 	if (dk->dk_openpart == 0) {
354 		int s = spl7();
355 		while (dkutab[unit].b_actf)
356 			sleep((caddr_t)dk, PZERO-1);
357 		splx(s);
358 		dk->dk_state = CLOSED;
359 		dk->dk_wlabel = 0;
360 	}
361 	return (0);
362 }
363 
364 vdinit(dev, flags)
365 	dev_t dev;
366 	int flags;
367 {
368 	register struct disklabel *lp;
369 	register struct dksoftc *dk;
370 	struct vba_device *vi;
371 	int unit = vdunit(dev), error = 0;
372 	char *msg, *readdisklabel();
373 	extern int cold;
374 
375 	dk = &dksoftc[unit];
376 	if (flags & O_NDELAY) {
377 		dk->dk_state = OPENRAW;
378 		return;
379 	}
380 	dk->dk_state = RDLABEL;
381 	lp = &dklabel[unit];
382 	vi = vddinfo[unit];
383 	if (msg = readdisklabel(dev, vdstrategy, lp)) {
384 		if (cold) {
385 			printf(": %s", msg);
386 			dk->dk_state = CLOSED;
387 		} else {
388 			log(LOG_ERR, "dk%d: %s\n", unit, msg);
389 			dk->dk_state = OPENRAW;
390 		}
391 #ifdef COMPAT_42
392 		if (vdmaptype(vi, lp))
393 			dk->dk_state = OPEN;
394 #endif
395 	} else {
396 		/*
397 		 * Now that we have the label, configure
398 		 * the correct drive parameters.
399 		 */
400 		if (vdreset_drive(vi))
401 			dk->dk_state = OPEN;
402 		else {
403 			dk->dk_state = CLOSED;
404 			error = ENXIO;
405 		}
406 	}
407 #ifndef SECSIZE
408 	vd_setsecsize(dk, lp);
409 #endif
410 	wakeup((caddr_t)dk);
411 	return (error);
412 }
413 
414 #ifndef SECSIZE
415 vd_setsecsize(dk, lp)
416 	register struct dksoftc *dk;
417 	register struct disklabel *lp;
418 {
419 	int mul;
420 
421 	/*
422 	 * Calculate scaling shift for mapping
423 	 * DEV_BSIZE blocks to drive sectors.
424 	 */
425 	mul = DEV_BSIZE / lp->d_secsize;
426 	dk->dk_bshift = 0;
427 	while ((mul >>= 1) > 0)
428 		dk->dk_bshift++;
429 }
430 #endif SECSIZE
431 
432 /*ARGSUSED*/
433 vddgo(vm)
434 	struct vba_device *vm;
435 {
436 
437 }
438 
439 vdstrategy(bp)
440 	register struct buf *bp;
441 {
442 	register struct vba_device *vi;
443 	register struct disklabel *lp;
444 	register struct dksoftc *dk;
445 	register int unit;
446 	register daddr_t sn;
447 	struct buf *dp;
448 	daddr_t sz, maxsz;
449 	int part, s;
450 
451 	unit = vdunit(bp->b_dev);
452 	if (unit >= NDK) {
453 		bp->b_error = ENXIO;
454 		goto bad;
455 	}
456 	vi = vddinfo[unit];
457 	lp = &dklabel[unit];
458 	if (vi == 0 || vi->ui_alive == 0) {
459 		bp->b_error = ENXIO;
460 		goto bad;
461 	}
462 	dk = &dksoftc[unit];
463 	if (dk->dk_state < OPEN)
464 		goto q;
465 	if (dk->dk_state != OPEN && (bp->b_flags & B_READ) == 0) {
466 		bp->b_error = EROFS;
467 		goto bad;
468 	}
469 	part = vdpart(bp->b_dev);
470 	if ((dk->dk_openpart & (1 << part)) == 0) {
471 		bp->b_error = ENODEV;
472 		goto bad;
473 	}
474 	sz = (bp->b_bcount + lp->d_secsize - 1) / lp->d_secsize;
475 	maxsz = lp->d_partitions[part].p_size;
476 #ifndef SECSIZE
477 	sn = bp->b_blkno << dk->dk_bshift;
478 #else SECSIZE
479 	sn = bp->b_blkno;
480 #endif SECSIZE
481 	if (sn + lp->d_partitions[part].p_offset <= LABELSECTOR &&
482 #if LABELSECTOR != 0
483 	    sn + lp->d_partitions[part].p_offset + sz > LABELSECTOR &&
484 #endif
485 	    (bp->b_flags & B_READ) == 0 && dk->dk_wlabel == 0) {
486 		bp->b_error = EROFS;
487 		goto bad;
488 	}
489 	if (sn < 0 || sn + sz > maxsz) {
490 		if (sn == maxsz) {
491 			bp->b_resid = bp->b_bcount;
492 			goto done;
493 		}
494 		sz = maxsz - sn;
495 		if (sz <= 0) {
496 			bp->b_error = EINVAL;
497 			goto bad;
498 		}
499 		bp->b_bcount = sz * lp->d_secsize;
500 	}
501 	bp->b_cylin = (sn + lp->d_partitions[part].p_offset) / lp->d_secpercyl;
502 #ifdef SECSIZE
503 if (bp->b_blksize != lp->d_secsize && (bp->b_flags & B_PGIN) == 0)
504 panic("vdstrat blksize");
505 #endif SECSIZE
506 q:
507 	s = spl7();
508 	dp = &dkutab[vi->ui_unit];
509 	disksort(dp, bp);
510 	if (!dp->b_active) {
511 		(void) vdustart(vi);
512 		if (!vi->ui_mi->um_tab.b_active)
513 			vdstart(vi->ui_mi);
514 	}
515 	splx(s);
516 	return;
517 bad:
518 	bp->b_flags |= B_ERROR;
519 done:
520 	biodone(bp);
521 	return;
522 }
523 
524 vdustart(vi)
525 	register struct vba_device *vi;
526 {
527 	register struct buf *bp, *dp;
528 	register struct vba_ctlr *vm;
529 	register int unit = vi->ui_unit;
530 	register struct dksoftc *dk;
531 	register struct vdsoftc *vd;
532 	struct disklabel *lp;
533 
534 	dp = &dkutab[unit];
535 	/*
536 	 * If queue empty, nothing to do.
537 	 */
538 	if ((bp = dp->b_actf) == NULL)
539 		return;
540 	/*
541 	 * If drive is off-cylinder and controller supports seeks,
542 	 * place drive on seek queue for controller.
543 	 * Otherwise, place on transfer queue.
544 	 */
545 	vd = &vdsoftc[vi->ui_ctlr];
546 	dk = &dksoftc[unit];
547 	vm = vi->ui_mi;
548 	if (bp->b_cylin != dk->dk_curcyl && vd->vd_flags&VD_DOSEEKS) {
549 		lp = &dklabel[unit];
550 		bp->b_track = (bp->b_blkno % lp->d_secpercyl) / lp->d_nsectors;
551 		if (vm->um_tab.b_seekf == NULL)
552 			vm->um_tab.b_seekf = dp;
553 		else
554 			vm->um_tab.b_seekl->b_forw = dp;
555 		vm->um_tab.b_seekl = dp;
556 	} else {
557 		if (vm->um_tab.b_actf == NULL)
558 			vm->um_tab.b_actf = dp;
559 		else
560 			vm->um_tab.b_actl->b_forw = dp;
561 		vm->um_tab.b_actl = dp;
562 	}
563 	dp->b_forw = NULL;
564 	dp->b_active++;
565 }
566 
567 /*
568  * Start next transfer on a controller.
569  * There are two queues of drives, the first on-cylinder
570  * and the second off-cylinder from their next transfers.
571  * Perform the first transfer for the first drive on the on-cylinder
572  * queue, if any, otherwise the first transfer for the first drive
573  * on the second queue.  Initiate seeks on remaining drives on the
574  * off-cylinder queue, then move them all to the on-cylinder queue.
575  */
576 vdstart(vm)
577 	register struct vba_ctlr *vm;
578 {
579 	register struct buf *bp;
580 	register struct vba_device *vi;
581 	register struct vdsoftc *vd;
582 	register struct dksoftc *dk;
583 	register struct disklabel *lp;
584 	register struct dcb **dcbp;
585 	struct mdcb *mdcb;
586 	struct buf *dp;
587 	int sn, tn;
588 
589 loop:
590 	/*
591 	 * Pull a request off the controller queue.
592 	 */
593 	if ((dp = vm->um_tab.b_actf) == NULL &&
594 	    (dp = vm->um_tab.b_seekf) == NULL)
595 		return;
596 	if ((bp = dp->b_actf) == NULL) {
597 		if (dp == vm->um_tab.b_actf)
598 			vm->um_tab.b_actf = dp->b_forw;
599 		else
600 			vm->um_tab.b_seekf = dp->b_forw;
601 		goto loop;
602 	}
603 
604 	/*
605 	 * Mark controller busy, and determine
606 	 * destination of this request.
607 	 */
608 	vm->um_tab.b_active++;
609 	vi = vddinfo[vdunit(bp->b_dev)];
610 	dk = &dksoftc[vi->ui_unit];
611 #ifndef SECSIZE
612 	sn = bp->b_blkno << dk->dk_bshift;
613 #else SECSIZE
614 	sn = bp->b_blkno;
615 #endif SECSIZE
616 	lp = &dklabel[vi->ui_unit];
617 	sn %= lp->d_secpercyl;
618 	tn = sn / lp->d_nsectors;
619 	sn %= lp->d_nsectors;
620 
621 	/*
622 	 * Construct dcb for read/write command.
623 	 */
624 	vd = &vdsoftc[vm->um_ctlr];
625 	vd->vd_dcb.intflg = DCBINT_DONE;
626 	vd->vd_dcb.devselect = dk->dk_dcb.devselect;
627 	vd->vd_dcb.operrsta = 0;
628 	vd->vd_dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
629 	vd->vd_dcb.trail.rwtrail.disk.cylinder = bp->b_cylin;
630 	vd->vd_dcb.trail.rwtrail.disk.track = tn;
631 	vd->vd_dcb.trail.rwtrail.disk.sector = sn;
632 	dk->dk_curcyl = bp->b_cylin;
633 	bp->b_track = 0;		/* init overloaded field */
634 	vd->vd_dcb.trailcnt = sizeof (struct trrw) / sizeof (long);
635 	if (bp->b_flags & B_FORMAT)
636 		vd->vd_dcb.opcode = dk->dk_op;
637 	else if (vd->vd_flags & VD_SCATGATH &&
638 	    ((int)bp->b_un.b_addr & (sizeof(long) - 1)) == 0)
639 		vd->vd_dcb.opcode = (bp->b_flags & B_READ)? VDOP_RAS : VDOP_GAW;
640 	else
641 		vd->vd_dcb.opcode = (bp->b_flags & B_READ)? VDOP_RD : VDOP_WD;
642 
643 	switch (vd->vd_dcb.opcode) {
644 	case VDOP_FSECT:
645 		vd->vd_dcb.trailcnt = sizeof (struct trrw) / sizeof (long);
646 		vd->vd_dcb.trail.fmtrail.nsectors = bp->b_bcount /
647 		    lp->d_secsize;
648 		vd->vd_dcb.trail.fmtrail.hdr = *(dskadr *)&dk->dk_althdr;
649 		vd->vd_dcb.trail.rwtrail.disk.cylinder |= dk->dk_fmtflags;
650 		goto setupaddr;
651 
652 	case VDOP_RDRAW:
653 	case VDOP_RD:
654 	case VDOP_WD:
655 		vd->vd_dcb.trail.rwtrail.wcount = (bp->b_bcount+1) >> 1;
656 setupaddr:
657 		vd->vd_dcb.trail.rwtrail.memadr =
658 			vbasetup(bp, &vd->vd_rbuf, lp->d_secsize);
659 		break;
660 
661 	case VDOP_RAS:
662 	case VDOP_GAW:
663 		vd->vd_dcb.trailcnt += vba_sgsetup(bp, &vd->vd_rbuf,
664 		    &vd->vd_dcb.trail.sgtrail);
665 		break;
666 	}
667 	if (vi->ui_dk >= 0) {
668 		dk_busy |= 1<<vi->ui_dk;
669 		dk_xfer[vi->ui_dk]++;
670 		dk_wds[vi->ui_dk] += bp->b_bcount>>6;
671 	}
672 
673 	/*
674 	 * Look for any seeks to be performed on other drives on this
675 	 * controller.  If overlapped seeks exist, insert seek commands
676 	 * on the controller's command queue before the transfer.
677 	 */
678 	dcbp = &vd->vd_mdcb.mdcb_head;
679 
680 	if (dp == vm->um_tab.b_seekf)
681 		dp = dp->b_forw;
682 	else
683 		dp = vm->um_tab.b_seekf;
684 	for (; dp != NULL; dp = dp->b_forw) {
685 		if ((bp = dp->b_actf) == NULL)
686 			continue;
687 		vi = vddinfo[vdunit(bp->b_dev)];
688 		dk = &dksoftc[vi->ui_unit];
689 		dk->dk_curcyl = bp->b_cylin;
690 		if (vi->ui_dk >= 0)
691 			dk_seek[vi->ui_dk]++;
692 		dk->dk_dcb.operrsta = 0;
693 		dk->dk_dcb.trail.sktrail.skaddr.cylinder = bp->b_cylin;
694 		dk->dk_dcb.trail.sktrail.skaddr.track = bp->b_track;
695 		*dcbp = (struct dcb *)dk->dk_dcbphys;
696 		dcbp = &dk->dk_dcb.nxtdcb;
697 	}
698 	*dcbp = (struct dcb *)vd->vd_dcbphys;
699 	if (vm->um_tab.b_actf)
700 		vm->um_tab.b_actl->b_forw = vm->um_tab.b_seekf;
701 	else
702 		vm->um_tab.b_actf = vm->um_tab.b_seekf;
703 	if (vm->um_tab.b_seekf)
704 		vm->um_tab.b_actl = vm->um_tab.b_seekl;
705 	vm->um_tab.b_seekf = 0;
706 
707 	/*
708 	 * Initiate operation.
709 	 */
710 	vd->vd_mdcb.mdcb_status = 0;
711 	VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type);
712 }
713 
714 #define	DONTCARE (DCBS_DSE|DCBS_DSL|DCBS_TOP|DCBS_TOM|DCBS_FAIL|DCBS_DONE)
715 /*
716  * Handle a disk interrupt.
717  */
718 vdintr(ctlr)
719 	register ctlr;
720 {
721 	register struct buf *bp, *dp;
722 	register struct vba_ctlr *vm = vdminfo[ctlr];
723 	register struct vba_device *vi;
724 	register struct vdsoftc *vd = &vdsoftc[ctlr];
725 	register status;
726 	int ecode, timedout;
727 	struct dksoftc *dk;
728 
729 	if (!vm->um_tab.b_active) {
730 		printf("vd%d: stray interrupt\n", ctlr);
731 		return;
732 	}
733 	/*
734 	 * Get device and block structures, and a pointer
735 	 * to the vba_device for the drive.
736 	 */
737 	dp = vm->um_tab.b_actf;
738 	bp = dp->b_actf;
739 	vi = vddinfo[vdunit(bp->b_dev)];
740 	if (vi->ui_dk >= 0)
741 		dk_busy &= ~(1<<vi->ui_dk);
742 	timedout = (vd->vd_wticks >= VDMAXTIME);
743 	/*
744 	 * Check for and process errors on
745 	 * either the drive or the controller.
746 	 */
747 	uncache(&vd->vd_dcb.operrsta);
748 	status = vd->vd_dcb.operrsta;
749 	if (bp->b_flags & B_FORMAT) {
750 		dk->dk_operrsta = status;
751 		uncache(&vd->vd_dcb.err_code);
752 		dk->dk_ecode = vd->vd_dcb.err_code;
753 	}
754 	if (status & VDERR_HARD || timedout) {
755 		if (vd->vd_type == VDTYPE_SMDE) {
756 			uncache(&vd->vd_dcb.err_code);
757 			ecode = vd->vd_dcb.err_code;
758 		}
759 		if (status & DCBS_WPT) {
760 			/*
761 			 * Give up on write locked devices immediately.
762 			 */
763 			printf("dk%d: write locked\n", vi->ui_unit);
764 			bp->b_flags |= B_ERROR;
765 		} else if (status & VDERR_RETRY || timedout) {
766 			int endline = 1;
767 
768 			if (status & VDERR_CTLR || timedout) {
769 				vdharderr("controller err",
770 				    vd, bp, &vd->vd_dcb);
771 				printf("; resetting controller...");
772 				vdreset_ctlr(vm);
773 			} else if (status & VDERR_DRIVE) {
774 				vdharderr("drive err", vd, bp, &vd->vd_dcb);
775 				printf("; resetting drive...");
776 				if (!vdreset_drive(vi))
777 					vi->ui_alive = 0;
778 			} else
779 				endline = 0;
780 			/*
781 			 * Retry transfer once, unless reset failed.
782 			 */
783 			if (!vi->ui_alive || dp->b_errcnt++ >= 2 ||
784 			    bp->b_flags & B_FORMAT) {
785 				if (endline)
786 					printf("\n");
787 				goto hard;
788 			}
789 
790 			if (endline)
791 				printf(" retrying\n");
792 			vm->um_tab.b_active = 0;	/* force retry */
793 		} else  {
794 	hard:
795 			bp->b_flags |= B_ERROR;
796 			vdharderr("hard error", vd, bp, &vd->vd_dcb);
797 			printf("\n");
798 		}
799 	} else if (status & DCBS_SOFT)
800 		vdsofterr(vd, bp, &vd->vd_dcb);
801 	vd->vd_wticks = 0;
802 	if (vm->um_tab.b_active) {
803 		vm->um_tab.b_active = 0;
804 		vm->um_tab.b_actf = dp->b_forw;
805 		dp->b_active = 0;
806 		dp->b_errcnt = 0;
807 		dp->b_actf = bp->av_forw;
808 		bp->b_resid = 0;
809 		vbadone(bp, &vd->vd_rbuf);
810 		biodone(bp);
811 		/*
812 		 * If this unit has more work to do,
813 		 * then start it up right away.
814 		 */
815 		if (dp->b_actf)
816 			vdustart(vi);
817 		else if ((dk = &dksoftc[vi->ui_unit])->dk_openpart == 0)
818 			wakeup((caddr_t)dk);
819 	}
820 	/*
821 	 * If there are devices ready to
822 	 * transfer, start the controller.
823 	 */
824 	if (vm->um_tab.b_actf || vm->um_tab.b_seekf)
825 		vdstart(vm);
826 }
827 
828 vdharderr(what, vd, bp, dcb)
829 	char *what;
830 	struct vdsoftc *vd;
831 	register struct buf *bp;
832 	register struct dcb *dcb;
833 {
834 	int unit = vdunit(bp->b_dev), status = dcb->operrsta;
835 	int part = vdpart(bp->b_dev);
836 	register struct disklabel *lp = &dklabel[unit];
837 	char partname = 'a' + part;
838 	int sn;
839 
840 	if (vd->vd_wticks < VDMAXTIME)
841 		status &= ~DONTCARE;
842 /* generic
843 	sn = bp->b_blkno + lp->d_partitions[part].p_offset;
844 */
845 	sn = ((dcb->err_cyl & 0xfff) * lp->d_ntracks + dcb->err_trk)
846 	    * lp->d_nsectors + dcb->err_sec;
847 	printf("dk%d%c: %s bn [%d-%d) (sn %d), status %b",
848 	    unit, partname, what, bp->b_blkno,
849 	    bp->b_blkno + (bp->b_bcount - 1)/DEV_BSIZE, sn, status, VDERRBITS);
850 	if (vd->vd_type == VDTYPE_SMDE)
851 		printf(" ecode %x", dcb->err_code);
852 	printf("\n(error sec %d trk %d cyl %d wcount %d)", dcb->err_sec,
853 	    dcb->err_trk, dcb->err_cyl, dcb->err_wcount);
854 }
855 
856 vdsofterr(vd, bp, dcb)
857 	struct vdsoftc *vd;
858 	register struct buf *bp;
859 	register struct dcb *dcb;
860 {
861 	int unit = vdunit(bp->b_dev), status = dcb->operrsta;
862 	int part = vdpart(bp->b_dev);
863 	char partname = 'a' + part;
864 	int sn = (bp->b_blkno << dksoftc[unit].dk_bshift) +
865 	    dklabel[unit].d_partitions[part].p_offset;
866 
867 	if (status != (DCBS_CCD|DCBS_SOFT|DCBS_ERR|DCBS_DONE))
868 		log(LOG_WARNING,
869 		    "dk%d%c: soft error sn %d bn %d, status %b ecode %x\n",
870 		    unit, partname, sn, bp->b_blkno, status, VDERRBITS,
871 		    dcb->err_code);
872 	else
873 		log(LOG_WARNING, "dk%d%c: soft ecc sn %d bn %d\n",
874 		    unit, part, sn, bp->b_blkno);
875 }
876 
877 vdioctl(dev, cmd, data, flag)
878 	dev_t dev;
879 	int cmd;
880 	caddr_t data;
881 	int flag;
882 {
883 	register int unit = vdunit(dev);
884 	register struct disklabel *lp = &dklabel[unit];
885 	register struct dksoftc *dk = &dksoftc[unit];
886 	int error = 0, wlab, vdformat();
887 
888 	switch (cmd) {
889 
890 	case DIOCGDINFO:
891 		*(struct disklabel *)data = *lp;
892 		break;
893 
894 	case DIOCGPART:
895 		((struct partinfo *)data)->disklab = lp;
896 		((struct partinfo *)data)->part =
897 		    &lp->d_partitions[vdpart(dev)];
898 		break;
899 
900 	case DIOCSDINFO:
901 		if ((flag & FWRITE) == 0)
902 			error = EBADF;
903 		else
904 			error = setdisklabel(lp, (struct disklabel *)data,
905 			    (dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart);
906 		if (error == 0 && dk->dk_state == OPENRAW &&
907 		    vdreset_drive(vddinfo[unit]))
908 			dk->dk_state = OPEN;
909 		break;
910 
911 	case DIOCWLABEL:
912 		if ((flag & FWRITE) == 0)
913 			error = EBADF;
914 		else
915 			dk->dk_wlabel = *(int *)data;
916 		break;
917 
918 	case DIOCWDINFO:
919 		/* simulate opening partition 0 so write succeeds */
920 		dk->dk_openpart |= (1 << 0);		/* XXX */
921 		wlab = dk->dk_wlabel;
922 		dk->dk_wlabel = 1;
923 		if ((flag & FWRITE) == 0)
924 			error = EBADF;
925 		else if ((error = setdisklabel(lp, (struct disklabel *)data,
926 		    (dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart)) == 0) {
927 			dk->dk_state = OPEN;
928 			error = writedisklabel(dev, vdstrategy, lp);
929 		}
930 		dk->dk_openpart = dk->dk_copenpart | dk->dk_bopenpart;
931 		dk->dk_wlabel = wlab;
932 		break;
933 
934 	case DIOCWFORMAT:
935 	    {
936 		register struct format_op *fop;
937 		struct uio auio;
938 		struct iovec aiov;
939 
940 		if ((flag & FWRITE) == 0) {
941 			error = EBADF;
942 			break;
943 		}
944 		fop = (struct format_op *)data;
945 		aiov.iov_base = fop->df_buf;
946 		aiov.iov_len = fop->df_count;
947 		auio.uio_iov = &aiov;
948 		auio.uio_iovcnt = 1;
949 		auio.uio_resid = fop->df_count;
950 		auio.uio_segflg = UIO_USERSPACE;
951 		auio.uio_offset = fop->df_startblk * lp->d_secsize;
952 		dk->dk_operrsta = fop->dk_operrsta;
953 		dk->dk_ecode = fop->dk_ecode;
954 		/*
955 		 * Don't return errors, as the format op won't get copied
956 		 * out if we return nonzero.  Callers must check the returned
957 		 * count.
958 		 */
959 		(void) physio(vdformat, (struct buf *)NULL, dev,
960 		    (cmd == DIOCWFORMAT ? B_WRITE : B_READ), minphys, &auio);
961 		fop->df_count -= auio.uio_resid;
962 		fop->dk_operrsta = dk->dk_operrsta;
963 		fop->dk_ecode = dk->dk_ecode;
964 		break;
965 	    }
966 
967 	default:
968 		error = ENOTTY;
969 		break;
970 	}
971 	return (error);
972 }
973 
974 vdformat(bp)
975 	struct buf *bp;
976 {
977 	bp->b_flags |= B_FORMAT;
978 	vdstrategy(bp);
979 }
980 
981 /*
982  * Watch for lost interrupts.
983  */
984 vdwatch()
985 {
986 	register struct vdsoftc *vd;
987 	register struct vba_ctlr *vm;
988 	register int ctlr, unit;
989 	int s;
990 
991 	timeout(vdwatch, (caddr_t)0, hz);
992 	for (ctlr = 0; ctlr < NVD; ctlr++) {
993 		vm = vdminfo[ctlr];
994 		if (vm == 0 || vm->um_alive == 0)
995 			continue;
996 		vd = &vdsoftc[ctlr];
997 		s = spl7();
998 		if (vm->um_tab.b_active && vd->vd_wticks++ >= VDMAXTIME) {
999 			printf("vd%d: lost interrupt\n", ctlr);
1000 #ifdef maybe
1001 			VDABORT((struct vddevice *)vm->um_addr, vd->vd_type);
1002 #endif
1003 			vdintr(ctlr);
1004 		}
1005 		splx(s);
1006 	}
1007 }
1008 
1009 #define	DBSIZE	64	/* controller limit with 1K sectors */
1010 /*
1011  * Crash dump.
1012  */
1013 vddump(dev)
1014 	dev_t dev;
1015 {
1016 	register struct vba_device *vi;
1017 	register struct vba_ctlr *vm;
1018 	register struct disklabel *lp;
1019 	register struct vdsoftc *vd;
1020 	struct dksoftc *dk;
1021 	int part, unit, num;
1022 	u_long start;
1023 
1024 	start = 0;
1025 	unit = vdunit(dev);
1026 	if (unit > NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0)
1027 		return (ENXIO);
1028 	dk = &dksoftc[unit];
1029 	if (dk->dk_state != OPEN && dk->dk_state != OPENRAW &&
1030 	    vdinit(vdminor(unit, 0), 0) != 0)
1031 		return (ENXIO);
1032 	lp = &dklabel[unit];
1033 	part = vdpart(dev);
1034 	if (part >= lp->d_npartitions)
1035 		return (ENXIO);
1036 	vm = vi->ui_mi;
1037 	vdreset_ctlr(vm);
1038 	if (dumplo < 0)
1039 		return (EINVAL);
1040 	/*
1041 	 * Maxfree is in pages, dumplo is in DEV_BSIZE units.
1042 	 */
1043 	num = maxfree * (NBPG / lp->d_secsize);
1044 	dumplo *= DEV_BSIZE / lp->d_secsize;
1045 	if (dumplo + num >= lp->d_partitions[vdpart(dev)].p_size)
1046 		num = lp->d_partitions[vdpart(dev)].p_size - dumplo;
1047 	vd = &vdsoftc[vm->um_ctlr];
1048 	vd->vd_dcb.intflg = DCBINT_NONE;
1049 	vd->vd_dcb.opcode = VDOP_WD;
1050 	vd->vd_dcb.devselect = dk->dk_dcb.devselect;
1051 	vd->vd_dcb.trailcnt = sizeof (struct trrw) / sizeof (long);
1052 	while (num > 0) {
1053 		int nsec, cn, sn, tn;
1054 
1055 		nsec = MIN(num, DBSIZE);
1056 		sn = dumplo + start / lp->d_secsize;
1057 		cn = (sn + lp->d_partitions[vdpart(dev)].p_offset) /
1058 		    lp->d_secpercyl;
1059 		sn %= lp->d_secpercyl;
1060 		tn = sn / lp->d_nsectors;
1061 		sn %= lp->d_nsectors;
1062 		vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys;
1063 		vd->vd_dcb.trail.rwtrail.memadr = start;
1064 		vd->vd_dcb.trail.rwtrail.wcount = (nsec * lp->d_secsize) >> 1;
1065 		vd->vd_dcb.trail.rwtrail.disk.cylinder = cn;
1066 		vd->vd_dcb.trail.rwtrail.disk.track = tn;
1067 		vd->vd_dcb.trail.rwtrail.disk.sector = sn;
1068 		vd->vd_dcb.operrsta = 0;
1069 		VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type);
1070 		if (!vdpoll(vm, 5)) {
1071 			printf(" during dump\n");
1072 			return (EIO);
1073 		}
1074 		if (vd->vd_dcb.operrsta & VDERR_HARD) {
1075 			printf("dk%d: hard error, status=%b\n", unit,
1076 			    vd->vd_dcb.operrsta, VDERRBITS);
1077 			return (EIO);
1078 		}
1079 		start += nsec * lp->d_secsize;
1080 		num -= nsec;
1081 	}
1082 	return (0);
1083 }
1084 
1085 vdsize(dev)
1086 	dev_t dev;
1087 {
1088 	register int unit = vdunit(dev);
1089 	register struct dksoftc *dk;
1090 	struct vba_device *vi;
1091 	struct disklabel *lp;
1092 
1093 	if (unit >= NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0 ||
1094 	    (dk = &dksoftc[unit])->dk_state != OPEN)
1095 		return (-1);
1096 	lp = &dklabel[unit];
1097 #ifdef SECSIZE
1098 	return ((int)lp->d_partitions[vdpart(dev)].p_size);
1099 #else SECSIZE
1100 	return ((int)lp->d_partitions[vdpart(dev)].p_size >> dk->dk_bshift);
1101 #endif SECSIZE
1102 }
1103 
1104 /*
1105  * Perform a controller reset.
1106  */
1107 vdreset_ctlr(vm)
1108 	register struct vba_ctlr *vm;
1109 {
1110 	register struct vddevice *vdaddr = (struct vddevice *)vm->um_addr;
1111 	register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr];
1112 	register int unit;
1113 	struct vba_device *vi;
1114 
1115 	VDRESET(vdaddr, vd->vd_type);
1116 	if (vd->vd_type == VDTYPE_SMDE) {
1117 		vdaddr->vdcsr = 0;
1118 		vdaddr->vdtcf_mdcb = AM_ENPDA;
1119 		vdaddr->vdtcf_dcb = AM_ENPDA;
1120 		vdaddr->vdtcf_trail = AM_ENPDA;
1121 		vdaddr->vdtcf_data = AM_ENPDA;
1122 		vdaddr->vdccf = CCF_STS | XMD_32BIT | BSZ_16WRD |
1123 		    CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR;
1124 	}
1125 	if (!vdcmd(vm, VDOP_INIT, 10) || !vdcmd(vm, VDOP_DIAG, 10)) {
1126 		printf("%s cmd failed\n",
1127 		    vd->vd_dcb.opcode == VDOP_INIT ? "init" : "diag");
1128 		return;
1129 	}
1130 	for (unit = 0; unit < NDK; unit++)
1131 		if ((vi = vddinfo[unit])->ui_mi == vm && vi->ui_alive)
1132 			(void) vdreset_drive(vi);
1133 }
1134 
1135 vdreset_drive(vi)
1136 	register struct vba_device *vi;
1137 {
1138 	register struct disklabel *lp = &dklabel[vi->ui_unit];
1139 	struct vba_ctlr *vm = vdminfo[vi->ui_ctlr];
1140 	struct vddevice *vdaddr = (struct vddevice *)vm->um_addr;
1141 	register struct vdsoftc *vd = &vdsoftc[vi->ui_ctlr];
1142 	register struct dksoftc *dk = &dksoftc[vi->ui_unit];
1143 
1144 top:
1145 	vd->vd_dcb.opcode = VDOP_CONFIG;		/* command */
1146 	vd->vd_dcb.intflg = DCBINT_NONE;
1147 	vd->vd_dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
1148 	vd->vd_dcb.operrsta = 0;
1149 	vd->vd_dcb.devselect = vi->ui_slave | lp->d_devflags;
1150 	vd->vd_dcb.trail.rstrail.ncyl = lp->d_ncylinders;
1151 	vd->vd_dcb.trail.rstrail.nsurfaces = lp->d_ntracks;
1152 	if (vd->vd_type == VDTYPE_SMDE) {
1153 		vd->vd_dcb.trailcnt = sizeof (struct treset) / sizeof (long);
1154 		vd->vd_dcb.trail.rstrail.nsectors = lp->d_nsectors;
1155 		vd->vd_dcb.trail.rstrail.slip_sec = lp->d_sparespertrack;
1156 		vd->vd_dcb.trail.rstrail.recovery = VDRF_NORMAL;
1157 	} else
1158 		vd->vd_dcb.trailcnt = 2;		/* XXX */
1159 	vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys;
1160 	vd->vd_mdcb.mdcb_status = 0;
1161 	VDGO(vdaddr, vd->vd_mdcbphys, vd->vd_type);
1162 	if (!vdpoll(vm, 5)) {
1163 		printf(" during config\n");
1164 		return (0);
1165 	}
1166 	if (vd->vd_dcb.operrsta & VDERR_HARD) {
1167 		if (vd->vd_type == VDTYPE_SMDE) {
1168 			if (lp->d_devflags == 0) {
1169 				lp->d_devflags = VD_ESDI;
1170 				goto top;
1171 			}
1172 #ifdef notdef
1173 			/* this doesn't work, STA_US isn't set(?) */
1174 			if ((vdaddr->vdstatus[vi->ui_slave] & STA_US) == 0)
1175 				return (0);
1176 #endif
1177 		}
1178 		if ((vd->vd_dcb.operrsta & (DCBS_OCYL|DCBS_NRDY)) == 0)
1179 			printf("dk%d: config error %b ecode %x\n", vi->ui_unit,
1180 			   vd->vd_dcb.operrsta, VDERRBITS,
1181 			   (u_char) vd->vd_dcb.err_code);
1182 		else if ((vd->vd_flags & VD_STARTED) == 0) {
1183 			int started;
1184 
1185 			printf(" starting drives, wait ... ");
1186 			vd->vd_flags |= VD_STARTED;
1187 			started = (vdcmd(vm, VDOP_START, 10) == 1);
1188 			DELAY(62000000);
1189 			printf("done");
1190 			lp->d_devflags = 0;
1191 			if (started)
1192 				goto top;
1193 		}
1194 		return (0);
1195 	}
1196 	dk->dk_dcb.devselect |= lp->d_devflags;
1197 	return (1);
1198 }
1199 
1200 /*
1201  * Perform a command w/o trailer.
1202  */
1203 vdcmd(vm, cmd, t)
1204 	register struct vba_ctlr *vm;
1205 {
1206 	register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr];
1207 
1208 	vd->vd_dcb.opcode = cmd;		/* command */
1209 	vd->vd_dcb.intflg = DCBINT_NONE;
1210 	vd->vd_dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
1211 	vd->vd_dcb.operrsta = 0;
1212 	vd->vd_dcb.devselect = 0;
1213 	vd->vd_dcb.trailcnt = 0;
1214 	vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys;
1215 	vd->vd_mdcb.mdcb_status = 0;
1216 	VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type);
1217 	if (!vdpoll(vm, t)) {
1218 		printf(" during init\n");
1219 		return (0);
1220 	}
1221 	return ((vd->vd_dcb.operrsta&VDERR_HARD) == 0);
1222 }
1223 
1224 /*
1225  * Poll controller until operation
1226  * completes or timeout expires.
1227  */
1228 vdpoll(vm, t)
1229 	register struct vba_ctlr *vm;
1230 	register int t;
1231 {
1232 	register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr];
1233 	register struct vddevice *vdaddr = (struct vddevice *)vm->um_addr;
1234 
1235 	t *= 1000;
1236 	for (;;) {
1237 		uncache(&vd->vd_dcb.operrsta);
1238 		if (vd->vd_dcb.operrsta & (DCBS_DONE|DCBS_ABORT))
1239 			break;
1240 		if (--t <= 0) {
1241 			printf("vd%d: controller timeout", vm->um_ctlr);
1242 			VDABORT(vdaddr, vd->vd_type);
1243 			return (0);
1244 		}
1245 		DELAY(1000);
1246 	}
1247 	if (vd->vd_type == VDTYPE_SMDE) {
1248 		do {
1249 			DELAY(50);
1250 			uncache(&vdaddr->vdcsr);
1251 		} while (vdaddr->vdcsr & CS_GO);
1252 	 	DELAY(300);
1253 		uncache(&vd->vd_dcb.err_code);
1254 	}
1255 	DELAY(200);
1256 	uncache(&vd->vd_dcb.operrsta);
1257 	return (1);
1258 }
1259 
1260 #ifdef COMPAT_42
1261 struct	vdst {
1262 	int	nsec;		/* sectors/track */
1263 	int	ntrack;		/* tracks/cylinder */
1264 	int	ncyl;		/* cylinders */
1265 	int	secsize;	/* sector size */
1266 	char	*name;		/* type name */
1267 	struct {
1268 		int	off;	/* partition offset in sectors */
1269 		int	size;	/* partition size in sectors */
1270 	} parts[8];
1271 } vdst[] = {
1272 	{ 66, 23, 850, 512, "NEC 800",
1273 		{0,	 1290300},	/* a cyl   0 - 849 */
1274 	},
1275 	{ 48, 24, 711, 512, "xsd",
1276 		{0,	 61056},	/* a cyl   0 - 52 */
1277 		{61056,	 61056},	/* b cyl  53 - 105 */
1278 		{122112, 691200}, 	/* c cyl 106 - 705 */
1279 		{237312, 576000}, 	/* d cyl 206 - 705 */
1280 		{352512, 460800},	/* e cyl 306 - 705 */
1281 		{467712, 345600}, 	/* f cyl 406 - 705 */
1282 		{582912, 230400},	/* g cyl 506 - 705 */
1283 		{698112, 115200}	/* h cyl 606 - 705 */
1284 	},
1285 	{ 44, 20, 842, 512, "eagle",
1286 		{0,	 52800},	/* egl0a cyl   0 - 59 */
1287 		{52800,	 66000},	/* egl0b cyl  60 - 134 */
1288 		{118800, 617760}, 	/* egl0c cyl 135 - 836 */
1289 		{736560, 4400}, 	/* egl0d cyl 837 - 841 */
1290 		{0, 	 736560},	/* egl0e cyl   0 - 836 */
1291 		{0, 	 740960}, 	/* egl0f cyl   0 - 841 */
1292 		{118800, 310640},	/* egl0g cyl 135 - 487 */
1293 		{429440, 307120}	/* egl0h cyl 488 - 836 */
1294 	},
1295 	{ 64, 10, 823, 512, "fuj",
1296 		{0,	 38400},	/* fuj0a cyl   0 - 59 */
1297 		{38400,	 48000},	/* fuj0b cyl  60 - 134 */
1298 		{86400,	 437120}, 	/* fuj0c cyl 135 - 817 */
1299 		{159360, 364160}, 	/* fuj0d cyl 249 - 817 */
1300 		{232320, 291200},	/* fuj0e cyl 363 - 817 */
1301 		{305280, 218240}, 	/* fuj0f cyl 477 - 817 */
1302 		{378240, 145280},	/* fuj0g cyl 591 - 817 */
1303 		{451200, 72320}		/* fug0h cyl 705 - 817 */
1304 	},
1305 	{ 32, 24, 711, 512, "xfd",
1306 		{ 0,	 40704 },	/* a cyl   0 - 52 */
1307 		{ 40704, 40704 },	/* b cyl  53 - 105 */
1308 		{ 81408, 460800 },	/* c cyl 106 - 705 */
1309 		{ 0,	 81408 },	/* d cyl 709 - 710 (a & b) */
1310 		{ 0,	 542208 },	/* e cyl   0 - 705 */
1311 		{ 40704, 501504 },	/* f cyl  53 - 705 (b & c) */
1312 		{ 81408, 230400 },	/* g cyl 106 - 405 (1/2 of c) */
1313 		{ 311808,230400 }	/* h cyl 406 - 705 (1/2 of c) */
1314 	},
1315 	{ 32, 19, 823, 512, "smd",
1316 		{0,	 40128},	/* a cyl   0-65 */
1317 		{40128,  27360},	/* b cyl  66-110 */
1318 		{67488,  429856},	/* c cyl 111-817 */
1319 		{139232, 358112},	/* d cyl 229 - 817 */
1320 		{210976, 286368},	/* e cyl 347 - 817 */
1321 		{282720, 214624},	/* f cyl 465 - 817 */
1322 		{354464, 142880},	/* g cyl 583 - 817 */
1323 		{426208, 71136}		/* h cyl 701 - 817 */
1324 	},
1325 	{ 18, 15, 1224, 1024, "mxd",
1326 		{0,	 21600},	/* a cyl   0-79 */
1327 		{21600,  22410},	/* b cyl  80-162 */
1328 		{44010,  285120},	/* c cyl 163-1217 */
1329 #ifdef notyet
1330 		{x, 237600},	/* d cyl y - 1217 */
1331 		{x, 190080},	/* e cyl y - 1217 */
1332 		{x, 142560},	/* f cyl y - 1217 */
1333 		{x, 95040},	/* g cyl y - 1217 */
1334 		{x, 47520}		/* h cyl 701 - 817 */
1335 #endif
1336 	},
1337 	{ 32, 10, 823, 512, "fsd",
1338 		{0,	 19200},	/* a cyl   0 -  59 */
1339 		{19200,	 24000},	/* b cyl  60 - 134 */
1340 		{43200,	 218560},	/* c cyl 135 - 817 */
1341 	}
1342 };
1343 #define	NVDST	(sizeof (vdst) / sizeof (vdst[0]))
1344 
1345 /*
1346  * Construct a label for an unlabeled pack.  We
1347  * deduce the drive type by reading from the last
1348  * track on successively smaller drives until we
1349  * don't get an error.
1350  */
1351 vdmaptype(vi, lp)
1352 	register struct vba_device *vi;
1353 	register struct disklabel *lp;
1354 {
1355 	register struct vdsoftc *vd;
1356 	register struct vdst *p;
1357 	struct vba_ctlr *vm = vi->ui_mi;
1358 	int i;
1359 
1360 	vd = &vdsoftc[vi->ui_ctlr];
1361 	for (p = vdst; p < &vdst[NVDST]; p++) {
1362 		if (vd->vd_type == VDTYPE_VDDC && p->nsec != 32)
1363 			continue;
1364 		lp->d_nsectors = p->nsec;
1365 		lp->d_ntracks = p->ntrack;
1366 		lp->d_ncylinders = p->ncyl;
1367 		lp->d_secsize = p->secsize;
1368 		if (!vdreset_drive(vi))
1369 			return (0);
1370 		vd->vd_dcb.opcode = VDOP_RD;
1371 		vd->vd_dcb.intflg = DCBINT_NONE;
1372 		vd->vd_dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
1373 		vd->vd_dcb.devselect = dksoftc[vi->ui_unit].dk_dcb.devselect;
1374 		vd->vd_dcb.trailcnt = sizeof (struct trrw) / sizeof (long);
1375 		vd->vd_dcb.trail.rwtrail.memadr =
1376 		    vtoph((struct proc *)0, (unsigned)vd->vd_rbuf.vb_rawbuf);
1377 		vd->vd_dcb.trail.rwtrail.wcount = lp->d_secsize / sizeof(short);
1378 		vd->vd_dcb.operrsta = 0;
1379 		vd->vd_dcb.trail.rwtrail.disk.cylinder = p->ncyl - 2;
1380 		vd->vd_dcb.trail.rwtrail.disk.track = p->ntrack - 1;
1381 		vd->vd_dcb.trail.rwtrail.disk.sector = p->nsec - 1;
1382 		vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys;
1383 		vd->vd_mdcb.mdcb_status = 0;
1384 		VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type);
1385 		if (!vdpoll(vm, 60))
1386 			printf(" during probe\n");
1387 		if ((vd->vd_dcb.operrsta & VDERR_HARD) == 0)
1388 			break;
1389 	}
1390 	if (p >= &vdst[NVDST])
1391 		return (0);
1392 
1393 	for (i = 0; i < 8; i++) {
1394 		lp->d_partitions[i].p_offset = p->parts[i].off;
1395 		lp->d_partitions[i].p_size = p->parts[i].size;
1396 	}
1397 	lp->d_npartitions = 8;
1398 	lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
1399 	lp->d_rpm = 3600;
1400 	bcopy(p->name, lp->d_typename, 4);
1401 	return (1);
1402 }
1403 #endif COMPAT_42
1404 #endif
1405