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