bzero(xyc->reqs, XYC_MAXIOPB * sizeof(struct xy_iorq));
struct xy_iopb *xyc_chain __P((struct xyc_softc *, struct xy_iorq *));int	xyc_cmd __P((struct xyc_softc *, int, int, int, int, int, char *, int));char   *xyc_e2str __P((int));int	xyc_entoact __P((int));int	xyc_error __P((struct xyc_softc *, struct xy_iorq *,		   struct xy_iopb *, int));int	xyc_ioctlcmd __P((struct xy_softc *, dev_t dev, struct xd_iocmd *));void	xyc_perror __P((struct xy_iorq *, struct xy_iopb *, int));int	xyc_piodriver __P((struct xyc_softc *, struct xy_iorq *));int	xyc_remove_iorq __P((struct xyc_softc *));int	xyc_reset __P((struct xyc_softc *, int, struct xy_iorq *, int, 			struct xy_softc *));inline void xyc_rqinit __P((struct xy_iorq *, struct xyc_softc *,			    struct xy_softc *, int, u_long, int,			    caddr_t, struct buf *));void	xyc_rqtopb __P((struct xy_iorq *, struct xy_iopb *, int, int));int	xyc_start __P((struct xyc_softc *, struct xy_iorq *));int	xyc_startbuf __P((struct xyc_softc *, struct xy_softc *, struct buf *));int	xyc_submit_iorq __P((struct xyc_softc *, struct xy_iorq *, int));void	xyc_tick __P((void *));int	xyc_unbusy __P((struct xyc *, int));int	xyc_xyreset __P((struct xyc_softc *, struct xy_softc *));/* machine interrupt hook */int	xycintr __P((void *));/* {b,c}devsw */int	xyclose __P((dev_t, int, int));int	xydump __P((dev_t));int	xyioctl __P((dev_t, u_long, caddr_t, int, struct proc *));int	xyopen __P((dev_t, int, int));int	xyread __P((dev_t, struct uio *));int	xywrite __P((dev_t, struct uio *));int	xysize __P((dev_t));void	xystrategy __P((struct buf *));/* autoconf */int	xycmatch __P((struct device *, void *, void *));void	xycattach __P((struct device *, struct device *, void *));int	xymatch __P((struct device *, void *, void *));void	xyattach __P((struct device *, struct device *, void *));static	void xydummystrat __P((struct buf *));int	xygetdisklabel __P((struct xy_softc *, void *));/* * cfdrivers: device driver interface to autoconfig */struct cfdriver xyccd = {	NULL, "xyc", xycmatch, xycattach, DV_DULL, sizeof(struct xyc_softc)};struct cfdriver xycd = {	NULL, "xy", xymatch, xyattach, DV_DISK, sizeof(struct xy_softc)};struct xyc_attach_args {	/* this is the "aux" args to xyattach */	int	driveno;	/* unit number */	char	*dvmabuf;	/* scratch buffer for reading disk label */	int	fullmode;	/* submit mode */	int	booting;	/* are we booting or not? */};/* * dkdriver */struct dkdriver xydkdriver = { xystrategy };/* * start: disk label fix code (XXX) */static void *xy_labeldata;static voidxydummystrat(bp)	struct buf *bp;{	if (bp->b_bcount != XYFM_BPS)		panic("xydummystrat");	bcopy(xy_labeldata, bp->b_un.b_addr, XYFM_BPS);	bp->b_flags |= B_DONE;	bp->b_flags &= ~B_BUSY;}intxygetdisklabel(xy, b)	struct xy_softc *xy;	void *b;{	char *err;	struct sun_disklabel *sdl;	/* We already have the label data in `b'; setup for dummy strategy */	xy_labeldata = b;	/* Required parameter for readdisklabel() */	xy->sc_dk.dk_label->d_secsize = XYFM_BPS;	err = readdisklabel(MAKEDISKDEV(0, xy->sc_dev.dv_unit, RAW_PART),					xydummystrat,				xy->sc_dk.dk_label, xy->sc_dk.dk_cpulabel);	if (err) {		printf("%s: %s\n", xy->sc_dev.dv_xname, err);		return(XY_ERR_FAIL);	}	/* Ok, we have the label; fill in `pcyl' if there's SunOS magic */	sdl = (struct sun_disklabel *)xy->sc_dk.dk_cpulabel->cd_block;	if (sdl->sl_magic == SUN_DKMAGIC)		xy->pcyl = sdl->sl_pcyl;	else {		printf("%s: WARNING: no `pcyl' in disk label.\n", 							xy->sc_dev.dv_xname);		xy->pcyl = xy->sc_dk.dk_label->d_ncylinders +			xy->sc_dk.dk_label->d_acylinders;		printf("%s: WARNING: guessing pcyl=%d (ncyl+acyl)\n", 		xy->sc_dev.dv_xname, xy->pcyl);	}	xy->ncyl = xy->sc_dk.dk_label->d_ncylinders;	xy->acyl = xy->sc_dk.dk_label->d_acylinders;	xy->nhead = xy->sc_dk.dk_label->d_ntracks;	xy->nsect = xy->sc_dk.dk_label->d_nsectors;	xy->sectpercyl = xy->nhead * xy->nsect;	xy->sc_dk.dk_label->d_secsize = XYFM_BPS; /* not handled by                                          	  * sun->bsd */	return(XY_ERR_AOK);}/* * end: disk label fix code (XXX) *//* * a u t o c o n f i g   f u n c t i o n s *//* * xycmatch: determine if xyc is present or not.   we do a * soft reset to detect the xyc. */int xycmatch(parent, match, aux)	struct device *parent;	void   *match, *aux;{	struct cfdata *cf = match;	struct confargs *ca = aux;	int x;	if (ca->ca_bustype != BUS_VME16)		return (0);	/* Default interrupt priority always splbio==2 */	if (ca->ca_intpri == -1)		ca->ca_intpri = 2;	x = bus_peek(ca->ca_bustype, ca->ca_paddr + 5, 1);	if (x == -1)		return (0);	return (1);}/* * xycattach: attach controller */void xycattach(parent, self, aux)	struct device *parent, *self;	void   *aux;{	struct xyc_softc *xyc = (void *) self;	struct confargs *ca = aux;	struct xyc_attach_args xa;	int     lcv, err, pri, res, pbsz;	void	*tmp, *tmp2;	u_long	ultmp;	/* get addressing and intr level stuff from autoconfig and load it	 * into our xyc_softc. */	xyc->xyc = (struct xyc *)		bus_mapin(ca->ca_bustype, ca->ca_paddr, sizeof(struct xyc));	xyc->ipl = ca->ca_intpri;	xyc->vector = ca->ca_intvec;	xyc->no_ols = 0; /* XXX should be from config */	for (lcv = 0; lcv < XYC_MAXDEV; lcv++)		xyc->sc_drives[lcv] = (struct xy_softc *) 0;	/* 	 * allocate and zero buffers	 * check boundaries of the KVA's ... all IOPBs must reside in  	 * the same 64K region. 	 */	pbsz = XYC_MAXIOPB * sizeof(struct xy_iopb);	tmp = tmp2 = (struct xy_iopb *) dvma_malloc(pbsz);	/* KVA */	ultmp = (u_long) tmp;	if ((ultmp & 0xffff0000) != ((ultmp + pbsz) & 0xffff0000)) {		tmp = (struct xy_iopb *) dvma_malloc(pbsz); /* retry! */		dvma_free(tmp2, pbsz);		ultmp = (u_long) tmp;		if ((ultmp & 0xffff0000) != ((ultmp + pbsz) & 0xffff0000)) {			printf("%s: can't alloc IOPB mem in 64K\n", 				xyc->sc_dev.dv_xname);			return;		}	}	xyc->iopbase = tmp;	bzero(xyc->iopbase, pbsz);	xyc->dvmaiopb = (struct xy_iopb *)		dvma_kvtopa((long) xyc->iopbase, BUS_VME16);	xyc->reqs = (struct xy_iorq *)	    malloc(XYC_MAXIOPB * sizeof(struct xy_iorq), M_DEVBUF, M_NOWAIT);	bzero(xyc->reqs, XYC_MAXIOPB * sizeof(struct xy_iorq));	if (xyc->reqs == NULL)		panic("xyc malloc");	/* 	 * init iorq to iopb pointers, and non-zero fields in the	 * iopb which never change. 	 */	for (lcv = 0; lcv < XYC_MAXIOPB; lcv++) {		xyc->xy_chain[lcv] = NULL;		xyc->reqs[lcv].iopb = &xyc->iopbase[lcv];		xyc->iopbase[lcv].asr = 1;	/* always the same */		xyc->iopbase[lcv].eef = 1;	/* always the same */		xyc->iopbase[lcv].ecm = XY_ECM;	/* always the same */		xyc->iopbase[lcv].aud = 1;	/* always the same */		xyc->iopbase[lcv].relo = 1;	/* always the same */		xyc->iopbase[lcv].thro = XY_THRO;/* always the same */	}	xyc->ciorq = &xyc->reqs[XYC_CTLIOPB];    /* short hand name */	xyc->ciopb = &xyc->iopbase[XYC_CTLIOPB]; /* short hand name */	xyc->xy_hand = 0;	/* read controller parameters and insure we have a 450/451 */	err = xyc_cmd(xyc, XYCMD_ST, 0, 0, 0, 0, 0, XY_SUB_POLL);	res = xyc->ciopb->ctyp;	XYC_DONE(xyc, err);	if (res != XYCT_450) {		if (err)			printf(": %s: ", xyc_e2str(err));		printf(": doesn't identify as a 450/451\n");		return;	}	printf(": Xylogics 450/451");	if (xyc->no_ols)		printf(" [OLS disabled]"); /* 450 doesn't overlap seek right */	printf("\n");	if (err) {		printf("%s: error: %s\n", xyc->sc_dev.dv_xname,				xyc_e2str(err));		return;	}	if ((xyc->xyc->xyc_csr & XYC_ADRM) == 0) {		printf("%s: 24 bit addressing turned off\n", 						xyc->sc_dev.dv_xname);		printf("please set hardware jumpers JM1-JM2=in, JM3-JM4=out\n");		printf("to enable 24 bit mode and this driver\n");		return;	}	/* link in interrupt with higher level software */	isr_add_vectored(xycintr, (void *)xyc,	                 ca->ca_intpri, ca->ca_intvec);	evcnt_attach(&xyc->sc_dev, "intr", &xyc->sc_intrcnt);	/* now we must look for disks using autoconfig */	xa.dvmabuf = (char *) dvma_malloc(XYFM_BPS);	xa.fullmode = XY_SUB_POLL;	xa.booting = 1;	for (xa.driveno = 0; xa.driveno < XYC_MAXDEV; xa.driveno++)		(void) config_found(self, (void *) &xa, NULL);	dvma_free(xa.dvmabuf, XYFM_BPS);	/* start the watchdog clock */	timeout(xyc_tick, xyc, XYC_TICKCNT);}/* * xymatch: probe for disk. * * note: we almost always say disk is present.   this allows us to * spin up and configure a disk after the system is booted (we can * call xyattach!). */int xymatch(parent, match, aux)	struct device *parent;	void   *match, *aux;{	struct xyc_softc *xyc = (void *) parent;	struct cfdata *cf = match;	struct xyc_attach_args *xa = aux;	/* looking for autoconf wildcard or exact match */	if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != xa->driveno)		return 0;	return 1;}/* * xyattach: attach a disk.   this can be called from autoconf and also * from xyopen/xystrategy. */void xyattach(parent, self, aux)	struct device *parent, *self;	void   *aux;{	struct xy_softc *xy = (void *) self, *oxy;	struct xyc_softc *xyc = (void *) parent;	struct xyc_attach_args *xa = aux;	int     res, err, spt, mb, blk, lcv, fmode, s, newstate;	struct dkbad *dkb;	struct bootpath *bp;	/*	 * Always re-initialize the disk structure.  We want statistics	 * to start with a clean slate.	 */	bzero(&xy->sc_dk, sizeof(xy->sc_dk));	xy->sc_dk.dk_driver = &xydkdriver;	xy->sc_dk.dk_name = xy->sc_dev.dv_xname;	/* if booting, init the xy_softc */	if (xa->booting) {		xy->state = XY_DRIVE_UNKNOWN;	/* to start */		xy->flags = 0;		xy->parent = xyc;		/* init queue of waiting bufs */		xy->xyq.b_active = 0;		xy->xyq.b_actf = 0;		xy->xyq.b_actb = &xy->xyq.b_actf; /* XXX b_actb: not used? */		xy->xyrq = &xyc->reqs[xa->driveno];	}	xy->xy_drive = xa->driveno;	fmode = xa->fullmode;	xyc->sc_drives[xa->driveno] = xy;	/* if not booting, make sure we are the only process in the attach for	 * this drive.   if locked out, sleep on it. */	if (!xa->booting) {		s = splbio();		while (xy->state == XY_DRIVE_ATTACHING) {			if (tsleep(&xy->state, PRIBIO, "xyattach", 0)) {				splx(s);				return;			}		}		printf("%s at %s",			xy->sc_dev.dv_xname, xy->parent->sc_dev.dv_xname);	}	/* we now have control */	xy->state = XY_DRIVE_ATTACHING;	newstate = XY_DRIVE_UNKNOWN;	/* first try and reset the drive */	err = xyc_cmd(xyc, XYCMD_RST, 0, xy->xy_drive, 0, 0, 0, fmode);	XYC_DONE(xyc, err);	if (err == XY_ERR_DNRY) {		printf(" drive %d: off-line\n", xa->driveno);		goto done;	}	if (err) {		printf(": ERROR 0x%02x (%s)\n", err, xyc_e2str(err));		goto done;	}	printf(" drive %d: ready", xa->driveno);	/*	 * now set drive parameters (to semi-bogus values) so we can read the	 * disk label.	 */	xy->pcyl = xy->ncyl = 1;	xy->acyl = 0;	xy->nhead = 1;	xy->nsect = 1;	xy->sectpercyl = 1;	for (lcv = 0; lcv < 126; lcv++)	/* init empty bad144 table */		xy->dkb.bt_bad[lcv].bt_cyl = 			xy->dkb.bt_bad[lcv].bt_trksec = 0xffff;	/* read disk label */	for (xy->drive_type = 0 ; xy->drive_type <= XYC_MAXDT ; 						xy->drive_type++) {		err = xyc_cmd(xyc, XYCMD_RD, 0, xy->xy_drive, 0, 1, 						xa->dvmabuf, fmode);		XYC_DONE(xyc, err);		if (err == XY_ERR_AOK) break;	}	if (err != XY_ERR_AOK) {		printf("\n%s: reading disk label failed: %s\n",			xy->sc_dev.dv_xname, xyc_e2str(err));		goto done;	}	printf(" (drive type %d)\n", xy->drive_type);	newstate = XY_DRIVE_NOLABEL;	xy->hw_spt = spt = 0; /* XXX needed ? */	/* Attach the disk: must be before getdisklabel to malloc label */	disk_attach(&xy->sc_dk);	if (xygetdisklabel(xy, xa->dvmabuf) != XY_ERR_AOK)		goto done;	/* inform the user of what is up */	printf("%s: <%s>, pcyl %d\n", xy->sc_dev.dv_xname,		xa->dvmabuf, xy->pcyl);	mb = xy->ncyl * (xy->nhead * xy->nsect) / (1048576 / XYFM_BPS);	printf("%s: %dMB, %d cyl, %d head, %d sec, %d bytes/sec\n",		xy->sc_dev.dv_xname, mb, xy->ncyl, xy->nhead, xy->nsect,		XYFM_BPS);	/*	 * 450/451 stupidity: the drive type is encoded into the format	 * of the disk.   the drive type in the IOPB must match the drive	 * type in the format, or you will not be able to do I/O to the	 * disk (you get header not found errors).  if you have two drives 	 * of different sizes that have the same drive type in their 	 * formatting then you are out of luck.    	 *	 * this problem was corrected in the 753/7053.	 */	for (lcv = 0 ; lcv < XYC_MAXDEV ; lcv++) {		oxy = xyc->sc_drives[lcv];		if (oxy == NULL || oxy == xy) continue;		if (oxy->drive_type != xy->drive_type) continue;		if (xy->nsect != oxy->nsect || xy->pcyl != oxy->pcyl ||			xy->nhead != oxy->nhead) {			printf("%s: %s and %s must be the same size!\n",				xyc->sc_dev.dv_xname, xy->sc_dev.dv_xname,				oxy->sc_dev.dv_xname);			panic("xy drive size mismatch");		}	}			/* now set the real drive parameters! */	blk = (xy->nsect - 1) +		((xy->nhead - 1) * xy->nsect) +		((xy->pcyl - 1) * xy->nsect * xy->nhead);	err = xyc_cmd(xyc, XYCMD_SDS, 0, xy->xy_drive, blk, 0, 0, fmode);	XYC_DONE(xyc, err);	if (err) {		printf("%s: write drive size failed: %s\n",			xy->sc_dev.dv_xname, xyc_e2str(err));		goto done;	}	newstate = XY_DRIVE_ONLINE;	/*	 * read bad144 table. this table resides on the first sector of the	 * last track of the disk (i.e. second cyl of "acyl" area).	 */	blk = (xy->ncyl + xy->acyl - 1) * (xy->nhead * xy->nsect) + 								/* last cyl */	    (xy->nhead - 1) * xy->nsect;	/* last head */	err = xyc_cmd(xyc, XYCMD_RD, 0, xy->xy_drive, blk, 1, 						xa->dvmabuf, fmode);	XYC_DONE(xyc, err);	if (err) {		printf("%s: reading bad144 failed: %s\n",			xy->sc_dev.dv_xname, xyc_e2str(err));		goto done;	}	/* check dkbad for sanity */	dkb = (struct dkbad *) xa->dvmabuf;	for (lcv = 0; lcv < 126; lcv++) {		if ((dkb->bt_bad[lcv].bt_cyl == 0xffff ||				dkb->bt_bad[lcv].bt_cyl == 0) &&		     dkb->bt_bad[lcv].bt_trksec == 0xffff)			continue;	/* blank */		if (dkb->bt_bad[lcv].bt_cyl >= xy->ncyl)			break;		if ((dkb->bt_bad[lcv].bt_trksec >> 8) >= xy->nhead)			break;		if ((dkb->bt_bad[lcv].bt_trksec & 0xff) >= xy->nsect)			break;	}	if (lcv != 126) {		printf("%s: warning: invalid bad144 sector!\n",			xy->sc_dev.dv_xname);	} else {		bcopy(xa->dvmabuf, &xy->dkb, XYFM_BPS);	}	dk_establish(&xy->sc_dk, &xy->sc_dev);		/* XXX */done:	xy->state = newstate;	if (!xa->booting) {		wakeup(&xy->state);		splx(s);	}}/* * end of autoconfig functions *//* * { b , c } d e v s w   f u n c t i o n s *//* * xyclose: close device */int xyclose(dev, flag, fmt)	dev_t   dev;	int     flag, fmt;{	struct xy_softc *xy = xycd.cd_devs[DISKUNIT(dev)];	int     part = DISKPART(dev);	/* clear mask bits */	switch (fmt) {	case S_IFCHR:		xy->sc_dk.dk_copenmask &= ~(1 << part);		break;	case S_IFBLK:		xy->sc_dk.dk_bopenmask &= ~(1 << part);		break;	}	xy->sc_dk.dk_openmask = xy->sc_dk.dk_copenmask | xy->sc_dk.dk_bopenmask;	return 0;}/* * xydump: crash dump system */int xydump(dev)	dev_t   dev;{	int     unit, part;	struct xy_softc *xy;	unit = DISKUNIT(dev);	if (unit >= xycd.cd_ndevs)		return ENXIO;	part = DISKPART(dev);	xy = xycd.cd_devs[unit];	printf("%s%c: crash dump not supported (yet)\n", xy->sc_dev.dv_xname,	    'a' + part);	return ENXIO;	/* outline: globals: "dumplo" == sector number of partition to start	 * dump at (convert to physical sector with partition table)	 * "dumpsize" == size of dump in clicks "physmem" == size of physical	 * memory (clicks, ctob() to get bytes) (normal case: dumpsize ==	 * physmem)	 * 	 * dump a copy of physical memory to the dump device starting at sector	 * "dumplo" in the swap partition (make sure > 0).   map in pages as	 * we go.   use polled I/O.	 * 	 * XXX how to handle NON_CONTIG? */}/* * xyioctl: ioctls on XY drives.   based on ioctl's of other netbsd disks. */int xyioctl(dev, command, addr, flag, p)	dev_t   dev;	u_long  command;	caddr_t addr;	int     flag;	struct proc *p;{	struct xy_softc *xy;	struct xd_iocmd *xio;	int     error, s, unit;	unit = DISKUNIT(dev);	if (unit >= xycd.cd_ndevs || (xy = xycd.cd_devs[unit]) == NULL)		return (ENXIO);	/* switch on ioctl type */	switch (command) {	case DIOCSBAD:		/* set bad144 info */		if ((flag & FWRITE) == 0)			return EBADF;		s = splbio();		bcopy(addr, &xy->dkb, sizeof(xy->dkb));		splx(s);		return 0;	case DIOCGDINFO:	/* get disk label */		bcopy(xy->sc_dk.dk_label, addr, sizeof(struct disklabel));		return 0;	case DIOCGPART:	/* get partition info */		((struct partinfo *) addr)->disklab = xy->sc_dk.dk_label;		((struct partinfo *) addr)->part =		    &xy->sc_dk.dk_label->d_partitions[DISKPART(dev)];		return 0;	case DIOCSDINFO:	/* set disk label */		if ((flag & FWRITE) == 0)			return EBADF;		error = setdisklabel(xy->sc_dk.dk_label,		    (struct disklabel *) addr, /* xy->sc_dk.dk_openmask : */ 0,		    xy->sc_dk.dk_cpulabel);		if (error == 0) {			if (xy->state == XY_DRIVE_NOLABEL)				xy->state = XY_DRIVE_ONLINE;		}		return error;	case DIOCWLABEL:	/* change write status of disk label */		if ((flag & FWRITE) == 0)			return EBADF;		if (*(int *) addr)			xy->flags |= XY_WLABEL;		else			xy->flags &= ~XY_WLABEL;		return 0;	case DIOCWDINFO:	/* write disk label */		if ((flag & FWRITE) == 0)			return EBADF;		error = setdisklabel(xy->sc_dk.dk_label,		    (struct disklabel *) addr, /* xy->sc_dk.dk_openmask : */ 0,		    xy->sc_dk.dk_cpulabel);		if (error == 0) {			if (xy->state == XY_DRIVE_NOLABEL)				xy->state = XY_DRIVE_ONLINE;			/* Simulate opening partition 0 so write succeeds. */			xy->sc_dk.dk_openmask |= (1 << 0);			error = writedisklabel(MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART),			    xystrategy, xy->sc_dk.dk_label,			    xy->sc_dk.dk_cpulabel);			xy->sc_dk.dk_openmask =			    xy->sc_dk.dk_copenmask | xy->sc_dk.dk_bopenmask;		}		return error;	case DIOSXDCMD:		xio = (struct xd_iocmd *) addr;		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)			return (error);		return (xyc_ioctlcmd(xy, dev, xio));	default:		return ENOTTY;	}}/* * xyopen: open drive */int xyopen(dev, flag, fmt)	dev_t   dev;	int     flag, fmt;{	int     unit, part;	struct xy_softc *xy;	struct xyc_attach_args xa;	/* first, could it be a valid target? */	unit = DISKUNIT(dev);	if (unit >= xycd.cd_ndevs || (xy = xycd.cd_devs[unit]) == NULL)		return (ENXIO);	part = DISKPART(dev);	/* do we need to attach the drive? */	if (xy->state == XY_DRIVE_UNKNOWN) {		xa.driveno = xy->xy_drive;		xa.dvmabuf = (char *) dvma_malloc(XYFM_BPS);		xa.fullmode = XY_SUB_WAIT;		xa.booting = 0;		xyattach((struct device *) xy->parent, 						(struct device *) xy, &xa);		dvma_free(xa.dvmabuf, XYFM_BPS);		if (xy->state == XY_DRIVE_UNKNOWN) {			return (EIO);		}	}	/* check for partition */	if (part != RAW_PART &&	    (part >= xy->sc_dk.dk_label->d_npartitions ||		xy->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {		return (ENXIO);	}	/* set open masks */	switch (fmt) {	case S_IFCHR:		xy->sc_dk.dk_copenmask |= (1 << part);		break;	case S_IFBLK:		xy->sc_dk.dk_bopenmask |= (1 << part);		break;	}	xy->sc_dk.dk_openmask = xy->sc_dk.dk_copenmask | xy->sc_dk.dk_bopenmask;	return 0;}intxyread(dev, uio)	dev_t   dev;	struct uio *uio;{	return (physio(xystrategy, NULL, dev, B_READ, minphys, uio));}intxywrite(dev, uio)	dev_t   dev;	struct uio *uio;{	return (physio(xystrategy, NULL, dev, B_WRITE, minphys, uio));}/* * xysize: return size of a partition for a dump */int xysize(dev)	dev_t   dev;{	struct xy_softc *xysc;	int     unit, part, size;	/* valid unit?  try an open */	if (xyopen(dev, 0, S_IFBLK) != 0)		return (-1);	/* do it */	xysc = xycd.cd_devs[DISKUNIT(dev)];	part = DISKPART(dev);	if (xysc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)		size = -1;	/* only give valid size for swap partitions */	else		size = xysc->sc_dk.dk_label->d_partitions[part].p_size;	if (xyclose(dev, 0, S_IFBLK) != 0)		return -1;	return size;}/* * xystrategy: buffering system interface to xy. */void xystrategy(bp)	struct buf *bp;{	struct xy_softc *xy;	struct xyc_softc *parent;	struct buf *wq;	int     s, unit;	struct xyc_attach_args xa;	unit = DISKUNIT(bp->b_dev);	/* check for live device */	if (unit >= xycd.cd_ndevs || (xy = xycd.cd_devs[unit]) == 0 ||	    bp->b_blkno < 0 ||	    (bp->b_bcount % xy->sc_dk.dk_label->d_secsize) != 0) {		bp->b_error = EINVAL;		goto bad;	}	/* do we need to attach the drive? */	if (xy->state == XY_DRIVE_UNKNOWN) {		xa.driveno = xy->xy_drive;		xa.dvmabuf = (char *) dvma_malloc(XYFM_BPS);		xa.fullmode = XY_SUB_WAIT;		xa.booting = 0;		xyattach((struct device *)xy->parent, (struct device *)xy, &xa);		dvma_free(xa.dvmabuf, XYFM_BPS);		if (xy->state == XY_DRIVE_UNKNOWN) {			bp->b_error = EIO;			goto bad;		}	}	if (xy->state != XY_DRIVE_ONLINE && DISKPART(bp->b_dev) != RAW_PART) {		/* no I/O to unlabeled disks, unless raw partition */		bp->b_error = EIO;		goto bad;	}	/* short circuit zero length request */	if (bp->b_bcount == 0)		goto done;	/* check bounds with label (disksubr.c).  Determine the size of the	 * transfer, and make sure it is within the boundaries of the	 * partition. Adjust transfer if needed, and signal errors or early	 * completion. */	if (bounds_check_with_label(bp, xy->sc_dk.dk_label,		(xy->flags & XY_WLABEL) != 0) <= 0)		goto done;	/*	 * now we know we have a valid buf structure that we need to do I/O	 * on.	 */	s = splbio();		/* protect the queues */	disksort(&xy->xyq, bp);	/* start 'em up */	xyc_start(xy->parent, NULL);	/* done! */	splx(s);	return;bad:				/* tells upper layers we have an error */	bp->b_flags |= B_ERROR;done:				/* tells upper layers we are done with this				 * buf */	bp->b_resid = bp->b_bcount;	biodone(bp);}/* * end of {b,c}devsw functions *//* * i n t e r r u p t   f u n c t i o n * * xycintr: hardware interrupt. */int xycintr(v)	void   *v;{	struct xyc_softc *xycsc = v;	struct xy_softc *xy;	struct buf *bp;	/* kick the event counter */	xycsc->sc_intrcnt.ev_count++;	/* remove as many done IOPBs as possible */	xyc_remove_iorq(xycsc);	/* start any iorq's already waiting */	xyc_start(xycsc, NULL);	return (1);}/* * end of interrupt function *//* * i n t e r n a l   f u n c t i o n s *//* * xyc_rqinit: fill out the fields of an I/O request */inline void xyc_rqinit(rq, xyc, xy, md, blk, cnt, db, bp)	struct xy_iorq *rq;	struct xyc_softc *xyc;	struct xy_softc *xy;	int     md;	u_long  blk;	int     cnt;	caddr_t db;	struct buf *bp;{	rq->xyc = xyc;	rq->xy = xy;	rq->ttl = XYC_MAXTTL + 10;	rq->mode = md;	rq->tries = rq->errno = rq->lasterror = 0;	rq->blockno = blk;	rq->sectcnt = cnt;	rq->dbuf = rq->dbufbase = db;	rq->buf = bp;}/* * xyc_rqtopb: load up an IOPB based on an iorq */void xyc_rqtopb(iorq, iopb, cmd, subfun)	struct xy_iorq *iorq;	struct xy_iopb *iopb;	int     cmd, subfun;{	u_long  block, dp;	/* normal IOPB case, standard stuff */	/* chain bit handled later */	iopb->ien = (XY_STATE(iorq->mode) == XY_SUB_POLL) ? 0 : 1;	iopb->com = cmd;	iopb->errno = 0;	iopb->errs = 0;	iopb->done = 0;	if (iorq->xy) {		iopb->unit = iorq->xy->xy_drive;		iopb->dt = iorq->xy->drive_type;	} else {		iopb->unit = 0;		iopb->dt = 0;	}	block = iorq->blockno;	if (iorq->xy == NULL || block == 0) {		iopb->sect = iopb->head = iopb->cyl = 0;	} else {		iopb->sect = block % iorq->xy->nsect;		block = block / iorq->xy->nsect;		iopb->head = block % iorq->xy->nhead;		block = block / iorq->xy->nhead;		iopb->cyl = block;	}	iopb->scnt = iorq->sectcnt;	dp = dvma_kvtopa((long)iorq->dbuf, BUS_VME16);	if (iorq->dbuf == NULL) {		iopb->dataa = 0;		iopb->datar = 0;	} else {		iopb->dataa = (dp & 0xffff);		iopb->datar = ((dp & 0xff0000) >> 16);	}	iopb->subfn = subfun;}/* * xyc_unbusy: wait for the xyc to go unbusy, or timeout. */intxyc_unbusy(xyc, del)struct xyc *xyc;int del;{	while (del-- > 0) {		if ((xyc->xyc_csr & XYC_GBSY) == 0)			break;		DELAY(1);	}	return(del == 0 ? XY_ERR_FAIL : XY_ERR_AOK);}/* * xyc_cmd: front end for POLL'd and WAIT'd commands.  Returns 0 or error. * note that NORM requests are handled seperately. */int xyc_cmd(xycsc, cmd, subfn, unit, block, scnt, dptr, fullmode)	struct xyc_softc *xycsc;	int     cmd, subfn, unit, block, scnt;	char   *dptr;	int     fullmode;{	int     submode = XY_STATE(fullmode), retry;	u_long  dp;	struct xy_iorq *iorq = xycsc->ciorq;	struct xy_iopb *iopb = xycsc->ciopb;	/*	 * is someone else using the control iopq wait for it if we can	 */start:	if (submode == XY_SUB_WAIT && XY_STATE(iorq->mode) != XY_SUB_FREE) {		if (tsleep(iorq, PRIBIO, "xyc_cmd", 0))                                 return(XY_ERR_FAIL);		goto start;	}	if (XY_STATE(iorq->mode) != XY_SUB_FREE) {		DELAY(1000000);		/* XY_SUB_POLL: steal the iorq */		iorq->mode = XY_SUB_FREE;		printf("%s: stole control iopb\n", xycsc->sc_dev.dv_xname);	}	/* init iorq/iopb */	xyc_rqinit(iorq, xycsc,	    (unit == XYC_NOUNIT) ? NULL : xycsc->sc_drives[unit],	    fullmode, block, scnt, dptr, NULL);	/* load IOPB from iorq */	xyc_rqtopb(iorq, iopb, cmd, subfn);	/* submit it for processing */	xyc_submit_iorq(xycsc, iorq, fullmode);	/* error code will be in iorq */	return(XY_ERR_AOK);}/* * xyc_startbuf * start a buffer for running */int xyc_startbuf(xycsc, xysc, bp)	struct xyc_softc *xycsc;	struct xy_softc *xysc;	struct buf *bp;{	int     partno;	struct xy_iorq *iorq;	struct xy_iopb *iopb;	u_long  block, dp;	caddr_t dbuf;	iorq = xysc->xyrq;	iopb = iorq->iopb;	/* get buf */	if (bp == NULL) 		panic("xyc_startbuf null buf");	partno = DISKPART(bp->b_dev);#ifdef XYC_DEBUG	printf("xyc_startbuf: %s%c: %s block %d\n", xysc->sc_dev.dv_xname,	    'a' + partno, (bp->b_flags & B_READ) ? "read" : "write", bp->b_blkno);	printf("xyc_startbuf: b_bcount %d, b_data 0x%x\n",	    bp->b_bcount, bp->b_data);#endif	/*	 * load request.  we have to calculate the correct block number based	 * on partition info.	 * 	 * also, note that there are two kinds of buf structures, those with	 * B_PHYS set and those without B_PHYS.   if B_PHYS is set, then it is	 * a raw I/O (to a cdevsw) and we are doing I/O directly to the users'	 * buffer which has already been mapped into DVMA space. (Not on sun3)	 * However, if B_PHYS is not set, then the buffer is a normal system	 * buffer which does *not* live in DVMA space.  In that case we call	 * dvma_mapin to map it into DVMA space so we can do the DMA to it.	 * 	 * in cases where we do a dvma_mapin, note that iorq points to the buffer	 * as mapped into DVMA space, where as the bp->b_data points to its	 * non-DVMA mapping.	 *	 * XXX - On the sun3, B_PHYS does NOT mean the buffer is mapped	 * into dvma space, only that it was remapped into the kernel.	 * We ALWAYS have to remap the kernel buf into DVMA space.	 * (It is done inexpensively, using whole segments!)	 */	block = bp->b_blkno + ((partno == RAW_PART) ? 0 :	    xysc->sc_dk.dk_label->d_partitions[partno].p_offset);	dbuf = dvma_mapin(bp->b_data, bp->b_bcount);	if (dbuf == NULL) {	/* out of DVMA space */		printf("%s: warning: out of DVMA space\n", 			   xycsc->sc_dev.dv_xname);		return (XY_ERR_FAIL);	/* XXX: need some sort of		                         * call-back scheme here? */	}	/* init iorq and load iopb from it */	xyc_rqinit(iorq, xycsc, xysc, XY_SUB_NORM | XY_MODE_VERBO, block,	    bp->b_bcount / XYFM_BPS, dbuf, bp);	xyc_rqtopb(iorq, iopb, (bp->b_flags & B_READ) ? XYCMD_RD : XYCMD_WR, 0);	/* Instrumentation. */	disk_busy(&xysc->sc_dk);	return (XY_ERR_AOK);}/* * xyc_submit_iorq: submit an iorq for processing.  returns XY_ERR_AOK * if ok.  if it fail returns an error code.  type is XY_SUB_*. * * note: caller frees iorq in all cases except NORM * * return value: *   NORM: XY_AOK (req pending), XY_FAIL (couldn't submit request) *   WAIT: XY_AOK (success), <error-code> (failed) *   POLL: <same as WAIT> *   NOQ : <same as NORM> * * there are three sources for i/o requests: * [1] xystrategy: normal block I/O, using "struct buf" system. * [2] autoconfig/crash dump: these are polled I/O requests, no interrupts. * [3] open/ioctl: these are I/O requests done in the context of a process, *                 and the process should block until they are done. * * software state is stored in the iorq structure.  each iorq has an * iopb structure.  the hardware understands the iopb structure. * every command must go through an iopb.  a 450 handles one iopb at a * time, where as a 451 can take them in chains.  [the 450 claims it * can handle chains, but is appears to be buggy...]   iopb are allocated * in DVMA space at boot up time.  each disk gets one iopb, and the * controller gets one (for POLL and WAIT commands).  what happens if  * the iopb is busy?  for i/o type [1], the buffers are queued at the  * "buff" layer and * picked up later by the interrupt routine.  for case  * [2] we can only be blocked if there is a WAIT type I/O request being * run.   since this can only happen when we are crashing, we wait a sec * and then steal the IOPB.  for case [3] the process can sleep * on the iorq free list until some iopbs are avaliable. */int xyc_submit_iorq(xycsc, iorq, type)	struct xyc_softc *xycsc;	struct xy_iorq *iorq;	int     type;{	struct xy_iopb *iopb;	u_long  iopbaddr;#ifdef XYC_DEBUG	printf("xyc_submit_iorq(%s, addr=0x%x, type=%d)\n", 		xycsc->sc_dev.dv_xname, iorq, type);#endif	/* first check and see if controller is busy */	if ((xycsc->xyc->xyc_csr & XYC_GBSY) != 0) {#ifdef XYC_DEBUG		printf("xyc_submit_iorq: XYC not ready (BUSY)\n");#endif		if (type == XY_SUB_NOQ)			return (XY_ERR_FAIL);	/* failed */		switch (type) {		case XY_SUB_NORM:			return XY_ERR_AOK;	/* success */		case XY_SUB_WAIT:			while (iorq->iopb->done == 0) {				sleep(iorq, PRIBIO);			}			return (iorq->errno);		case XY_SUB_POLL:		/* steal controller */			iopbaddr = xycsc->xyc->xyc_rsetup; /* RESET */			if (xyc_unbusy(xycsc->xyc,XYC_RESETUSEC) == XY_ERR_FAIL)				panic("xyc_submit_iorq: stuck xyc");			printf("%s: stole controller\n",				xycsc->sc_dev.dv_xname);			break;		default:			panic("xyc_submit_iorq adding");		}	}	iopb = xyc_chain(xycsc, iorq);	 /* build chain */	if (iopb == NULL) { /* nothing doing? */		if (type == XY_SUB_NORM || type == XY_SUB_NOQ) 			return(XY_ERR_AOK);		panic("xyc_submit_iorq: xyc_chain failed!\n");	}	iopbaddr = dvma_kvtopa((long) iopb, BUS_VME16);		XYC_GO(xycsc->xyc, iopbaddr);	/* command now running, wrap it up */	switch (type) {	case XY_SUB_NORM:	case XY_SUB_NOQ:		return (XY_ERR_AOK);	/* success */	case XY_SUB_WAIT:		while (iorq->iopb->done == 0) {			sleep(iorq, PRIBIO);		}		return (iorq->errno);	case XY_SUB_POLL:		return (xyc_piodriver(xycsc, iorq));	default:		panic("xyc_submit_iorq wrap up");	}	panic("xyc_submit_iorq");	return 0;	/* not reached */}/* * xyc_chain: build a chain.  return dvma address of first element in * the chain.   iorq != NULL: means we only want that item on the chain. */struct xy_iopb *xyc_chain(xycsc, iorq)struct xyc_softc *xycsc;struct xy_iorq *iorq;{  int togo, chain, hand;  struct xy_iopb *iopb, *prev_iopb;  bzero(xycsc->xy_chain, sizeof(xycsc->xy_chain));  /*   * promote control IOPB to the top   */  if (iorq == NULL) {    if ((XY_STATE(xycsc->reqs[XYC_CTLIOPB].mode) == XY_SUB_POLL ||        XY_STATE(xycsc->reqs[XYC_CTLIOPB].mode) == XY_SUB_WAIT) &&		xycsc->iopbase[XYC_CTLIOPB].done == 0)    iorq = &xycsc->reqs[XYC_CTLIOPB];  }  /*    * special case: if iorq != NULL then we have a POLL or WAIT request.   * we let these take priority and do them first.   */  if (iorq) {     xycsc->xy_chain[0] = iorq;    iorq->iopb->chen = 0;    return(iorq->iopb);  }  /*   * NORM case: do round robin and maybe chain (if allowed and possible)   */  chain = 0;  hand = xycsc->xy_hand;  xycsc->xy_hand = (xycsc->xy_hand + 1) % XYC_MAXIOPB;  for (togo = XYC_MAXIOPB ; togo > 0 ; togo--, hand = (hand + 1) % XYC_MAXIOPB){    if (XY_STATE(xycsc->reqs[hand].mode) != XY_SUB_NORM ||		xycsc->iopbase[hand].done)      continue;   /* not ready-for-i/o */    xycsc->xy_chain[chain] = &xycsc->reqs[hand];    iopb = xycsc->xy_chain[chain]->iopb;    iopb->chen = 0;    if (chain != 0) {   /* adding a link to a chain? */	  prev_iopb = xycsc->xy_chain[chain-1]->iopb;	  prev_iopb->chen = 1;	  prev_iopb->nxtiopb = 0xffff &		dvma_kvtopa((long) iopb, BUS_VME16);    } else {            /* head of chain */		iorq = xycsc->xy_chain[chain];    }    chain++;    if (xycsc->no_ols) break;   /* quit if chaining dis-allowed */  }  return(iorq ? iorq->iopb : NULL);}