xref: /csrg-svn/sys/tahoe/vba/vd.c (revision 30756)
1*30756Skarels /*	vd.c	1.18	87/04/02	*/
224004Ssam 
329564Ssam #include "dk.h"
424004Ssam #if NVD > 0
524004Ssam /*
630519Ssam  * Versabus VDDC/SMDE driver.
725675Ssam  */
825675Ssam #include "param.h"
925675Ssam #include "buf.h"
1025675Ssam #include "cmap.h"
1125675Ssam #include "conf.h"
1225675Ssam #include "dir.h"
1329564Ssam #include "dkstat.h"
1430519Ssam #include "disklabel.h"
1525675Ssam #include "map.h"
1630519Ssam #include "file.h"
1725675Ssam #include "systm.h"
1825675Ssam #include "user.h"
1925675Ssam #include "vmmac.h"
2025675Ssam #include "proc.h"
2125675Ssam #include "uio.h"
2230370Skarels #include "syslog.h"
2330370Skarels #include "kernel.h"
2430519Ssam #include "ioctl.h"
25*30756Skarels #include "stat.h"
2624004Ssam 
2729951Skarels #include "../tahoe/cpu.h"
2829951Skarels #include "../tahoe/mtpr.h"
2929951Skarels #include "../tahoe/pte.h"
3029951Skarels 
3125675Ssam #include "../tahoevba/vbavar.h"
3225928Ssam #include "../tahoevba/vdreg.h"
3324004Ssam 
3430519Ssam #define	COMPAT_42
3530519Ssam 
3630519Ssam #define vdunit(dev)	(minor(dev) >> 3)
3730519Ssam #define vdpart(dev)	(minor(dev) & 0x07)
3830519Ssam #define	vdminor(unit,part)	(((unit) << 3) | (part))
3924004Ssam 
4024004Ssam struct	vba_ctlr *vdminfo[NVD];
4129564Ssam struct  vba_device *vddinfo[NDK];
42*30756Skarels int	vdprobe(), vdslave(), vdattach(), vddgo(), vdstrategy();
4330519Ssam long	vdaddr[] = { 0xffff2000, 0xffff2100, 0xffff2200, 0xffff2300, 0 };
4425675Ssam struct	vba_driver vddriver =
4530519Ssam   { vdprobe, vdslave, vdattach, vddgo, vdaddr, "dk", vddinfo, "vd", vdminfo };
4624004Ssam 
4724004Ssam /*
4830519Ssam  * Per-controller state.
4930519Ssam  */
5030519Ssam struct vdsoftc {
5130519Ssam 	u_short	vd_flags;
5230519Ssam #define	VD_INIT		0x1	/* controller initialized */
5330519Ssam #define	VD_STARTED	0x2	/* start command issued */
5430519Ssam #define	VD_DOSEEKS	0x4	/* should overlap seeks */
55*30756Skarels #define	VD_SCATGATH	0x8	/* can do scatter-gather commands (correctly) */
5630519Ssam 	u_short	vd_type;	/* controller type */
5730519Ssam 	u_short	vd_wticks;	/* timeout */
5830519Ssam 	struct	mdcb vd_mdcb;	/* master command block */
5930519Ssam 	u_long	vd_mdcbphys;	/* physical address of vd_mdcb */
6030519Ssam 	struct	dcb vd_dcb;	/* i/o command block */
6130519Ssam 	u_long	vd_dcbphys;	/* physical address of vd_dcb */
6230601Skarels 	struct	vb_buf vd_rbuf;	/* vba resources */
6330519Ssam } vdsoftc[NVD];
6430519Ssam 
6530519Ssam /*
6625675Ssam  * Per-drive state.
6725675Ssam  */
6830519Ssam struct	dksoftc {
6930519Ssam 	u_short	dk_state;	/* open fsm */
70*30756Skarels 	u_short	dk_copenpart;	/* character units open on this drive */
71*30756Skarels 	u_short	dk_bopenpart;	/* block units open on this drive */
72*30756Skarels 	u_short	dk_openpart;	/* all units open on this drive */
73*30756Skarels #ifndef SECSIZE
74*30756Skarels 	u_short	dk_bshift;	/* shift for * (DEV_BSIZE / sectorsize) XXX */
75*30756Skarels #endif SECSIZE
7630519Ssam 	u_int	dk_curcyl;	/* last selected cylinder */
77*30756Skarels 	struct	skdcb dk_dcb;	/* seek command block */
7830519Ssam 	u_long	dk_dcbphys;	/* physical address of dk_dcb */
7930519Ssam } dksoftc[NDK];
8024004Ssam 
8124004Ssam /*
8230519Ssam  * Drive states.  Used during steps of open/initialization.
8330519Ssam  * States < OPEN (> 0) are transient, during an open operation.
8430519Ssam  * OPENRAW is used for unabeled disks, to allow format operations.
8525675Ssam  */
8630519Ssam #define	CLOSED		0		/* disk is closed */
8730519Ssam #define	WANTOPEN	1		/* open requested, not started */
8830519Ssam #define	WANTOPENRAW	2		/* open requested, no label */
8930519Ssam #define	RDLABEL		3		/* reading pack label */
9030519Ssam #define	OPEN		4		/* intialized and ready */
9130519Ssam #define	OPENRAW		5		/* open, no label */
9224004Ssam 
9330519Ssam struct	buf rdkbuf[NDK];	/* raw i/o buffer headers */
9430519Ssam struct	buf dkutab[NDK];	/* i/o queue headers */
9530519Ssam struct	disklabel dklabel[NDK];	/* pack labels */
9624004Ssam 
9730519Ssam #define b_cylin	b_resid
9830574Skarels #define	b_track	b_error		/* used for seek commands */
9930574Skarels #define	b_seekf	b_forw		/* second queue on um_tab */
10030574Skarels #define	b_seekl	b_back		/* second queue on um_tab */
10130519Ssam 
10230519Ssam int	vdwstart, vdwatch();
10330519Ssam 
10424004Ssam /*
10525675Ssam  * See if the controller is really there; if so, initialize it.
10625675Ssam  */
10725857Ssam vdprobe(reg, vm)
10825857Ssam 	caddr_t reg;
10925857Ssam 	struct vba_ctlr *vm;
11025675Ssam {
11125857Ssam 	register br, cvec;		/* must be r12, r11 */
11230519Ssam 	register struct vddevice *vdaddr = (struct vddevice *)reg;
11330519Ssam 	struct vdsoftc *vd;
11430573Skarels 	int s;
11525857Ssam 
11630370Skarels #ifdef lint
11730370Skarels 	br = 0; cvec = br; br = cvec;
11830370Skarels 	vdintr(0);
11930370Skarels #endif
12025857Ssam 	if (badaddr((caddr_t)reg, 2))
12125675Ssam 		return (0);
12230519Ssam 	vd = &vdsoftc[vm->um_ctlr];
12330519Ssam 	vdaddr->vdreset = 0xffffffff;
12425675Ssam 	DELAY(1000000);
12530519Ssam 	if (vdaddr->vdreset != (unsigned)0xffffffff) {
12630519Ssam 		vd->vd_type = VDTYPE_VDDC;
12730519Ssam 		vd->vd_flags &= ~VD_DOSEEKS;
12825675Ssam 		DELAY(1000000);
12925675Ssam 	} else {
13030519Ssam 		vd->vd_type = VDTYPE_SMDE;
13130519Ssam 		vd->vd_flags |= VD_DOSEEKS;
13230519Ssam 		vdaddr->vdrstclr = 0;
13325675Ssam 		DELAY(3000000);
13430519Ssam 		vdaddr->vdcsr = 0;
13530519Ssam 		vdaddr->vdtcf_mdcb = AM_ENPDA;
13630519Ssam 		vdaddr->vdtcf_dcb = AM_ENPDA;
13730519Ssam 		vdaddr->vdtcf_trail = AM_ENPDA;
13830519Ssam 		vdaddr->vdtcf_data = AM_ENPDA;
13930519Ssam 		vdaddr->vdccf = CCF_SEN | CCF_DER | CCF_STS |
14029921Skarels 		    XMD_32BIT | BSZ_16WRD |
14125925Ssam 		    CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR;
14225675Ssam 	}
14330519Ssam 	vd->vd_mdcbphys = vtoph((struct proc *)0, (unsigned)&vd->vd_mdcb);
14430519Ssam 	vd->vd_dcbphys = vtoph((struct proc *)0, (unsigned)&vd->vd_dcb);
14530519Ssam 	vm->um_addr = reg;		/* XXX */
14630573Skarels 	s = spl7();
14730519Ssam 	if (!vdcmd(vm, VDOP_INIT, 10) || !vdcmd(vm, VDOP_DIAG, 10)) {
14830519Ssam 		printf("vd%d: %s cmd failed\n", vm->um_ctlr,
14930519Ssam 		    vd->vd_dcb.opcode == VDOP_INIT ? "init" : "diag");
15030573Skarels 		splx(s);
15130519Ssam 		return (0);
15230519Ssam 	}
153*30756Skarels 	if (vd->vd_type == VDTYPE_SMDE) {
154*30756Skarels 		vd->vd_dcb.trail.idtrail.date = 0;
155*30756Skarels 		if (vdcmd(vm, VDOP_IDENT, 10)) {
156*30756Skarels 			uncache(&vd->vd_dcb.trail.idtrail.date);
157*30756Skarels 			if (vd->vd_dcb.trail.idtrail.date != 0)
158*30756Skarels 				vd->vd_flags |= VD_SCATGATH;
159*30756Skarels 		}
160*30756Skarels 	}
16130573Skarels 	splx(s);
16225925Ssam 	/*
16325950Ssam 	 * Allocate page tables and i/o buffer.
16425925Ssam 	 */
165*30756Skarels 	vbainit(&vd->vd_rbuf, MAXPHYS,
16630601Skarels 	    vd->vd_type == VDTYPE_VDDC ? VB_24BIT : VB_32BIT);
16725857Ssam 	br = 0x17, cvec = 0xe0 + vm->um_ctlr;	/* XXX */
16830519Ssam 	return (sizeof (struct vddevice));
16925675Ssam }
17024004Ssam 
17124004Ssam /*
17230519Ssam  * See if a drive is really there.
17330519Ssam  *
17430519Ssam  * Can't read pack label here as various data structures
17530519Ssam  * aren't setup for doing a read in a straightforward
17630519Ssam  * manner.  Instead just probe for the drive and leave
17730519Ssam  * the pack label stuff to the attach routine.
17825675Ssam  */
17925675Ssam vdslave(vi, addr)
18025675Ssam 	register struct vba_device *vi;
18130519Ssam 	struct vddevice *vdaddr;
18225675Ssam {
18330519Ssam 	register struct disklabel *lp = &dklabel[vi->ui_unit];
18430519Ssam 	struct vdsoftc *vd = &vdsoftc[vi->ui_ctlr];
18524004Ssam 
18630519Ssam 	if ((vd->vd_flags&VD_INIT) == 0) {
187*30756Skarels 		printf("vd%d: %s controller%s\n", vi->ui_ctlr,
188*30756Skarels 		    vd->vd_type == VDTYPE_VDDC ? "VDDC" : "SMDE",
189*30756Skarels 		    (vd->vd_flags & VD_SCATGATH) ? " with scatter-gather" : "");
19030519Ssam 		vd->vd_flags |= VD_INIT;
19125675Ssam 	}
19230519Ssam 
19325675Ssam 	/*
19430519Ssam 	 * Initialize label enough to do a reset on
19530519Ssam 	 * the drive.  The remainder of the default
19630519Ssam 	 * label values will be filled in in vdinit
19730519Ssam 	 * at attach time.
19825675Ssam 	 */
19930519Ssam 	lp->d_secsize = DEV_BSIZE / 2;		/* XXX */
20030519Ssam 	lp->d_nsectors = 32;
20130519Ssam 	lp->d_ntracks = 24;
20230519Ssam 	lp->d_ncylinders = 711;
20330519Ssam 	lp->d_secpercyl = 32*24;
20430519Ssam 	return (vdreset_drive(vi));
20524004Ssam }
20624004Ssam 
20730519Ssam vdattach(vi)
20830519Ssam 	register struct vba_device *vi;
20924004Ssam {
21030519Ssam 	register int unit = vi->ui_unit;
21130519Ssam 	register struct dksoftc *dk = &dksoftc[unit];
21230519Ssam 	register struct disklabel *lp;
21325675Ssam 
21430519Ssam 	/*
21530519Ssam 	 * Initialize invariant portion of
21630519Ssam 	 * dcb used for overlapped seeks.
21730519Ssam 	 */
21830519Ssam 	dk->dk_dcb.opcode = VDOP_SEEK;
21930519Ssam 	dk->dk_dcb.intflg = DCBINT_NONE | DCBINT_PBA;
22030519Ssam 	dk->dk_dcb.devselect = vi->ui_slave;
221*30756Skarels 	dk->dk_dcb.trailcnt = sizeof (struct trseek) / sizeof (long);
22230519Ssam 	dk->dk_dcb.trail.sktrail.skaddr.sector = 0;
22330519Ssam 	dk->dk_dcbphys = vtoph((struct proc *)0, (unsigned)&dk->dk_dcb);
22430601Skarels 	/*
22530601Skarels 	 * Try to initialize device and read pack label.
22630601Skarels 	 */
22730601Skarels 	if (vdinit(vdminor(unit, 0), 0) != 0) {
22830601Skarels 		printf(": unknown drive type");
22930601Skarels 		return;
23030601Skarels 	}
23130519Ssam 	lp = &dklabel[unit];
23230519Ssam 	printf(": %s <ntrak %d, ncyl %d, nsec %d>",
23330519Ssam 	    lp->d_typename, lp->d_ntracks, lp->d_ncylinders, lp->d_nsectors);
23430519Ssam 	/*
23530519Ssam 	 * (60 / rpm) / (sectors per track * (bytes per sector / 2))
23630519Ssam 	 */
23730519Ssam 	if (vi->ui_dk >= 0)
23830519Ssam 		dk_mspw[vi->ui_dk] = 120.0 /
23930519Ssam 		    (lp->d_rpm * lp->d_nsectors * lp->d_secsize);
24030519Ssam #ifdef notyet
24130573Skarels 	addswap(makedev(VDMAJOR, vdminor(unit, 0)), lp);
24230519Ssam #endif
24324004Ssam }
24424004Ssam 
245*30756Skarels vdopen(dev, flags, fmt)
24630519Ssam 	dev_t dev;
247*30756Skarels 	int flags, fmt;
24824004Ssam {
24930519Ssam 	register unit = vdunit(dev);
25030519Ssam 	register struct disklabel *lp;
25130519Ssam 	register struct dksoftc *dk;
25230519Ssam 	register struct partition *pp;
25330519Ssam 	struct vba_device *vi;
254*30756Skarels 	int s, error, part = vdpart(dev), mask = 1 << part;
25530519Ssam 	daddr_t start, end;
25624004Ssam 
25730519Ssam 	if (unit >= NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0)
25830519Ssam 		return (ENXIO);
25930519Ssam 	lp = &dklabel[unit];
26030519Ssam 	dk = &dksoftc[unit];
26130519Ssam 
26230519Ssam 	s = spl7();
26330519Ssam 	while (dk->dk_state != OPEN && dk->dk_state != OPENRAW &&
26430519Ssam 	    dk->dk_state != CLOSED)
26530519Ssam 		sleep((caddr_t)dk, PZERO+1);
26630519Ssam 	splx(s);
26730519Ssam 	if (dk->dk_state != OPEN && dk->dk_state != OPENRAW)
26830519Ssam 		if (error = vdinit(dev, flags))
26930519Ssam 			return (error);
27030573Skarels 
27130573Skarels 	if (vdwstart == 0) {
27230573Skarels 		timeout(vdwatch, (caddr_t)0, hz);
27330573Skarels 		vdwstart++;
27430573Skarels 	}
27530519Ssam 	/*
27630519Ssam 	 * Warn if a partion is opened
27730519Ssam 	 * that overlaps another partition which is open
27830519Ssam 	 * unless one is the "raw" partition (whole disk).
27930519Ssam 	 */
28030519Ssam #define	RAWPART		2		/* 'c' partition */	/* XXX */
28130519Ssam 	if ((dk->dk_openpart & (1 << part)) == 0 &&
28230519Ssam 	    part != RAWPART) {
28330519Ssam 		pp = &lp->d_partitions[part];
28430519Ssam 		start = pp->p_offset;
28530519Ssam 		end = pp->p_offset + pp->p_size;
28630519Ssam 		for (pp = lp->d_partitions;
28730519Ssam 		     pp < &lp->d_partitions[lp->d_npartitions]; pp++) {
28830519Ssam 			if (pp->p_offset + pp->p_size <= start ||
28930519Ssam 			    pp->p_offset >= end)
29030519Ssam 				continue;
29130519Ssam 			if (pp - lp->d_partitions == RAWPART)
29230519Ssam 				continue;
29330519Ssam 			if (dk->dk_openpart & (1 << (pp - lp->d_partitions)))
29430519Ssam 				log(LOG_WARNING,
29530519Ssam 				    "dk%d%c: overlaps open partition (%c)\n",
29630519Ssam 				    unit, part + 'a',
29730519Ssam 				    pp - lp->d_partitions + 'a');
29830519Ssam 		}
29924004Ssam 	}
30030519Ssam 	if (part >= lp->d_npartitions)
30130519Ssam 		return (ENXIO);
302*30756Skarels 	dk->dk_openpart |= mask;
303*30756Skarels 	switch (fmt) {
304*30756Skarels 	case S_IFCHR:
305*30756Skarels 		dk->dk_copenpart |= mask;
306*30756Skarels 		break;
307*30756Skarels 	case S_IFBLK:
308*30756Skarels 		dk->dk_bopenpart |= mask;
309*30756Skarels 		break;
310*30756Skarels 	}
31130519Ssam 	return (0);
31225675Ssam }
31324004Ssam 
314*30756Skarels vdclose(dev, flags, fmt)
31530519Ssam 	dev_t dev;
316*30756Skarels 	int flags, fmt;
31724004Ssam {
31830519Ssam 	register int unit = vdunit(dev);
31930519Ssam 	register struct dksoftc *dk = &dksoftc[unit];
320*30756Skarels 	int part = vdpart(dev), mask = 1 << part;
32124004Ssam 
322*30756Skarels 	switch (fmt) {
323*30756Skarels 	case S_IFCHR:
324*30756Skarels 		dk->dk_copenpart &= ~mask;
325*30756Skarels 		break;
326*30756Skarels 	case S_IFBLK:
327*30756Skarels 		dk->dk_bopenpart &= ~mask;
328*30756Skarels 		break;
329*30756Skarels 	}
330*30756Skarels 	if (((dk->dk_copenpart | dk->dk_bopenpart) & mask) == 0)
331*30756Skarels 		dk->dk_openpart &= ~mask;
33230519Ssam 	/*
33330519Ssam 	 * Should wait for i/o to complete on this partition
33430519Ssam 	 * even if others are open, but wait for work on blkflush().
33530519Ssam 	 */
33630519Ssam 	if (dk->dk_openpart == 0) {
33730573Skarels 		int s = spl7();
33830573Skarels 		while (dkutab[unit].b_actf)
33930573Skarels 			sleep((caddr_t)dk, PZERO-1);
34030519Ssam 		splx(s);
34130519Ssam 		dk->dk_state = CLOSED;
34224004Ssam 	}
343*30756Skarels 	return (0);
34425675Ssam }
34524004Ssam 
34630519Ssam vdinit(dev, flags)
34730519Ssam 	dev_t dev;
34830519Ssam 	int flags;
34925675Ssam {
35030519Ssam 	register struct disklabel *lp;
35130519Ssam 	register struct dksoftc *dk;
35230519Ssam 	struct vba_device *vi;
35330519Ssam 	int unit = vdunit(dev), error = 0;
354*30756Skarels 	char *msg, *readdisklabel();
35530519Ssam 	extern int cold;
35625675Ssam 
35730519Ssam 	dk = &dksoftc[unit];
358*30756Skarels #ifndef SECSIZE
359*30756Skarels 	dk->dk_bshift = 1;		/* DEV_BSIZE / 512 */
360*30756Skarels #endif SECSIZE
36130519Ssam 	if (flags & O_NDELAY) {
36230519Ssam 		dk->dk_state = OPENRAW;
363*30756Skarels 		return;
36430519Ssam 	}
36530519Ssam 	dk->dk_state = RDLABEL;
36630519Ssam 	lp = &dklabel[unit];
36730519Ssam 	vi = vddinfo[unit];
368*30756Skarels 	if (msg = readdisklabel(dev, vdstrategy, lp)) {
36930519Ssam 		if (cold)
37030601Skarels 			printf(": %s", msg);
37130519Ssam 		else
37230601Skarels 			log(LOG_ERR, "dk%d: %s\n", vi->ui_unit, msg);
37330519Ssam #ifdef COMPAT_42
374*30756Skarels 		if (!vdmaptype(vi, lp))
375*30756Skarels 			dk->dk_state = OPENRAW;
376*30756Skarels 		else
37730519Ssam 			dk->dk_state = OPEN;
37830519Ssam #else
37930519Ssam 		dk->dk_state = OPENRAW;
38030519Ssam #endif
381*30756Skarels 	} else {
382*30756Skarels 		/*
383*30756Skarels 		 * Now that we have the label, configure
384*30756Skarels 		 * the correct drive parameters.
385*30756Skarels 		 */
386*30756Skarels 		if (!vdreset_drive(vi)) {
387*30756Skarels 			dk->dk_state = CLOSED;
388*30756Skarels 			error = ENXIO;
389*30756Skarels 		} else
390*30756Skarels 			dk->dk_state = OPEN;
39125675Ssam 	}
392*30756Skarels #ifndef SECSIZE
393*30756Skarels 	/*
394*30756Skarels 	 * If open, calculate scaling shift for
395*30756Skarels 	 * mapping DEV_BSIZE blocks to drive sectors.
396*30756Skarels 	 */
397*30756Skarels 	if (dk->dk_state == OPEN || dk->dk_state == OPENRAW) {
398*30756Skarels 		int mul = DEV_BSIZE / lp->d_secsize;
399*30756Skarels 		dk->dk_bshift = 0;
400*30756Skarels 		while ((mul >>= 1) > 0)
401*30756Skarels 			dk->dk_bshift++;
40230519Ssam 	}
403*30756Skarels #endif SECSIZE
40430519Ssam 	wakeup((caddr_t)dk);
40530519Ssam 	return (error);
40624004Ssam }
40724004Ssam 
40825675Ssam /*ARGSUSED*/
40930519Ssam vddgo(vm)
41030519Ssam 	struct vba_device *vm;
41124004Ssam {
41224004Ssam 
41324004Ssam }
41424004Ssam 
41524004Ssam vdstrategy(bp)
41625675Ssam 	register struct buf *bp;
41724004Ssam {
41830519Ssam 	register struct vba_device *vi;
41930519Ssam 	register struct disklabel *lp;
42030519Ssam 	register struct dksoftc *dk;
42130519Ssam 	register int unit;
42230573Skarels 	register daddr_t sn;
42330519Ssam 	struct buf *dp;
42430573Skarels 	daddr_t sz, maxsz;
42530519Ssam 	int part, s;
42624004Ssam 
42730519Ssam 	unit = vdunit(bp->b_dev);
42830519Ssam 	if (unit > NDK) {
42929954Skarels 		bp->b_error = ENXIO;
43025675Ssam 		goto bad;
43129954Skarels 	}
43230519Ssam 	vi = vddinfo[unit];
43330519Ssam 	lp = &dklabel[unit];
43430519Ssam 	if (vi == 0 || vi->ui_alive == 0) {
43530519Ssam 		bp->b_error = ENXIO;
43630519Ssam 		goto bad;
43730519Ssam 	}
43830601Skarels 	sz = (bp->b_bcount + lp->d_secsize - 1) / lp->d_secsize;
43930519Ssam 	dk = &dksoftc[unit];
44030519Ssam 	if (dk->dk_state < OPEN)
44130519Ssam 		goto q;
44230519Ssam 	part = vdpart(bp->b_dev);
44330519Ssam 	if ((dk->dk_openpart & (1 << part)) == 0) {
44430519Ssam 		bp->b_error = ENODEV;
44530519Ssam 		goto bad;
44630519Ssam 	}
44730519Ssam 	maxsz = lp->d_partitions[part].p_size;
448*30756Skarels #ifndef SECSIZE
449*30756Skarels 	sn = bp->b_blkno << dk->dk_bshift;
450*30756Skarels #else SECSIZE
45130573Skarels 	sn = bp->b_blkno;
452*30756Skarels #endif SECSIZE
45330519Ssam 	if (sn < 0 || sn + sz > maxsz) {
45430519Ssam 		if (sn == maxsz) {
45529954Skarels 			bp->b_resid = bp->b_bcount;
45629954Skarels 			goto done;
45729954Skarels 		}
458*30756Skarels 		sz = maxsz - sn;
45930573Skarels 		if (sz <= 0) {
46030573Skarels 			bp->b_error = EINVAL;
46130573Skarels 			goto bad;
46230573Skarels 		}
46330573Skarels 		bp->b_bcount = sz * lp->d_secsize;
46425675Ssam 	}
46530519Ssam 	bp->b_cylin = (sn + lp->d_partitions[part].p_offset) / lp->d_secpercyl;
466*30756Skarels #ifdef SECSIZE
467*30756Skarels if (bp->b_blksize != lp->d_secsize && (bp->b_flags & B_PGIN) == 0)
468*30756Skarels panic("vdstrat blksize");
469*30756Skarels #endif SECSIZE
47030519Ssam q:
47125675Ssam 	s = spl7();
47230519Ssam 	dp = &dkutab[vi->ui_unit];
47330519Ssam 	disksort(dp, bp);
47430519Ssam 	if (!dp->b_active) {
47530519Ssam 		(void) vdustart(vi);
47630573Skarels 		if (!vi->ui_mi->um_tab.b_active)
47730519Ssam 			vdstart(vi->ui_mi);
47824004Ssam 	}
47930519Ssam 	splx(s);
48024004Ssam 	return;
48125675Ssam bad:
48229954Skarels 	bp->b_flags |= B_ERROR;
48329954Skarels done:
48430519Ssam 	biodone(bp);
48530519Ssam 	return;
48624004Ssam }
48724004Ssam 
48830519Ssam vdustart(vi)
48930519Ssam 	register struct vba_device *vi;
49024004Ssam {
49130519Ssam 	register struct buf *bp, *dp;
49230519Ssam 	register struct vba_ctlr *vm;
49330519Ssam 	register int unit = vi->ui_unit;
49430519Ssam 	register struct dksoftc *dk;
49530519Ssam 	register struct vdsoftc *vd;
49630519Ssam 	struct disklabel *lp;
49724004Ssam 
49830519Ssam 	dp = &dkutab[unit];
49930519Ssam 	/*
50030519Ssam 	 * If queue empty, nothing to do.
50130519Ssam 	 */
50230519Ssam 	if ((bp = dp->b_actf) == NULL)
50330519Ssam 		return;
50430519Ssam 	/*
50530574Skarels 	 * If drive is off-cylinder and controller supports seeks,
50630574Skarels 	 * place drive on seek queue for controller.
50730574Skarels 	 * Otherwise, place on transfer queue.
50830519Ssam 	 */
50930519Ssam 	vd = &vdsoftc[vi->ui_ctlr];
51030519Ssam 	dk = &dksoftc[unit];
51130574Skarels 	vm = vi->ui_mi;
51230519Ssam 	if (bp->b_cylin != dk->dk_curcyl && vd->vd_flags&VD_DOSEEKS) {
51330519Ssam 		lp = &dklabel[unit];
51430574Skarels 		bp->b_track = (bp->b_blkno % lp->d_secpercyl) / lp->d_nsectors;
51530574Skarels 		if (vm->um_tab.b_seekf == NULL)
51630574Skarels 			vm->um_tab.b_seekf = dp;
51730574Skarels 		else
51830574Skarels 			vm->um_tab.b_seekl->b_forw = dp;
51930574Skarels 		vm->um_tab.b_seekl = dp;
52030574Skarels 	} else {
52130574Skarels 		if (vm->um_tab.b_actf == NULL)
52230574Skarels 			vm->um_tab.b_actf = dp;
52330574Skarels 		else
52430574Skarels 			vm->um_tab.b_actl->b_forw = dp;
52530574Skarels 		vm->um_tab.b_actl = dp;
52630519Ssam 	}
52730573Skarels 	dp->b_forw = NULL;
52830573Skarels 	dp->b_active++;
52925675Ssam }
53025675Ssam 
53125675Ssam /*
53230519Ssam  * Start next transfer on a controller.
53330574Skarels  * There are two queues of drives, the first on-cylinder
53430574Skarels  * and the second off-cylinder from their next transfers.
53530574Skarels  * Perform the first transfer for the first drive on the on-cylinder
53630574Skarels  * queue, if any, otherwise the first transfer for the first drive
53730574Skarels  * on the second queue.  Initiate seeks on remaining drives on the
53830574Skarels  * off-cylinder queue, then move them all to the on-cylinder queue.
53925675Ssam  */
54030519Ssam vdstart(vm)
54130519Ssam 	register struct vba_ctlr *vm;
54225675Ssam {
54325675Ssam 	register struct buf *bp;
54430519Ssam 	register struct vba_device *vi;
54530519Ssam 	register struct vdsoftc *vd;
54630519Ssam 	register struct dksoftc *dk;
54730519Ssam 	register struct disklabel *lp;
54830519Ssam 	register int slave;
54930519Ssam 	register struct dcb **dcbp;
55030519Ssam 	struct mdcb *mdcb;
55130519Ssam 	struct buf *dp;
55230519Ssam 	int sn, tn;
55325675Ssam 
55430519Ssam loop:
55530519Ssam 	/*
55630519Ssam 	 * Pull a request off the controller queue.
55730519Ssam 	 */
55830574Skarels 	if ((dp = vm->um_tab.b_actf) == NULL &&
55930574Skarels 	    (dp = vm->um_tab.b_seekf) == NULL)
56030519Ssam 		return;
56130519Ssam 	if ((bp = dp->b_actf) == NULL) {
56230601Skarels 		if (dp == vm->um_tab.b_actf)
56330601Skarels 			vm->um_tab.b_actf = dp->b_forw;
56430601Skarels 		else
56530601Skarels 			vm->um_tab.b_seekf = dp->b_forw;
56630519Ssam 		goto loop;
56730519Ssam 	}
56825675Ssam 
56924004Ssam 	/*
57030519Ssam 	 * Mark controller busy, and determine
57130519Ssam 	 * destination of this request.
57224004Ssam 	 */
57330519Ssam 	vm->um_tab.b_active++;
57430519Ssam 	vi = vddinfo[vdunit(bp->b_dev)];
57530519Ssam 	dk = &dksoftc[vi->ui_unit];
576*30756Skarels #ifndef SECSIZE
577*30756Skarels 	sn = bp->b_blkno << dk->dk_bshift;
578*30756Skarels #else SECSIZE
57930573Skarels 	sn = bp->b_blkno;
580*30756Skarels #endif SECSIZE
58130519Ssam 	lp = &dklabel[vi->ui_unit];
58230519Ssam 	sn %= lp->d_secpercyl;
58330519Ssam 	tn = sn / lp->d_nsectors;
58430519Ssam 	sn %= lp->d_nsectors;
58530519Ssam 
58630519Ssam 	/*
58730519Ssam 	 * Construct dcb for read/write command.
58830519Ssam 	 */
58930519Ssam 	vd = &vdsoftc[vm->um_ctlr];
59030519Ssam 	slave = vi->ui_slave;
59130519Ssam 	vd->vd_dcb.intflg = DCBINT_DONE;
59230519Ssam 	vd->vd_dcb.devselect = slave;
59330519Ssam 	vd->vd_dcb.operrsta = 0;
59430519Ssam 	vd->vd_dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
59530519Ssam 	vd->vd_dcb.trail.rwtrail.disk.cylinder = bp->b_cylin;
59630519Ssam 	vd->vd_dcb.trail.rwtrail.disk.track = tn;
59730519Ssam 	vd->vd_dcb.trail.rwtrail.disk.sector = sn;
59830574Skarels 	dk->dk_curcyl = bp->b_cylin;
59930574Skarels 	bp->b_track = 0;		/* init overloaded field */
600*30756Skarels 	vd->vd_dcb.trailcnt = sizeof (struct trrw) / sizeof (long);
601*30756Skarels 	if (vd->vd_flags & VD_SCATGATH &&
602*30756Skarels 	    ((int)bp->b_un.b_addr & (sizeof(long) - 1)) == 0) {
603*30756Skarels 		vd->vd_dcb.opcode = (bp->b_flags & B_READ)? VDOP_RAS : VDOP_GAW;
604*30756Skarels 		vd->vd_dcb.trailcnt += vba_sgsetup(bp, &vd->vd_rbuf,
605*30756Skarels 		    &vd->vd_dcb.trail.sgtrail);
606*30756Skarels 	} else {
607*30756Skarels 		vd->vd_dcb.opcode = (bp->b_flags & B_READ)? VDOP_RD : VDOP_WD;
608*30756Skarels 		vd->vd_dcb.trail.rwtrail.memadr =
609*30756Skarels 			vbasetup(bp, &vd->vd_rbuf, lp->d_secsize);
610*30756Skarels 		vd->vd_dcb.trail.rwtrail.wcount = (bp->b_bcount+1) >> 1;
611*30756Skarels 	}
61230574Skarels 	if (vi->ui_dk >= 0) {
61330574Skarels 		dk_busy |= 1<<vi->ui_dk;
61430574Skarels 		dk_xfer[vi->ui_dk]++;
61530574Skarels 		dk_wds[vi->ui_dk] += bp->b_bcount>>6;
61630574Skarels 	}
61730519Ssam 
61830519Ssam 	/*
61930519Ssam 	 * Look for any seeks to be performed on other drives on this
62030519Ssam 	 * controller.  If overlapped seeks exist, insert seek commands
62130519Ssam 	 * on the controller's command queue before the transfer.
62230519Ssam 	 */
62330519Ssam 	dcbp = &vd->vd_mdcb.mdcb_head;
62430519Ssam 
62530574Skarels 	if (dp == vm->um_tab.b_seekf)
62630574Skarels 		dp = dp->b_forw;
62730574Skarels 	else
62830574Skarels 		dp = vm->um_tab.b_seekf;
62930574Skarels 	for (; dp != NULL; dp = dp->b_forw) {
63030574Skarels 		if ((bp = dp->b_actf) == NULL)
63130574Skarels 			continue;
63230574Skarels 		vi = vddinfo[vdunit(bp->b_dev)];
63330574Skarels 		dk = &dksoftc[vi->ui_unit];
63430519Ssam 		dk->dk_curcyl = bp->b_cylin;
63530574Skarels 		if (vi->ui_dk >= 0)
63630574Skarels 			dk_seek[vi->ui_dk]++;
63730574Skarels 		dk->dk_dcb.operrsta = 0;
63830574Skarels 		dk->dk_dcb.trail.sktrail.skaddr.cylinder = bp->b_cylin;
63930574Skarels 		dk->dk_dcb.trail.sktrail.skaddr.track = bp->b_track;
64030574Skarels 		*dcbp = (struct dcb *)dk->dk_dcbphys;
64130574Skarels 		dcbp = &dk->dk_dcb.nxtdcb;
64224004Ssam 	}
64330519Ssam 	*dcbp = (struct dcb *)vd->vd_dcbphys;
64430574Skarels 	if (vm->um_tab.b_actf)
64530574Skarels 		vm->um_tab.b_actl->b_forw = vm->um_tab.b_seekf;
64630574Skarels 	else
64730574Skarels 		vm->um_tab.b_actf = vm->um_tab.b_seekf;
64830601Skarels 	if (vm->um_tab.b_seekf)
64930601Skarels 		vm->um_tab.b_actl = vm->um_tab.b_seekl;
65030574Skarels 	vm->um_tab.b_seekf = 0;
65124004Ssam 
65230519Ssam 	/*
65330519Ssam 	 * Initiate operation.
65430519Ssam 	 */
65530519Ssam 	vd->vd_mdcb.mdcb_status = 0;
65630519Ssam 	VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type);
65724004Ssam }
65824004Ssam 
65930519Ssam #define	DONTCARE (DCBS_DSE|DCBS_DSL|DCBS_TOP|DCBS_TOM|DCBS_FAIL|DCBS_DONE)
66024004Ssam /*
66124004Ssam  * Handle a disk interrupt.
66224004Ssam  */
66325675Ssam vdintr(ctlr)
66430519Ssam 	register ctlr;
66524004Ssam {
66630519Ssam 	register struct buf *bp, *dp;
66730519Ssam 	register struct vba_ctlr *vm = vdminfo[ctlr];
66830519Ssam 	register struct vba_device *vi;
66930519Ssam 	register struct vdsoftc *vd = &vdsoftc[ctlr];
67030519Ssam 	register status;
67130601Skarels 	int ecode;
67230573Skarels 	struct dksoftc *dk;
67324004Ssam 
67430519Ssam 	vd->vd_wticks = 0;
67530519Ssam 	if (!vm->um_tab.b_active) {
67625675Ssam 		printf("vd%d: stray interrupt\n", ctlr);
67724004Ssam 		return;
67824004Ssam 	}
67925675Ssam 	/*
68030519Ssam 	 * Get device and block structures, and a pointer
68130519Ssam 	 * to the vba_device for the drive.
68225675Ssam 	 */
68330519Ssam 	dp = vm->um_tab.b_actf;
68430519Ssam 	bp = dp->b_actf;
68530519Ssam 	vi = vddinfo[vdunit(bp->b_dev)];
68630574Skarels 	if (vi->ui_dk >= 0)
68730574Skarels 		dk_busy &= ~(1<<vi->ui_dk);
68830519Ssam 	/*
68930519Ssam 	 * Check for and process errors on
69030519Ssam 	 * either the drive or the controller.
69130519Ssam 	 */
69230519Ssam 	uncache(&vd->vd_dcb.operrsta);
69330519Ssam 	status = vd->vd_dcb.operrsta;
69430519Ssam 	if (status & VDERR_HARD) {
69530601Skarels 		if (vd->vd_type == VDTYPE_SMDE) {
69630601Skarels 			uncache(&vd->vd_dcb.err_code);
69730601Skarels 			ecode = vd->vd_dcb.err_code;
69830601Skarels 		}
69930519Ssam 		if (status & DCBS_WPT) {
70030519Ssam 			/*
70130519Ssam 			 * Give up on write locked devices immediately.
70230519Ssam 			 */
70330573Skarels 			printf("dk%d: write locked\n", vi->ui_unit);
70430519Ssam 			bp->b_flags |= B_ERROR;
70530573Skarels 		} else if (status & VDERR_RETRY) {
70630519Ssam 			if (status & VDERR_DRIVE) {
70730519Ssam 				if (!vdreset_drive(vi))
70830519Ssam 					vi->ui_alive = 0;
70930519Ssam 			} else if (status & VDERR_CTLR)
71030519Ssam 				vdreset_ctlr(vm);
71130519Ssam 			/*
71230519Ssam 			 * Retry transfer once, unless reset failed.
71330519Ssam 			 */
71430519Ssam 			if (!vi->ui_alive || bp->b_errcnt++ >= 2)
71530519Ssam 				goto hard;
71630519Ssam 			vm->um_tab.b_active = 0;	/* force retry */
71730519Ssam 		} else  {
71830519Ssam 	hard:
71930519Ssam 			bp->b_flags |= B_ERROR;
72030519Ssam 			/* NEED TO ADJUST b_blkno to failed sector */
72130519Ssam 			harderr(bp, "dk");
72230519Ssam 			printf("status %x (%b)", status,
72330519Ssam 			   status &~ DONTCARE, VDERRBITS);
72430601Skarels 			if (vd->vd_type == VDTYPE_SMDE)
72530601Skarels 				printf(" ecode %x", ecode);
72630519Ssam 			printf("\n");
72730519Ssam 		}
72830519Ssam 	} else if (status & DCBS_SOFT)
72930519Ssam 		vdsofterr(vd, bp, &vd->vd_dcb);
73030519Ssam 	if (vm->um_tab.b_active) {
73130519Ssam 		vm->um_tab.b_active = 0;
73230519Ssam 		vm->um_tab.b_errcnt = 0;
73330519Ssam 		vm->um_tab.b_actf = dp->b_forw;
73430519Ssam 		dp->b_active = 0;
73530519Ssam 		dp->b_errcnt = 0;
73630519Ssam 		dp->b_actf = bp->av_forw;
73730519Ssam 		bp->b_resid = 0;
73830601Skarels 		vbadone(bp, &vd->vd_rbuf);
73930519Ssam 		biodone(bp);
74030370Skarels 		/*
74130519Ssam 		 * If this unit has more work to do,
74230519Ssam 		 * then start it up right away.
74330370Skarels 		 */
74430519Ssam 		if (dp->b_actf)
74530519Ssam 			vdustart(vi);
74630573Skarels 		else if ((dk = &dksoftc[vi->ui_unit])->dk_openpart == 0)
74730573Skarels 			wakeup((caddr_t)dk);
74824004Ssam 	}
74925675Ssam 	/*
75030519Ssam 	 * If there are devices ready to
75130519Ssam 	 * transfer, start the controller.
75225675Ssam 	 */
75330601Skarels 	if (vm->um_tab.b_actf || vm->um_tab.b_seekf)
75430519Ssam 		vdstart(vm);
75524004Ssam }
75624004Ssam 
75730519Ssam vdsofterr(vd, bp, dcb)
75830519Ssam 	struct vdsoftc *vd;
75925675Ssam 	register struct buf *bp;
76030519Ssam 	register struct dcb *dcb;
76125675Ssam {
76230519Ssam 	int unit = vdunit(bp->b_dev), status = dcb->operrsta;
76330519Ssam 	char part = 'a' + vdpart(bp->b_dev);
76425675Ssam 
76530601Skarels 	if (status != (DCBS_CCD|DCBS_SOFT|DCBS_ERR|DCBS_DONE)) {
76630519Ssam 		if (vd->vd_type == VDTYPE_SMDE)
76730519Ssam 			uncache(&dcb->err_code);
76830519Ssam 		log(LOG_WARNING, "dk%d%c: soft error sn%d status %b ecode %x\n",
76930519Ssam 		    unit, part, bp->b_blkno, status, VDERRBITS, dcb->err_code);
77030519Ssam 	} else
77130370Skarels 		log(LOG_WARNING, "dk%d%c: soft ecc sn%d\n",
77230370Skarels 		    unit, part, bp->b_blkno);
77325675Ssam }
77425675Ssam 
77524004Ssam vdread(dev, uio)
77625675Ssam 	dev_t dev;
77725675Ssam 	struct uio *uio;
77824004Ssam {
77930519Ssam 	register int unit = vdunit(dev);
78024004Ssam 
78129564Ssam 	if (unit >= NDK)
78225675Ssam 		return (ENXIO);
78330519Ssam 	return (physio(vdstrategy, &rdkbuf[unit], dev, B_READ, minphys, uio));
78424004Ssam }
78524004Ssam 
78624004Ssam vdwrite(dev, uio)
78725675Ssam 	dev_t dev;
78825675Ssam 	struct uio *uio;
78924004Ssam {
79030519Ssam 	register int unit = vdunit(dev);
79124004Ssam 
79229564Ssam 	if (unit >= NDK)
79325675Ssam 		return (ENXIO);
79430519Ssam 	return (physio(vdstrategy, &rdkbuf[unit], dev, B_WRITE, minphys, uio));
79524004Ssam }
79624004Ssam 
79730519Ssam vdioctl(dev, cmd, data, flag)
79825675Ssam 	dev_t dev;
79930519Ssam 	int cmd;
80030519Ssam 	caddr_t data;
80130519Ssam 	int flag;
80224004Ssam {
80330519Ssam 	int unit = vdunit(dev);
80430519Ssam 	register struct disklabel *lp = &dklabel[unit];
80530519Ssam 	int error = 0;
80624004Ssam 
80730519Ssam 	switch (cmd) {
80830519Ssam 
80930519Ssam 	case DIOCGDINFO:
81030519Ssam 		*(struct disklabel *)data = *lp;
81130519Ssam 		break;
81230519Ssam 
81330573Skarels 	case DIOCGPART:
81430573Skarels 		((struct partinfo *)data)->disklab = lp;
81530573Skarels 		((struct partinfo *)data)->part =
81630573Skarels 		    &lp->d_partitions[vdpart(dev)];
81730519Ssam 		break;
81830519Ssam 
81930519Ssam 	case DIOCSDINFO:
82030519Ssam 		if ((flag & FWRITE) == 0)
82130519Ssam 			error = EBADF;
82230519Ssam 		else
82330519Ssam 			*lp = *(struct disklabel *)data;
82430519Ssam 		break;
82530519Ssam 
82630519Ssam 	case DIOCWDINFO: {
82730519Ssam 		struct buf *bp;
82830519Ssam 		struct disklabel *dlp;
82930519Ssam 
83030519Ssam 		if ((flag & FWRITE) == 0) {
83130519Ssam 			error = EBADF;
83230519Ssam 			break;
83330519Ssam 		}
83430519Ssam 		*lp = *(struct disklabel *)data;
83530519Ssam 		bp = geteblk(lp->d_secsize);
836*30756Skarels 		bp->b_dev = makedev(major(dev), vdminor(vdunit(dev), 0));
83730519Ssam 		bp->b_blkno = LABELSECTOR;
83830519Ssam 		bp->b_bcount = lp->d_secsize;
83930519Ssam 		bp->b_flags = B_READ;
84030519Ssam 		dlp = (struct disklabel *)(bp->b_un.b_addr + LABELOFFSET);
84130519Ssam 		vdstrategy(bp);
84230519Ssam 		biowait(bp);
84330519Ssam 		if (bp->b_flags & B_ERROR) {
84430519Ssam 			error = u.u_error;		/* XXX */
84530519Ssam 			u.u_error = 0;
84630519Ssam 			goto bad;
84730519Ssam 		}
84830519Ssam 		*dlp = *lp;
84930519Ssam 		bp->b_flags = B_WRITE;
85030519Ssam 		vdstrategy(bp);
85130519Ssam 		biowait(bp);
85230519Ssam 		if (bp->b_flags & B_ERROR) {
85330519Ssam 			error = u.u_error;		/* XXX */
85430519Ssam 			u.u_error = 0;
85530519Ssam 		}
85630519Ssam bad:
85730519Ssam 		brelse(bp);
85830519Ssam 		break;
85925675Ssam 	}
86030519Ssam 
86130519Ssam 	default:
86230519Ssam 		error = ENOTTY;
86330519Ssam 		break;
86424004Ssam 	}
86525675Ssam 	return (0);
86624004Ssam }
86724004Ssam 
86825675Ssam /*
86930519Ssam  * Watch for lost interrupts.
87025675Ssam  */
87130519Ssam vdwatch()
87230519Ssam {
87330519Ssam 	register struct vdsoftc *vd;
87430519Ssam 	register struct vba_ctlr *vm;
87525675Ssam 	register int ctlr, unit;
87630519Ssam 
87730519Ssam 	timeout(vdwatch, (caddr_t)0, hz);
87830519Ssam 	for (ctlr = 0; ctlr < NVD; ctlr++) {
87930519Ssam 		vm = vdminfo[ctlr];
88030519Ssam 		if (vm == 0 || vm->um_alive == 0)
88130519Ssam 			continue;
88230519Ssam 		vd = &vdsoftc[ctlr];
88330601Skarels 		if (vm->um_tab.b_active && vd->vd_wticks++ >= 20) {
88430519Ssam 			vd->vd_wticks = 0;
88530519Ssam 			printf("vd%d: lost interrupt\n", ctlr);
88630519Ssam 			/* abort pending dcb's and restart controller */
88730519Ssam 		}
88830519Ssam 	}
88930519Ssam }
89030519Ssam 
89130519Ssam #define	DBSIZE	64	/* controller limit with 1K sectors */
89230519Ssam /*
89330519Ssam  * Crash dump.
89430519Ssam  */
89530519Ssam vddump(dev)
89630519Ssam 	dev_t dev;
89724004Ssam {
89830519Ssam 	register struct vba_device *vi;
89930519Ssam 	register struct vba_ctlr *vm;
90030519Ssam 	register struct disklabel *lp;
90130519Ssam 	register struct vdsoftc *vd;
90230519Ssam 	struct dksoftc *dk;
90330519Ssam 	int part, unit, num;
90430601Skarels 	u_long start;
90524004Ssam 
90630519Ssam 	start = 0;
90730519Ssam 	unit = vdunit(dev);
90830519Ssam 	if (unit > NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0)
90930519Ssam 		return (ENXIO);
91030519Ssam 	dk = &dksoftc[unit];
91130519Ssam 	if (dk->dk_state != OPEN && dk->dk_state != OPENRAW)
91230519Ssam 		return (ENXIO);
91330519Ssam 	lp = &dklabel[unit];
91430519Ssam 	part = vdpart(dev);
91530519Ssam 	if (part >= lp->d_npartitions)
91630519Ssam 		return (ENXIO);
91730519Ssam 	vm = vdminfo[vi->ui_ctlr];
91830519Ssam 	vdreset_ctlr(vm);
91930519Ssam 	if (dumplo < 0)
92030519Ssam 		return (EINVAL);
92130519Ssam 	/*
922*30756Skarels 	 * Maxfree is in pages, dumplo is in DEV_BSIZE units.
92330519Ssam 	 */
92430519Ssam 	num = maxfree * (NBPG / lp->d_secsize);
925*30756Skarels 	dumplo *= DEV_BSIZE / lp->d_secsize;
92630519Ssam 	if (dumplo + num >= lp->d_partitions[vdpart(dev)].p_size)
92730519Ssam 		num = lp->d_partitions[vdpart(dev)].p_size - dumplo;
92830519Ssam 	vd = &vdsoftc[vm->um_ctlr];
92930519Ssam 	vd->vd_dcb.intflg = DCBINT_NONE;
93030519Ssam 	vd->vd_dcb.opcode = VDOP_WD;
93130519Ssam 	vd->vd_dcb.devselect = vi->ui_slave;
932*30756Skarels 	vd->vd_dcb.trailcnt = sizeof (struct trrw) / sizeof (long);
93330519Ssam 	while (num > 0) {
93430519Ssam 		int nsec, cn, sn, tn;
93530519Ssam 
93630519Ssam 		nsec = MIN(num, DBSIZE);
93730601Skarels 		sn = dumplo + start / lp->d_secsize;
93830519Ssam 		cn = (sn + lp->d_partitions[vdpart(dev)].p_offset) /
93930519Ssam 		    lp->d_secpercyl;
94030519Ssam 		sn %= lp->d_secpercyl;
94130519Ssam 		tn = sn / lp->d_nsectors;
94230519Ssam 		sn %= lp->d_nsectors;
94330519Ssam 		vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys;
94430519Ssam 		vd->vd_dcb.trail.rwtrail.memadr = start;
94530519Ssam 		vd->vd_dcb.trail.rwtrail.wcount = (nsec * lp->d_secsize) >> 1;
94630519Ssam 		vd->vd_dcb.trail.rwtrail.disk.cylinder = cn;
94730519Ssam 		vd->vd_dcb.trail.rwtrail.disk.track = tn;
94830519Ssam 		vd->vd_dcb.trail.rwtrail.disk.sector = sn;
94930519Ssam 		vd->vd_dcb.operrsta = 0;
95030519Ssam 		VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type);
95130519Ssam 		if (!vdpoll(vm, 5)) {
95230519Ssam 			printf(" during dump\n");
95330519Ssam 			return (EIO);
95430519Ssam 		}
95530519Ssam 		if (vd->vd_dcb.operrsta & VDERR_HARD) {
95630519Ssam 			printf("dk%d: hard error, status=%b\n", unit,
95730519Ssam 			    vd->vd_dcb.operrsta, VDERRBITS);
95830519Ssam 			return (EIO);
95930519Ssam 		}
96030519Ssam 		start += nsec * lp->d_secsize;
96130519Ssam 		num -= nsec;
96225675Ssam 	}
96330519Ssam 	return (0);
96424004Ssam }
96524004Ssam 
96624004Ssam vdsize(dev)
96725675Ssam 	dev_t dev;
96824004Ssam {
96930519Ssam 	register int unit = vdunit(dev);
97030519Ssam 	register struct dksoftc *dk;
97130519Ssam 	struct vba_device *vi;
97230519Ssam 	struct disklabel *lp;
97324004Ssam 
97430519Ssam 	if (unit >= NDK || (vi = vddinfo[unit]) == 0 || vi->ui_alive == 0 ||
97530519Ssam 	    (dk = &dksoftc[unit])->dk_state != OPEN)
97625675Ssam 		return (-1);
97730519Ssam 	lp = &dklabel[unit];
978*30756Skarels #ifdef SECSIZE
97930573Skarels 	return ((int)lp->d_partitions[vdpart(dev)].p_size);
980*30756Skarels #else SECSIZE
981*30756Skarels 	return ((int)lp->d_partitions[vdpart(dev)].p_size >> dk->dk_bshift);
982*30756Skarels #endif SECSIZE
98324004Ssam }
98424004Ssam 
98525675Ssam /*
98625675Ssam  * Perform a controller reset.
98725675Ssam  */
98830519Ssam vdreset_ctlr(vm)
98930519Ssam 	register struct vba_ctlr *vm;
99024004Ssam {
99130519Ssam 	register struct vddevice *vdaddr = (struct vddevice *)vm->um_addr;
99230519Ssam 	register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr];
99330519Ssam 	register int unit;
99430519Ssam 	struct vba_device *vi;
99525675Ssam 
99630519Ssam 	VDRESET(vdaddr, vd->vd_type);
99730519Ssam 	if (vd->vd_type == VDTYPE_SMDE) {
99830519Ssam 		vdaddr->vdcsr = 0;
99930519Ssam 		vdaddr->vdtcf_mdcb = AM_ENPDA;
100030519Ssam 		vdaddr->vdtcf_dcb = AM_ENPDA;
100130519Ssam 		vdaddr->vdtcf_trail = AM_ENPDA;
100230519Ssam 		vdaddr->vdtcf_data = AM_ENPDA;
100330519Ssam 		vdaddr->vdccf = CCF_STS | XMD_32BIT | BSZ_16WRD |
100425675Ssam 		    CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE | CCF_ERR;
100525675Ssam 	}
100630519Ssam 	if (!vdcmd(vm, VDOP_INIT, 10) || !vdcmd(vm, VDOP_DIAG, 10)) {
100730519Ssam 		printf("%s cmd failed\n",
100830519Ssam 		    vd->vd_dcb.opcode == VDOP_INIT ? "init" : "diag");
100930370Skarels 		return;
101025675Ssam 	}
101130519Ssam 	for (unit = 0; unit < NDK; unit++)
101230519Ssam 		if ((vi = vddinfo[unit])->ui_mi == vm && vi->ui_alive)
101330519Ssam 			(void) vdreset_drive(vi);
101430519Ssam }
101530519Ssam 
101630519Ssam vdreset_drive(vi)
101730519Ssam 	register struct vba_device *vi;
101830519Ssam {
101930519Ssam 	register struct disklabel *lp = &dklabel[vi->ui_unit];
102030519Ssam 	struct vba_ctlr *vm = vdminfo[vi->ui_ctlr];
102130519Ssam 	struct vddevice *vdaddr = (struct vddevice *)vm->um_addr;
102230519Ssam 	struct vdsoftc *vd = &vdsoftc[vi->ui_ctlr];
102330519Ssam 
102430519Ssam top:
102530519Ssam 	vd->vd_dcb.opcode = VDOP_CONFIG;		/* command */
102630519Ssam 	vd->vd_dcb.intflg = DCBINT_NONE;
102730519Ssam 	vd->vd_dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
102830519Ssam 	vd->vd_dcb.operrsta = 0;
102930519Ssam 	vd->vd_dcb.devselect = vi->ui_slave;
103030519Ssam 	vd->vd_dcb.trail.rstrail.ncyl = lp->d_ncylinders;
103130519Ssam 	vd->vd_dcb.trail.rstrail.nsurfaces = lp->d_ntracks;
103230519Ssam 	if (vd->vd_type == VDTYPE_SMDE) {
1033*30756Skarels 		vd->vd_dcb.trailcnt = sizeof (struct treset) / sizeof (long);
103430519Ssam 		vd->vd_dcb.trail.rstrail.nsectors = lp->d_nsectors;
103530601Skarels 		vd->vd_dcb.trail.rstrail.slip_sec = lp->d_sparespertrack;
103630519Ssam 		vd->vd_dcb.trail.rstrail.recovery = 0x18f;
103730519Ssam 	} else
103830519Ssam 		vd->vd_dcb.trailcnt = 2;		/* XXX */
103930519Ssam 	vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys;
104030519Ssam 	vd->vd_mdcb.mdcb_status = 0;
104130519Ssam 	VDGO(vdaddr, vd->vd_mdcbphys, vd->vd_type);
104230519Ssam 	if (!vdpoll(vm, 5)) {
104330519Ssam 		printf(" during config\n");
104430519Ssam 		return (0);
104525675Ssam 	}
104630519Ssam 	if (vd->vd_dcb.operrsta & VDERR_HARD) {
104730519Ssam 		if (vd->vd_type == VDTYPE_SMDE &&
104830519Ssam 		    (vdaddr->vdstatus[vi->ui_slave]&STA_US) == 0)
104930519Ssam 			return (0);
105030519Ssam 		if ((vd->vd_dcb.operrsta & (DCBS_OCYL|DCBS_NRDY)) == 0)
105130519Ssam 			printf("dk%d: config error\n", vi->ui_unit);
105230519Ssam 		else if ((vd->vd_flags&VD_STARTED) == 0) {
105330519Ssam 			int started;
105430519Ssam 
105530519Ssam 			printf("vd%d: starting drives, wait ... ", vm->um_ctlr);
105630519Ssam 			vd->vd_flags |= VD_STARTED;
105730519Ssam 			started = (vdcmd(vm, VDOP_START, 10) == 1);
105830519Ssam 			DELAY(62000000);
105930519Ssam 			printf("\n");
106030519Ssam 			if (started)
106130519Ssam 				goto top;
106230519Ssam 		}
106330519Ssam 		return (0);
106430519Ssam 	}
106530519Ssam 	return (1);
106625675Ssam }
106724004Ssam 
106825675Ssam /*
106930519Ssam  * Perform a command w/o trailer.
107025675Ssam  */
107130519Ssam vdcmd(vm, cmd, t)
107230519Ssam 	register struct vba_ctlr *vm;
107325675Ssam {
107430519Ssam 	register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr];
107525675Ssam 
107630519Ssam 	vd->vd_dcb.opcode = cmd;		/* command */
107730519Ssam 	vd->vd_dcb.intflg = DCBINT_NONE;
107830519Ssam 	vd->vd_dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
107930519Ssam 	vd->vd_dcb.operrsta = 0;
108030519Ssam 	vd->vd_dcb.devselect = 0;
108130519Ssam 	vd->vd_dcb.trailcnt = 0;
108230519Ssam 	vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys;
108330519Ssam 	vd->vd_mdcb.mdcb_status = 0;
108430519Ssam 	VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type);
108530519Ssam 	if (!vdpoll(vm, t)) {
108630519Ssam 		printf(" during init\n");
108730370Skarels 		return (0);
108830370Skarels 	}
108930519Ssam 	return ((vd->vd_dcb.operrsta&VDERR_HARD) == 0);
109025675Ssam }
109125675Ssam 
109225925Ssam /*
109330519Ssam  * Poll controller until operation
109430519Ssam  * completes or timeout expires.
109525925Ssam  */
109630519Ssam vdpoll(vm, t)
109730519Ssam 	register struct vba_ctlr *vm;
109825925Ssam 	register int t;
109925925Ssam {
110030519Ssam 	register struct vdsoftc *vd = &vdsoftc[vm->um_ctlr];
110130519Ssam 	register struct vddevice *vdaddr = (struct vddevice *)vm->um_addr;
110225925Ssam 
110325925Ssam 	t *= 1000;
110430370Skarels 	for (;;) {
110530519Ssam 		uncache(&vd->vd_dcb.operrsta);
110630519Ssam 		if (vd->vd_dcb.operrsta & (DCBS_DONE|DCBS_ABORT))
110730370Skarels 			break;
110825925Ssam 		if (--t <= 0) {
110930519Ssam 			printf("vd%d: controller timeout", vm->um_ctlr);
111030519Ssam 			VDABORT(vdaddr, vd->vd_type);
111125925Ssam 			DELAY(30000);
111225925Ssam 			return (0);
111325925Ssam 		}
111430370Skarels 		DELAY(1000);
111525925Ssam 	}
111630519Ssam 	if (vd->vd_type == VDTYPE_SMDE) {
111730519Ssam 		do {
111825925Ssam 			DELAY(50);
111930519Ssam 			uncache(&vdaddr->vdcsr);
112030519Ssam 		} while (vdaddr->vdcsr & CS_GO);
112125925Ssam 		DELAY(300);
112225925Ssam 	}
112325925Ssam 	DELAY(200);
112430519Ssam 	uncache(&vd->vd_dcb.operrsta);
112525925Ssam 	return (1);
112625925Ssam }
112725925Ssam 
112830519Ssam #ifdef COMPAT_42
112930519Ssam struct	vdst {
113030519Ssam 	int	nsec;		/* sectors/track */
113130519Ssam 	int	ntrack;		/* tracks/cylinder */
113230519Ssam 	int	ncyl;		/* cylinders */
113330519Ssam 	char	*name;		/* type name */
113430519Ssam 	struct {
113530519Ssam 		int	off;	/* partition offset in sectors */
113630519Ssam 		int	size;	/* partition size in sectors */
113730573Skarels 	} parts[8];
113830519Ssam } vdst[] = {
113930573Skarels 	{ 48, 24, 711, "xsd",
114030573Skarels 		{0,	 30528},	/* a cyl   0 - 52 */
114130573Skarels 		{30528,	 30528},	/* b cyl  53 - 105 */
114230573Skarels 		{61056,	 345600}, 	/* c cyl 106 - 705 */
114330573Skarels 		{118656, 288000}, 	/* d cyl 206 - 705 */
114430573Skarels 		{176256, 230400},	/* e cyl 306 - 705 */
114530573Skarels 		{233856, 172800}, 	/* f cyl 406 - 705 */
114630573Skarels 		{291456, 115200},	/* g cyl 506 - 705 */
114730573Skarels 		{349056, 57600}		/* h cyl 606 - 705 */
114830573Skarels 	},
114930573Skarels 	{ 44, 20, 842, "egl",
115030601Skarels 		{0,	 52800},	/* egl0a cyl   0 - 59 */
115130601Skarels 		{52800,	 66000},	/* egl0b cyl  60 - 134 */
115230601Skarels 		{118800, 617760}, 	/* egl0c cyl 135 - 836 */
1153*30756Skarels 		{736560, 4400}, 	/* egl0d cyl 837 - 841 */
115430601Skarels 		{0, 	 736560},	/* egl0e cyl 0 - 836 */
1155*30756Skarels 		{0, 	 740960}, 	/* egl0f cyl 0 - 841 */
115630601Skarels 		{118800, 310640},	/* egl0g cyl 135 - 487 */
115730601Skarels 		{429440, 307120}	/* egl0h cyl 488 - 836 */
115830573Skarels 	},
115930573Skarels 	{ 64, 10, 823, "fuj",
116030573Skarels 		{0,	 19200},	/* fuj0a cyl   0 - 59 */
116130573Skarels 		{19200,	 24000},	/* fuj0b cyl  60 - 134 */
116230573Skarels 		{43200,	 218560}, 	/* fuj0c cyl 135 - 817 */
116330573Skarels 		{79680,	 182080}, 	/* fuj0d cyl 249 - 817 */
116430573Skarels 		{116160, 145600},	/* fuj0e cyl 363 - 817 */
116530573Skarels 		{152640, 109120}, 	/* fuj0f cyl 477 - 817 */
116630573Skarels 		{189120, 72640},	/* fuj0g cyl 591 - 817 */
116730573Skarels 		{225600, 36160}		/* fug0h cyl 705 - 817 */
116830573Skarels 	},
116930573Skarels 	{ 32, 24, 711, "xfd",
1170*30756Skarels 		{ 0,	 40704 },	/* a cyl   0 - 52 */
1171*30756Skarels 		{ 40704, 40704 },	/* b cyl  53 - 105 */
1172*30756Skarels 		{ 81408, 460800 },	/* c cyl 106 - 705 */
1173*30756Skarels 		{ 0,	 81408 },	/* d cyl 709 - 710 (a & b) */
1174*30756Skarels 		{ 0,	 542208 },	/* e cyl   0 - 705 */
1175*30756Skarels 		{ 40704, 501504 },	/* f cyl  53 - 705 (b & c) */
1176*30756Skarels 		{ 81408, 230400 },	/* g cyl 106 - 405 (1/2 of c) */
1177*30756Skarels 		{ 311808,230400 }	/* h cyl 406 - 705 (1/2 of c) */
117830573Skarels 	},
117930573Skarels 	{ 32, 19, 823, "smd",
118030573Skarels 		{0,	 20064},	/* a cyl   0-65 */
118130573Skarels 		{20064, 13680},		/* b cyl  66-110 */
118230573Skarels 		{33744, 214928},	/* c cyl 111-817 */
118330573Skarels 		{69616,	 179056},	/* d cyl 229 - 817 */
118430573Skarels 		{105488, 143184},	/* e cyl 347 - 817 */
118530573Skarels 		{141360, 107312},	/* f cyl 465 - 817 */
118630573Skarels 		{177232, 71440},	/* g cyl 583 - 817 */
118730573Skarels 		{213104, 35568}		/* h cyl 701 - 817 */
118830573Skarels 	},
118930573Skarels 	{ 32, 10, 823, "fsd",
1190*30756Skarels 		{0,	 19200},	/* a cyl   0 -  59 */
1191*30756Skarels 		{19200,	 24000},	/* b cyl  60 - 134 */
1192*30756Skarels 		{43200,	 218560},	/* c cyl 135 - 817 */
119330573Skarels 	}
119430519Ssam };
119530519Ssam #define	NVDST	(sizeof (vdst) / sizeof (vdst[0]))
119630519Ssam 
119725675Ssam /*
119830519Ssam  * Construct a label for an unlabeled pack.  We
119930519Ssam  * deduce the drive type by reading from the last
120030519Ssam  * track on successively smaller drives until we
120130519Ssam  * don't get an error.
120225675Ssam  */
120330519Ssam vdmaptype(vi, lp)
120430519Ssam 	register struct vba_device *vi;
120530519Ssam 	register struct disklabel *lp;
120625675Ssam {
120730519Ssam 	register struct vdsoftc *vd;
120830519Ssam 	register struct vdst *p;
120930519Ssam 	struct vba_ctlr *vm = vdminfo[vi->ui_ctlr];
121030519Ssam 	int i;
121125675Ssam 
121230519Ssam 	vd = &vdsoftc[vi->ui_ctlr];
121330519Ssam 	for (p = vdst; p < &vdst[NVDST]; p++) {
121430519Ssam 		if (vd->vd_type == VDTYPE_VDDC && p->nsec != 32)
121530519Ssam 			continue;
121630519Ssam 		lp->d_nsectors = p->nsec;
121730519Ssam 		lp->d_ntracks = p->ntrack;
121830519Ssam 		lp->d_ncylinders = p->ncyl;
121930519Ssam 		if (!vdreset_drive(vi))
122030519Ssam 			return (0);
122130519Ssam 		vd->vd_dcb.opcode = VDOP_RD;
122230519Ssam 		vd->vd_dcb.intflg = DCBINT_NONE;
122330519Ssam 		vd->vd_dcb.nxtdcb = (struct dcb *)0;	/* end of chain */
122430519Ssam 		vd->vd_dcb.devselect = vi->ui_slave;
1225*30756Skarels 		vd->vd_dcb.trailcnt = sizeof (struct trrw) / sizeof (long);
122630601Skarels 		vd->vd_dcb.trail.rwtrail.memadr =
122730601Skarels 		    vtoph((struct proc *)0, (unsigned)vd->vd_rbuf.vb_rawbuf);
122830519Ssam 		vd->vd_dcb.trail.rwtrail.wcount = 512 / sizeof(short);
122930519Ssam 		vd->vd_dcb.operrsta = 0;
123030519Ssam 		vd->vd_dcb.trail.rwtrail.disk.cylinder = p->ncyl - 2;
123130519Ssam 		vd->vd_dcb.trail.rwtrail.disk.track = p->ntrack - 1;
123230519Ssam 		vd->vd_dcb.trail.rwtrail.disk.sector = p->nsec - 1;
123330519Ssam 		vd->vd_mdcb.mdcb_head = (struct dcb *)vd->vd_dcbphys;
123430519Ssam 		vd->vd_mdcb.mdcb_status = 0;
123530519Ssam 		VDGO(vm->um_addr, vd->vd_mdcbphys, vd->vd_type);
123630519Ssam 		if (!vdpoll(vm, 60))
123730519Ssam 			printf(" during probe\n");
123830519Ssam 		if ((vd->vd_dcb.operrsta & VDERR_HARD) == 0)
123930519Ssam 			break;
124024004Ssam 	}
124130519Ssam 	if (p >= &vdst[NVDST]) {
124230519Ssam 		printf("dk%d: unknown drive type\n", vi->ui_unit);
124330519Ssam 		return (0);
124430519Ssam 	}
124530573Skarels 	for (i = 0; i < 8; i++) {
124630519Ssam 		lp->d_partitions[i].p_offset = p->parts[i].off;
124730519Ssam 		lp->d_partitions[i].p_size = p->parts[i].size;
124830519Ssam 	}
124930573Skarels 	lp->d_npartitions = 8;
125030519Ssam 	lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
125130519Ssam 	lp->d_rpm = 3600;
125230519Ssam 	lp->d_secsize = 512;
125330519Ssam 	bcopy(p->name, lp->d_typename, 4);
125430519Ssam 	return (1);
125524004Ssam }
125630519Ssam #endif COMPAT_42
125724004Ssam #endif
1258