#include "vmeio.h"
#if NVMEIO > 0 || defined (BINARY)
/*****************************************************************************
*       Copyright (c) 1990 Bill Nesheim, Thinking Machines Corporation, Inc.
*	      of Cambridge, Mass. All rights reserved.
*									     
*  This notice is intended as a precaution against inadvertent publication
*  and does not constitute an admission or acknowledgement that publication
*  has occurred or constitute a waiver of confidentiality.
*									     
*  Connection Machine software is the proprietary and confidential property  
*  of Thinking Machines Corporation.
*****************************************************************************/

/*
 * vmeio.c
 *
 * Driver for the Thinking Machines VMEIO interface
 * SunOS version.
 * 
 * Bill Nesheim
 * Thinking Machines Corp.
 */


#if !defined(lint) && !defined(__SABER__)
static char rcsid[] = "$Id: vmeio.c,v 1.37 1992/09/04 18:44:50 sean Exp $";
#endif

#define SUNOS4
#ifdef sparc
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/dir.h>
#include <sys/buf.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/map.h>
#include <sys/vmmac.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <vm/seg.h>
#else
#include "../h/types.h"
#include "../h/errno.h"
#include "../h/time.h"
#include "../h/param.h"
#include "../h/file.h"
#include "../h/dir.h"
#include "../h/buf.h"
#include "../h/systm.h"
#include "../h/conf.h"
#include "../h/map.h"
#include "../h/vmmac.h"
#include "../h/mman.h"
#include "../h/ioctl.h"
#include "../h/uio.h"
#include "../h/kernel.h"
#include "../h/proc.h"
#include "../h/user.h"
#include "../vm/seg.h"
#endif

#include "../machine/pte.h"
#include "../machine/psl.h"
#include "../machine/mmu.h"
#include "../machine/cpu.h"
#include "../machine/scb.h"

#include "../sundev/mbvar.h"

#include "../tmc/vmeio-reg.h"
#include "../tmc/vmeio-ioctl.h"

#define VMEIO_UNIT(dev) (minor(dev))

#define DEBUG
int vmeio_printfs = 0;
int vmeio_printintr = 0;
int vmeio_clear_dram = 1;

static int stride_xfer = 0;
static long stride;
static int stripe;
static int nstrides;

/*
 * Your standard device driver boilerplate.
 */
int     nVMEIO = NVMEIO;		/* Max number of interface cards */
int     vmeio_init(), vmeio_attach(), vmeio_probe(), vmeio_strategy(), vmeio_minphys();
struct buf vmeio_bufs[NVMEIO];

struct mb_device *vmeio_info[NVMEIO];
struct mb_driver vmeiodriver = {
	vmeio_probe,		/* device probe entry */
	0,			/* no slave device */
	vmeio_attach,		/* device attach entry */
	0,			/* no "go" routine */
	0,			/* no "done" routine */
	0,			/* no "poll" routine */
	sizeof (struct vmeio_regs),	/* memory requirements */
	"vmeio",				/* device name */
	vmeio_info,  			/* mbdinit backpointer */
	0,			/* controller name */
	0,			/* controller struct */
	0,			/* Mainbus usage flags */
	0,
};

char *vmeio_bus_phases[] = {
    "idle",		
    "data transfer",
    "arbitration",
    "target select",
    "bad phase 4",
    "bad phase 5",
    "grant",
    "bad phase 7",
    };


/*
 * The sun-4 cpu times out the vme after 136 microseconds
 */


/*
 * Timer stuff; abort read or write after VMEIO_OP_TIMEOUT secs
 */
int vmeio_timer_running = 0;
#define VMEIO_OP_TIMEOUT 15	/* 15 seconds */
extern struct timeval time;
void vmeio_timer();
extern int hz;

/*
 * DMA bus transfers must be aligned as follows
 */
#define VMEIO_ALIGN	8

/*
 * We use the bottom 1mb of the board for FIFO
 * mode transfers.  The remainder can be mapped
 * for application buffering
 */
#define DRAM_FIFO_SIZE 		(1*1024*1024)

/*
 * The RMW buffer size maximum is two DV blocks @ striping
 * factor of 8
 */
#define RMW_BUFFER_SIZE		(16384*8*2)

int vmeio_dram_size = 8*1024*1024;	/* AMOUNT OF VMEIO DRAM */
int dram_allocation = 8*1024*1024 - DRAM_FIFO_SIZE;

/* LARGEST PIECE OF DRAM ALLOCATED AT ONCE */
int max_dram = 8*1024*1024 - DRAM_FIFO_SIZE - RMW_BUFFER_SIZE;
int dram_mask = DRAM_MASK_8MB;
/* 
 * Number of slots in the DRAM resource map 
 */
#define VMEIO_DRAM_SLOTS 	(128)	

/*
 * If we're doing a transfer to or from another VME address,	
 * we don't use physio() but set the board up to do the transfer
 * directly.  This is the bigest transfer we'll program the board
 * to do at once.
 *
 * This value comes from the size of the VMEIO VME word count register.
 * DO NOT MAKE IT BIGGER!
 */
#define MAX_VME_XFR		((16*1024*1024) - 4)
int vmeio_max_xfr = MAX_VME_XFR;


struct dram_buf {
    struct vmeio_softc *dbuf_vmeio;	
    struct proc *dbuf_owner;
    short	dbuf_owner_pid;
    caddr_t	dbuf_base;
    int		dbuf_len;
    struct dram_buf *dbuf_next;
    struct dram_buf *dbuf_prev;
} vmeio_dram_bufs[VMEIO_DRAM_SLOTS * NVMEIO];

struct dram_buf *vmeio_free_dbufs;
struct dram_buf vmeio_active_dbufs;


/*
 * Per-interface data
 */
struct vmeio_softc {
    int 	cs_state;		/* state flags for this VMEIO board */
    int		cs_mode;		/* What we're currently doing */
    short	cs_diag_sig;		/* signal # to send to diagnostic proc */
    short	cs_diag_pid;		/* proc id to send diag signals */
    struct buf cs_diag_buf;		/* buf header for diagnostic DMA tests */
    caddr_t	cs_diag_space;		/* kmem_alloc()'d space for diags */
    int	cs_dbuf_sz;			/* size of above */
    int	cs_diag_cookie;			/* DVMA address for diagnostics */
    struct vmeio_config cs_config; 	/* current board configuration info */
    int	cs_sel_coll;			/* select call collision flags */
    struct proc 
	*cs_rsel, 			/* read, write, and exception */
	*cs_wsel, 			/*    select waiters */
	*cs_xsel;
    struct buf 	*cs_buf;		/* current buf pointer */
    int		cs_mbinfo;		/* information stash for DMA */
    struct vmeio_target_set cs_ts; 	/* target set (where to send to) */
    struct vmeio_target_set cs_ss; 	/* source set (where data is coming from) */
    long	cs_start_time;		/* operation start time for timeout */
    long	cs_slave_time;		/* for timing out the bus */
    int		cs_last_sender;		/* Last guy sending to us */
    int		cs_dram_pfn;		/* Page frame # and type of DRAM */
    caddr_t	cs_dram_addr;		/* Virtual address of DRAM (when mapped)*/
    int		cs_dram_ptr;		/* Used for diag-loop only */
    int		cs_resid;		/* For multi-bank xfers */
    int		cs_wordcount;		/* Used for timeouts */
    struct map  cs_dram_map[VMEIO_DRAM_SLOTS];	/* Resource map of our DRAM */
} vmeio_softc[NVMEIO];

/*
 * cs_state flags:
 */
#define CS_ATTACHED		0x01	/* device exists and is attached */
#define CS_DIAG			0x02	/* some diagnostic stuff; need cleanup on close */
#define CS_DIAG_INTR		0x04	/* special interrupt handling */
#define CS_DIAG_LOOP		0x08	/* set FIFO loop back */
#define CS_DIAG_BUF_OK		0x10	/* diag buf allocated already */
#define CS_BUSY			0x20	/* I/O in progress */
#define CS_CHAN_BYPASS		0x80	/* bypass memory */

#define CS_OPEN			0x200	/* someone is using the device */

#define CS_ARBITER_OK		0x1000	/* CMIO bus arbiter is up */

/*
 * cs_mode (what we're currently doing)
 */

#define IDLE 0
#define XFR_DRAM_CMIO 1	
#define XFR_CMIO_DRAM 2
#define XFR_VME_DRAM 3
#define XFR_DRAM_VME 4
#define XFR_VME_CMIO 5
#define XFR_CMIO_VME 6


 
/*
 * cm_sel_coll flags:
 */
#define CM_RSEL_COLL    1               /* Read selects collided */
#define CM_WSEL_COLL    2               /* Write selects collided */
#define CM_XSEL_COLL    4               /* Exception selects collided */

/*
 * Other useful stuff.
 */

#define ON 1
#define OFF 0

#define VMEIO_PRI (PZERO+1)

/*
 * For marking time
 */
extern struct timeval time;
#define TIME_STAMP ((long)time.tv_sec)


int vmeio_ints_alive = 0;
int vmeio_write_bypass = 0;
int vmeio_read_bypass = 0;




/*
 * VMEIOPROBE
 * "Probe" routine. Called once for each interface. 
 * Determine if the device is really there.
 */

vmeio_probe(regs, ctlr)
	register struct vmeio_regs *regs;
	register int ctlr;
{
    int result;

    /* is something there? */
    if (peekl((long *)&regs->vmeio_status, &result) == -1) {
      printf("vmeio_probe: no controller present at %x\n", regs);
	return(0);
    }
    if (peekl((long *)&regs->vmeio_command, &result) == -1) {
      printf("vmeio_probe: no controller present at %x\n", regs);
	return(0);
    }
    if (peekl((long *)&regs->vmeio_setup, &result) == -1) {
      printf("vmeio_probe: no controller present at %x\n", regs);
	return(0);
    }
    if (peekl((long *)&regs->vmeio_cmio_status, &result) == -1) {
      printf("vmeio_probe: no controller present at %x\n", regs);
	return(0);
    }
    /*
     * Note that this one also resets the board!
     */
    if (pokel((long *)&regs->vmeio_reset, 0) == -1) {
      printf("vmeio_probe: no controller present at %x\n", regs);
	return(0);
    }

    return (sizeof (struct vmeio_regs));
}



/*
 * VMEIOATTACH
 * Called once for each unit.
 */
vmeio_attach(md)
struct mb_device *md;
{
    int unit = md->md_unit;
    register struct vmeio_softc *cs = &vmeio_softc[unit];
    register struct vmeio_regs *regs;
    int i;
    int kmx;

#ifdef notdef
    printf("vmeio_attach vmeio%d\n", unit); 
    printf("mb_device:\n\tmd_driver 0x%x\n\tmd_unit %d, md_ctlr %d, md_slave %d\n",
	   md->md_driver, md->md_unit, md->md_ctlr, md->md_slave);
    printf("\tmd_addr 0x%x, md_intpri 0x%x, md_dk 0x%x, md_flags 0x%x\n",
	   md->md_addr, md->md_intpri, md->md_dk, md->md_flags);
#endif

    regs = (struct vmeio_regs *)md->md_addr;
    vmeio_ints_alive++;

    /*
     * initialize config info
     */
    cs->cs_config.cf_sta_id = 0;
    cs->cs_config.cf_busid = -3;
    cs->cs_config.cf_rev = (regs->vmeio_status & VMEIO_REV_MASK) >> VMEIO_REV_SHIFT;
    cs->cs_config.cf_dram_size = (regs->vmeio_status & VMEIO_DRAM_32MB) >> VMEIO_DRAM_SHIFT;
    cs->cs_state = CS_ATTACHED;
    cs->cs_diag_sig = SIGUSR1;

    if (regs->vmeio_status & VMEIO_DRAM_32MB)
      {
	vmeio_dram_size = 32*1024*1024;
	dram_allocation = vmeio_dram_size - DRAM_FIFO_SIZE;
	max_dram = dram_allocation - RMW_BUFFER_SIZE;
	dram_mask = DRAM_MASK_32MB;
      }
    /*
     * Setup mapping for the RAM
     * The VME address of the memory is passed in the md_flags argument
     * (listed in the config file)
     */
    cs->cs_dram_pfn = btoc(md->md_flags) | PGT_VME_D32;
    printf("vmeio%d dram at vme32d32 0x%x\n", unit, md->md_flags);
	  
    /*
     * Reset the board
     */
    vmeio_reset(md);
    /*
      vmeio_printregs("vmeio_attach after reset", (struct vmeio_regs *)md->md_addr);
     */

    /*
     * The resource map here is a 
     * map of numbers acceptable to our mmap routine
     * representing all but the bottom 1mb of the VMEIO dram.
     * We allocate in chuncks of size dram_allocation.
     */
    rminit(cs->cs_dram_map, dram_allocation,
	   0x20100000,
	   "vmeio dram", VMEIO_DRAM_SLOTS);

    if (vmeio_free_dbufs == NULL) {
	int i;
	
	for (i = 0; i < VMEIO_DRAM_SLOTS * NVMEIO; i++) {
	    vmeio_free_dbuf(&vmeio_dram_bufs[i]);
	}
    }
    
    if (vmeio_clear_dram)
    /*
     * Now map in the DRAM and zero it.
     * The board initializes with bad parity.
     */
    {
	int kmx;
	struct pte *ppte;
	int vpagenum;

	printf("vmeio%d: clearing DRAM: ", unit);

	/* First get 1mb of kernel virtual address space */
	kmx = rmalloc(kernelmap, btoc(1*1024*1024));
	if (kmx == NULL)
	    panic("vmeio_attach() can't get kernel vmem");

	ppte = &Sysmap[kmx];
	vpagenum = btoc(Sysbase) + kmx;

	/* Zero the VMEIO dram 1mb at a time */
	for (i = 0; i < vmeio_dram_size; i += (1*1024*1024)) {
	    int ppagenum = cs->cs_dram_pfn + btoc(i);

	    printf(".");
	    mapin(ppte, vpagenum, ppagenum, 
		  btoc(1*1024*1024), PG_V|PG_KW);
	    
	    bzero(ctob(vpagenum), 1*1024*1024);
	    mapout(ppte, btoc(1*1024*1024));
	}
	/* Free the kernel virtual address space we used */
	rmfree(kernelmap, btoc(1*1024*1024), kmx);
	printf("\n");
    }
}


vmeio_free_dbuf(dp)
struct dram_buf *dp;
{
    if (dp == NULL)
	panic("vmeio_free_dbuf NULL");

    if (vmeio_printfs && dp)
	printf("vmeio_free_dbuf 0x%x, length 0x%x, base 0x%x\n",	
	       dp, dp->dbuf_len, dp->dbuf_base);

    if (dp->dbuf_vmeio)
        rmfree(dp->dbuf_vmeio->cs_dram_map, dp->dbuf_len, dp->dbuf_base);
    dp->dbuf_vmeio = NULL;
    dp->dbuf_len = NULL;
    dp->dbuf_base = NULL;

    if (dp->dbuf_prev)
	dp->dbuf_prev->dbuf_next = dp->dbuf_next;

    if (dp->dbuf_next)
	dp->dbuf_next->dbuf_prev = dp->dbuf_prev;

    dp->dbuf_prev = NULL;
    dp->dbuf_next = vmeio_free_dbufs;
    vmeio_free_dbufs = dp;
}

caddr_t
vmeio_allocate_dbuf(cs, len)
struct vmeio_softc *cs;
{
    register struct dram_buf *dp;
    struct proc *p;
    
    if (vmeio_printfs)
	printf("vmeio_allocate_dbuf length 0x%x\n", len);

    if (len & (VMEIO_ALIGN - 1))
	len = (len + VMEIO_ALIGN) & ~(VMEIO_ALIGN - 1);

    if (len <= 0)
	return(0);

    /*
     * First check if any dbuf owners have died
     */
    for (dp = vmeio_active_dbufs.dbuf_next; dp; ) {
	if (((p = pfind(dp->dbuf_owner_pid)) == NULL)
	    || (dp->dbuf_owner != p)) {
	    register struct dram_buf *ddp;
	    /*
	     * Owner has gone away.
	     * Free resources
	     */
	    ddp = dp;
	    dp = ddp->dbuf_next;
	    vmeio_free_dbuf(ddp);
	}
	else {
	  dp = dp->dbuf_next;
	}
    }

    
    /* 
     * Now get a dbuf of the requested size
     */
    dp = vmeio_free_dbufs;
    if (dp == NULL)
	return 0;

    if ((dp->dbuf_base = (caddr_t) rmalloc(cs->cs_dram_map, len)) == NULL)
	return 0;

    vmeio_free_dbufs = dp->dbuf_next;
    if (vmeio_free_dbufs)
	vmeio_free_dbufs->dbuf_prev = NULL;

    dp->dbuf_len = len;
    dp->dbuf_vmeio = cs;
    dp->dbuf_owner = u.u_procp;
    dp->dbuf_owner_pid = dp->dbuf_owner->p_pid;
    dp->dbuf_next = vmeio_active_dbufs.dbuf_next;
    dp->dbuf_prev = &vmeio_active_dbufs;

    if (dp->dbuf_next)
	dp->dbuf_next->dbuf_prev = dp;
    vmeio_active_dbufs.dbuf_next = dp;

    if (vmeio_printfs)
	printf("vmeio_allocate_dbuf returns dp 0x%x, base 0x%x\n", 
		dp, dp->dbuf_base);

    return (dp->dbuf_base);
}


/*=====================================================================*/

/*
 * VMEIO_OPEN
 */

vmeio_open(dev, o_flags)
dev_t dev;
int o_flags;
{
    int unit = VMEIO_UNIT(dev);

    register struct vmeio_softc *cs = &vmeio_softc[unit];

    if (unit > nVMEIO)
	return (ENXIO);

    if ((cs->cs_state & CS_ATTACHED) == 0)
	return (ENXIO);

    if ((cs->cs_state & CS_OPEN) == 0) {
	cs->cs_state |= CS_OPEN;

	/* set up default target info */
	cs->cs_ts.ts_ntargets = 1;
	cs->cs_ts.ts_blocksize = -1; /* undefined, and unused */
	cs->cs_ts.ts_target_id[0] = cs->cs_config.cf_sta_id;
	cs->cs_ss.ts_ntargets = 1;
	cs->cs_ss.ts_blocksize = -1; /* undefined, and unused */
	cs->cs_ss.ts_target_id[0] = cs->cs_config.cf_sta_id;

    }
    /*
     * All swell. Start up the timer if necessary
     */
    if (vmeio_timer_running == 0) {
	vmeio_timer_running++;
	timeout(vmeio_timer, (caddr_t) 0, hz);
    }
    return 0;
}

/* 
 * VMEIO_CLOSE 
 */ 
vmeio_close(dev, flag)
dev_t dev;
int flag;
{
    register int unit = VMEIO_UNIT(dev);
    register struct vmeio_softc *cs = &vmeio_softc[unit];
    register struct mb_device *md = vmeio_info[unit];
    int pri;
    
    /* free any diagnostic resources */
    vmeio_freebuf(cs, md);
    /* clear loopback, etc */
    cs->cs_state &= ~(CS_OPEN|CS_DIAG_LOOP|CS_DIAG|CS_DIAG_INTR);
    pri = splr(pritospl(md->md_intpri));
    vmeio_reset(md);	/* clears slave enable */
    splx(pri);
    return 0;
}

/*
 * VMEIO_MMAP
 * map in the VMEIO board registers and/or DRAM
 */
vmeio_mmap(dev, off, prot)
dev_t dev;
off_t off;
int prot;
{
    register int unit = VMEIO_UNIT(dev);
    register struct vmeio_softc *cs = &vmeio_softc[unit];
    register struct mb_device *mb = vmeio_info[unit];
    int ret;

    if (off < sizeof (struct vmeio_regs)) {
	/* 
	 * map board registers (diagnostic mode)
	 * if (!suser()) {
	 *	u.u_error = EPERM;
	 *	return (-1);
	 * }
	 */
	ret = hat_getkpfnum(mb->md_addr + off);
    } else if (off >= 0x10000000 && off < 0x10000000 + vmeio_dram_size) {
	/* 
	 * Map DRAM (diagnostic mode)
	 * if (!suser()) {
	 *	u.u_error = EPERM;
	 *	return (-1);
	 * }
	 */
	off &= dram_mask;
	ret = cs->cs_dram_pfn + btoc(off);
    } else if (off >= 0x20100000 && off < 0x20100000 + dram_allocation) {
	/* 
	 * Map DRAM (allocated mode)
	 */
	register struct dram_buf *dp;

	for (dp = vmeio_active_dbufs.dbuf_next; dp; dp = dp->dbuf_next) {
	    if (dp->dbuf_vmeio != cs)
		continue;
	    if (dp->dbuf_base <= (caddr_t)off &&
		(caddr_t)off < dp->dbuf_base + dp->dbuf_len)
		break;
	}

	if (dp == NULL) {
	    u.u_error = ESRCH;
	    return -1;
	}

	/* Request is within allocated range.  Go ahead with map */
	off &= dram_mask;
	ret = cs->cs_dram_pfn + btoc(off);
    } else {
        u.u_error = EINVAL;
	ret = -1;
    }
    return(ret);
}

/*=====================================================================*/

/*
 * VMEIO_SELECT
 */
vmeio_select(dev, rw)
dev_t dev;
int rw;
{
    register int unit = VMEIO_UNIT(dev);
    register struct vmeio_softc *cs = &vmeio_softc[unit];
    register struct mb_device *md = vmeio_info[unit];
    register struct vmeio_regs *regs;
    int s;


    /* this is how to do a select if we had a command channel */

    regs = (struct vmeio_regs *)md->md_addr;
    s = splr(pritospl(md->md_intpri));
    switch (rw) {
    case FREAD:
	u.u_error = EOPNOTSUPP;
	goto WIN;	
#ifdef notdef
	if (1)	/* Something there already */
	    goto WIN;

	if (cs->cs_rsel &&
	    cs->cs_rsel->p_wchan == (caddr_t)&selwait)
	    cs->cs_sel_coll |= CM_RSEL_COLL;
	else
	    cs->cs_rsel = u.u_procp;

	/* now enable interrupts and wait for data */
	break;
#endif
    case FWRITE:	
	u.u_error = EOPNOTSUPP;
	goto WIN;	
#ifdef notdef
	if (0) /* we have a channel available to send out a command */
	    goto WIN;

	    if (cs->cs_wsel && 
		cs->cs_wsel->p_wchan == (caddr_t)&selwait) 
		cs->cs_sel_coll |= CM_WSEL_COLL; 
	    else     
		cs->cs_wsel = u.u_procp; 

	/* now enable interrupts and wait for an available channel */
	break;
#endif
    default:			/* Exception check */
	if (regs->vmeio_status  & VMEIO_ERROR)
	    goto WIN;

	if (cs->cs_xsel &&  
	    cs->cs_xsel->p_wchan == (caddr_t)&selwait)  
	    cs->cs_sel_coll |= CM_XSEL_COLL;  
	else      
	    cs->cs_xsel = u.u_procp;  

	/* now enable interrupts and wait for an exception */
	break;    
    }
    splx(s);
    return 0;
 WIN:
    splx(s);
    return 1;
}

vmeio_rw(dev, uio, rw)
dev_t dev;
struct uio *uio;
{
    register struct mb_device *md;
    register struct iovec *iov;
    int pfnum;
    int unit = VMEIO_UNIT(dev);
    unsigned vme_addr;
    int all_prev_lengths_zero = 1;

#ifdef DEBUG
    if (vmeio_printfs)
	printf("vmeio%d rw: resid=%d\n", unit, uio->uio_resid);
#endif

    if (unit > nVMEIO)
	return (ENXIO);

    if (uio->uio_resid == 0)
	return (0);

    uio->uio_offset = 0;
    /* 
     * If this is a VME only transfer,
     * don't call physio to do it.
     * Just split it up into vmeio_max_xfr chunks
     * and call the strategy routine directly.
     */
    for (iov = uio->uio_iov; iov < &uio->uio_iov[uio->uio_iovcnt]; iov++) {
	register struct buf *bp;
	register int len = iov->iov_len;

	if (len == 0)
	  continue;

	bp = &vmeio_bufs[unit];
	md = vmeio_info[unit];
	/*
	 * We need to at least fault the PTE for addr in
	 * so we can see if it is in VME space.  But I can't figure
	 * out how to fault just the PTE.  So we fault a whole page.
	 * (But only one!) Sigh.
	 */
	if(as_fault(u.u_procp->p_as, iov->iov_base, (u_int) 4,
			 F_SOFTLOCK, rw == B_READ ? S_WRITE : S_READ))
	    return (EFAULT);

	pfnum = hat_getkpfnum(iov->iov_base);
	(void) as_fault(u.u_procp->p_as, iov->iov_base, (u_int)4,
			F_SOFTUNLOCK, rw == B_READ ? S_WRITE : S_READ);

	uio->uio_resid -= len;

#ifdef	sun4m
	if (bustype(pfnum) == BT_VME) {
#else
	if ((pfnum & PG_TYPE) == PGT_VME_D32) {
#endif
	    /*
	     * VME only transfer, don't have to worry
	     * about locking down pages, etc.
	     * By avoiding a call to physio() we
	     * can do bigger transfers.
	     */
	  while (len > 0)
	    {
	      int c;
	      
	      bzero(bp, sizeof (struct buf));
	      bp->b_error = 0;
	      bp->b_proc = u.u_procp;
	      bp->b_un.b_addr = iov->iov_base;
	      bp->b_flags = B_BUSY | B_PHYS | rw;
	      bp->b_dev = dev;
	      bp->b_blkno = 0;
	      bp->b_bcount = len;

#ifdef	sun4m
	      vme_addr = (unsigned) ctob(pfnum) |
		((unsigned)bp->b_un.b_addr & PAGEOFFSET);
#else
	      vme_addr =
		(unsigned) ctob(((pfnum & PG_PFNUM) & (~PG_TYPE))) | 
		  ((unsigned)bp->b_un.b_addr & PAGEOFFSET);
#endif

	      if (!((vme_addr >= md->md_flags) &&
		    (vme_addr < (md->md_flags + vmeio_dram_size))))
		/*
		 * off-board VME transfer
		 * split up into vmeio_max_xfr
		 * units
		 */
		if (bp->b_bcount > vmeio_max_xfr)
		  bp->b_bcount = vmeio_max_xfr;
	      c = bp->b_bcount;
	      
	      /* Do the I/O */
	      u.u_procp->p_flag |= SPHYSIO;
	      vac_flush(bp->b_un.b_addr, c);
	      if (stride_xfer)
		vmeio_strat_stride(bp);
	      else
		vmeio_strategy(bp);
	      iowait(bp);
	      vac_flush(bp->b_un.b_addr, c);
	      u.u_procp->p_flag &= ~SPHYSIO;
	      
	      if (bp->b_flags & B_ERROR)	
		return (bp->b_error);
	      len -= c;
	      iov->iov_base += c;
	    }
	} else {
	    /* Let physio deal with it */
	    struct uio nuio;
	    int err;

	    /* Important stuff */
	    nuio.uio_iov = iov;
	    nuio.uio_iovcnt = 1;
	    nuio.uio_resid = len;

	    /* Junk, may not be needed */
	    nuio.uio_offset = 0;
	    nuio.uio_segflg = uio->uio_segflg;
	    nuio.uio_fmode = uio->uio_fmode;

	    bzero(bp, sizeof (struct buf));
	    err = physio(vmeio_strategy, bp,
			 dev, rw, vmeio_minphys, &nuio);
	    if (err)
		return(err);
	}
    }
    return (0);
}

/*
 * We need to check the alignment of the user's buffers here
 */
vmeio_read(dev, uio)
dev_t dev;
struct uio *uio;
{
    stride_xfer = 0;
    return (vmeio_rw(dev, uio, B_READ));
}

vmeio_write(dev, uio)
dev_t dev;
struct uio *uio;
{
    stride_xfer = 0;
    return (vmeio_rw(dev, uio, B_WRITE));
}

/*
 * Determine the maximum DMA transfer to or from system memory.
 */
int vmeio_max_vme_xfer = 64*1024;

vmeio_minphys(bp)
    register struct buf *bp;
{
    register int size;

    size = min(vmeio_max_vme_xfer, bp->b_bcount);

#ifdef DEBUG
    if (size != bp->b_bcount)
	if (vmeio_printfs)
	    printf("vmeio_minphys %d changed to %d\n", bp->b_bcount, size);
#endif
    bp->b_bcount = size;
}

vmeio_strategy(bp)
    register struct buf *bp;
{
    register struct mb_device *md;
    register struct vmeio_softc *cs;
    register struct vmeio_regs *regs;
    unsigned vme_addr;
    int s, i;
    int setup;
    int sender_id;
    int pfnum;

    md = vmeio_info[VMEIO_UNIT(bp->b_dev)];
    cs = &vmeio_softc[VMEIO_UNIT(bp->b_dev)];

    regs = (struct vmeio_regs *)md->md_addr;


    s = splr(pritospl(md->md_intpri));

    /*
     * We don't allow unaligned I/O's, or transfers
     * not a multiple of 8 bytes.
     */
    if (((int) bp->b_un.b_addr & (VMEIO_ALIGN - 1))
	|| (bp->b_bcount & (VMEIO_ALIGN-1))) {
	    printf("vmeio%d: unaligned or bad length transfer, vaddr = 0x%x, count=0x%x\n",
	       VMEIO_UNIT(bp->b_dev), 
	       bp->b_un.b_addr, 
	       bp->b_bcount);
	bp->b_error = EINVAL;
	bp->b_flags |= B_ERROR;
	iodone(bp);
	(void) splx(s);
	return;
    }

    /*
     * We don't allow external loopback transfers > 4k bytes
     */
    if (!(cs->cs_state & CS_DIAG_LOOP)
	&& (cs->cs_ts.ts_target_id[0] == cs->cs_config.cf_sta_id)
	&& (bp->b_bcount > VMEIO_FIFO_SIZE/2)) {
	bp->b_error = EINVAL;
	bp->b_flags |= B_ERROR;
	iodone(bp);
	(void) splx(s);
	return;
    }

    /*
     * sanity check soft dram pointers
     */
    if (cs->cs_dram_ptr <= 0 || 
	cs->cs_dram_ptr >= DRAM_FIFO_SIZE)
	cs->cs_dram_ptr = 0;

    
    /* wait for the board to be free */
    while (cs->cs_state & CS_BUSY)
	sleep((caddr_t) cs, VMEIO_PRI);

    cs->cs_state |= CS_BUSY;    /* All ours */
    cs->cs_slave_time = 0;	/* Note that we have done something */
    cs->cs_start_time = time.tv_sec;
    cs->cs_buf = bp;

    /*
     * If this is a transfer to or from Sun system
     * memory, perform the appropriate mapping or 
     * address calculations.
     */
    pfnum = hat_getkpfnum(bp->b_un.b_addr);

#ifdef	sun4m
    if(bustype(pfnum) == BT_VME) {
#else
    if ((pfnum & PG_TYPE) == PGT_VME_D32) {
#endif

	cs->cs_mbinfo = 0;

#ifdef	sun4m
	vme_addr = (unsigned) ctob(pfnum) |
	    ((unsigned)bp->b_un.b_addr & PAGEOFFSET);
#else
	vme_addr = (unsigned) ctob(((pfnum & PG_PFNUM) & (~PG_TYPE))) | 
	    ((unsigned)bp->b_un.b_addr & PAGEOFFSET);
#endif

    } else {
	cs->cs_mbinfo = mbsetup(md->md_hd, bp, 0);
	vme_addr = MBI_ADDR(cs->cs_mbinfo);
    }

#ifdef DEBUG
    if (vmeio_printfs) {
	printf("VMEIO%d %s vaddr 0x%x pfnum 0x%x paddr 0x%x count %d\n",
	       VMEIO_UNIT(bp->b_dev), 
	       (bp->b_flags & B_READ) ? "READ" : "WRITE",
	       bp->b_un.b_addr, 
	       pfnum & PG_PFNUM,
	       vme_addr,
	       bp->b_bcount);
    }
#endif

    /*
     * Rah Rah! program the transfer!
     * First figure out where the data is coming from.
     * If it's already on the VMEIO (in VMEIO DRAM), we simply
     * push it out to the CMIO bus.  If it's not, put the board
     * in FIFO mode and send the data to the CMIO bus.
     */

    /* 
     * Before starting, shut everything down 
     */
    /* Be careful here to preserve previous mode */
    regs->vmeio_command &= ~(VMEIO_AUTO_MASTER_ENA);	
    
    /* Set vme count to zero */
    regs->vmeio_vme_count = 0;
    
    /* Shut off VME master enable */
    regs->vmeio_master_enable = 0;
    
    /* Clear existing interrupt conditions */
    regs->vmeio_status = 0;
    
    if ((vme_addr >= md->md_flags) &&
	(vme_addr < (md->md_flags + vmeio_dram_size))) {
	/*
	 * The transfer is to or from VMEIO DRAM, 
	 * we don't need to do a VME transfer.
	 * We reserve the bottom one megabyte of the DRAM for fifo mode,
	 * leaving the rest for applications to use.
	 */

      regs->vmeio_command = ENABLE_VME_TIMEOUT|IMASK_RAM_PARERR|
	IMASK_CMIO_TIMER_ERROR|IMASK_CMIO_EXCEPTION|IMASK_CMIO_PARERR|
	  IMASK_CMIO_ARBITER_TIMEOUT|VMEIO_RAM_PARITY_ENA;

	if (!(bp->b_flags & B_READ)) {
	    /* Writing to the CMIO bus */
	    cs->cs_mode = XFR_DRAM_CMIO;
	    regs->vmeio_read_pointer = (vme_addr - md->md_flags)>>3;
	    regs->vmeio_command |= IMASK_CMIO_DONE|VME_TO_IOP_DIRECTION|
	      (((vme_addr - md->md_flags) & MASK_8MB)>>1);
	    setup = CMIO_MASTER_ENABLE;
	    sender_id =		/* ourselves */ cs->cs_config.cf_sta_id;
	} else {
	    cs->cs_mode = XFR_CMIO_DRAM;
	    regs->vmeio_write_pointer = (vme_addr - md->md_flags)>>3;
	    regs->vmeio_command |= IMASK_CMIO_DONE|IOP_TO_VME_DIRECTION|
	      (((vme_addr - md->md_flags) & MASK_8MB)>>1);
	    setup = 0;
	    sender_id = cs->cs_ss.ts_target_id[0];
	}

	/* Set RAM word counter to twos complement of number of 64 bit
	   words to be transferred */
	cs->cs_resid = bp->b_bcount -
	  (VMEIO_BANK_SIZE-((vme_addr - md->md_flags)%VMEIO_BANK_SIZE));
	if (cs->cs_resid < 0)
	  cs->cs_resid = 0;

	regs->vmeio_word_count = -((bp->b_bcount - cs->cs_resid) >> 3);
	
	/*
	 * Set initial wordcount in cs so timeout routine can
	 * check for data movement.
	 */
	cs->cs_wordcount = regs->vmeio_word_count;

	/* Set up the CMIO side to do the transfer */
	regs->vmeio_cmio_setup = setup|CMIO_ENABLE_PARITY|CMIO_ENABLE_TIMER|
	    ((cs->cs_config.cf_flags & VMEIO_CF_ARBITER)
	     ? CMIO_ENABLE_ARBITER :0) | 
		 ((cs->cs_config.cf_bus_speed << CMIO_SPEED_SHIFT) 
		  & CMIO_SPEED_MASK) |
		      ((cs->cs_config.cf_sta_id << CMIO_STATION_ID_SHIFT)
		       & CMIO_STATION_ID_MASK) |
			   ((cs->cs_ts.ts_target_id[0] << CMIO_TARGET_ID_SHIFT) 
			    & CMIO_TARGET_ID_MASK) |
				((sender_id << CMIO_SENDER_ID_SHIFT)
				 & CMIO_SENDER_ID_MASK) |
				     CMIO_ENABLE_SLAVE | CMIO_FIFO_ENABLE;
	/* Wait for an interrupt! */
    } else {
	/*
	 * The transfer is from elsewhere on the VME bus.
	 * If we're not in DIAG_LOOP mode, use fifo,
	 * otherwise we just do a DMA to the DRAM.
	 */

	/* Set up VME address and count */
	regs->vmeio_vme_addr = vme_addr;
	regs->vmeio_vme_count = bp->b_bcount;

	/*
	 * Set initial wordcount in cs so timeout routine can
	 * check for data movement.
	 */
	cs->cs_wordcount = regs->vmeio_vme_count;

	if (cs->cs_state & CS_DIAG_LOOP) {
	    /* 
	     * DMA from VME to or from DRAM, no I/O bus activity
	     */

	    /* Set VME interrupt enables */
	    regs->vmeio_command = ENABLE_VME_TIMEOUT|
		IMASK_RAM_PARERR|IMASK_VME_TIMEOUT;
	    
	    /* Set up DRAM pointers */
	    if (bp->b_flags & B_READ) {
		/* DRAM to VME */
		cs->cs_mode = XFR_DRAM_VME;
		regs->vmeio_read_pointer = (cs->cs_dram_ptr)>>3;
		regs->vmeio_command |= IMASK_VME_MASTER_DONE|IOP_TO_VME_DIRECTION;
	    } else {
		/* VME to DRAM */
		cs->cs_mode = XFR_VME_DRAM;
		regs->vmeio_write_pointer = (cs->cs_dram_ptr)>>3;
		regs->vmeio_command |= IMASK_VME_MASTER_DONE|VME_TO_IOP_DIRECTION;
	    }
	    cs->cs_dram_ptr += bp->b_bcount;
#ifdef DEBUG
	    if (vmeio_printfs)
		vmeio_printregs("vmeio dram <-> vme strategy", regs);
#endif

	    /* Go! */
	    regs->vmeio_master_enable = 1;

	} else {
	    int command;
	    int setup = 0;

	    /*
	     * DMA between an arbitrary VME address
	     * and the CMIO bus.
	     */


	    if (cs->cs_ts.ts_target_id[0] == cs->cs_config.cf_sta_id) {
		/* 
		 * External loopback transfer,  use RAM bypass mode
		 */
		command = VMEIO_RAM_BYPASS;
		if (!(bp->b_flags & B_READ)) 
	        	setup = CMIO_MASTER_ENABLE;
	    } else {
		/*
		 * use FIFO or BYPASS mode.
		 * The bottom megabyte is reserved for FIFO transfers.
		 */
		if (!(bp->b_flags & B_READ)) {
		    /* Writing */
		    if (vmeio_write_bypass) {
 			command = VMEIO_RAM_BYPASS;
			setup = CMIO_MASTER_ENABLE;
		    } else
			command = VMEIO_RAM_FIFO_MODE | 
			    (VMEIO_BS_1MB << VMEIO_BLOCK_SIZE_SHIFT);
		} else {
		    if (vmeio_read_bypass)
			command = VMEIO_RAM_BYPASS;
		    else
			command = VMEIO_RAM_FIFO_MODE | 
			    (VMEIO_BS_1MB << VMEIO_BLOCK_SIZE_SHIFT);
		}
   	    }

	    /* Set up the direction (command register) */
	    if (!(bp->b_flags & B_READ)) {
		/* Writing to the CMIO bus */
		cs->cs_mode = XFR_VME_CMIO;
		command |= IMASK_CMIO_DONE|VMEIO_AUTO_MASTER_ENA|VME_TO_IOP_DIRECTION;
		sender_id =	/* ourselves */ cs->cs_config.cf_sta_id;
	    } else {
		cs->cs_mode = XFR_CMIO_VME;
		command |= IMASK_VME_MASTER_DONE|IOP_TO_VME_DIRECTION;
		sender_id = cs->cs_ss.ts_target_id[0];
	    }

	    /* Set VME interrupt enables, etc */
	    regs->vmeio_command = ENABLE_VME_TIMEOUT|IMASK_RAM_PARERR|
	      IMASK_CMIO_TIMER_ERROR|IMASK_CMIO_EXCEPTION|IMASK_CMIO_PARERR|
		IMASK_CMIO_ARBITER_TIMEOUT|IMASK_VME_TIMEOUT|
		  VMEIO_RAM_PARITY_ENA|command;

	    /* Program the CMIO bus side of the transfer */
	    regs->vmeio_cmio_setup = setup | 
		CMIO_ENABLE_PARITY|CMIO_ENABLE_TIMER|
		    ((cs->cs_config.cf_flags & VMEIO_CF_ARBITER)
		     ? CMIO_ENABLE_ARBITER :0) | 
			 ((cs->cs_config.cf_bus_speed << CMIO_SPEED_SHIFT) 
			  & CMIO_SPEED_MASK) |
			      ((cs->cs_config.cf_sta_id << CMIO_STATION_ID_SHIFT)
			       & CMIO_STATION_ID_MASK) |
				   ((cs->cs_ts.ts_target_id[0] << CMIO_TARGET_ID_SHIFT) 
				    & CMIO_TARGET_ID_MASK) |
					((sender_id << CMIO_SENDER_ID_SHIFT)
					 & CMIO_SENDER_ID_MASK) |
					     CMIO_ENABLE_SLAVE | CMIO_FIFO_ENABLE;
#ifdef DEBUG
	    if (vmeio_printfs)
		vmeio_printregs("vmeio VME <-> CMIO strategy", regs);
#endif
	    /* Turn on the VME master! */
	    regs->vmeio_master_enable = 1;
	}
	/* Wait for an interrupt! */
    }
#ifdef DEBUG
    if (vmeio_printfs)
	printf("\tTransfer mode %d\n", cs->cs_mode);
#endif
    (void) splx(s);
}

vmeio_strat_stride(bp)
    register struct buf *bp;
{
  register struct mb_device *md;
  register struct vmeio_softc *cs;
  register struct vmeio_regs *regs;
  unsigned vme_addr;
  unsigned address;
  int s, i, iovcnt;
  unsigned command;
  unsigned setup;
  int sender_id;
  int pfnum;
  int not_last_iov = 1;
  int unit;
  
  unit = VMEIO_UNIT(bp->b_dev);
  md = vmeio_info[unit];
  cs = &vmeio_softc[unit];

  regs = (struct vmeio_regs *)md->md_addr;
  
  s = splr(pritospl(md->md_intpri));
  
  /*
   * We don't allow unaligned I/O's, or transfers
   * not a multiple of 8 bytes.
   */
  if (((int) bp->b_un.b_addr & (VMEIO_ALIGN - 1))
      || (bp->b_bcount & (VMEIO_ALIGN-1))) {
    printf("vmeio%d: unaligned or bad length transfer, vaddr = 0x%x, count=0x%x\n",
	   VMEIO_UNIT(bp->b_dev), 
	   bp->b_un.b_addr, 
	   bp->b_bcount);
    bp->b_error = EINVAL;
    bp->b_flags |= B_ERROR;
    iodone(bp);
    (void) splx(s);
    return;
  }
  
  /*
   * We don't allow external loopback transfers > 4k bytes
   */
  if (!(cs->cs_state & CS_DIAG_LOOP)
      && (cs->cs_ts.ts_target_id[0] == cs->cs_config.cf_sta_id)
      && (bp->b_bcount > VMEIO_FIFO_SIZE/2)) {
    bp->b_error = EINVAL;
    bp->b_flags |= B_ERROR;
    iodone(bp);
    (void) splx(s);
    return;
  }
  
  /*
   * sanity check soft dram pointers
   */
  if (cs->cs_dram_ptr <= 0 || 
      cs->cs_dram_ptr >= DRAM_FIFO_SIZE)
    cs->cs_dram_ptr = 0;
  
  
  /* wait for the board to be free */
  while (cs->cs_state & CS_BUSY)
    sleep((caddr_t) cs, VMEIO_PRI);
  
  cs->cs_state |= CS_BUSY;    /* All ours */
  cs->cs_slave_time = 0;	/* Note that we have done something */
  cs->cs_start_time = time.tv_sec;
  cs->cs_buf = bp;
  
  /*
   * This is a transfer to or from VMEIO
   * memory, perform the appropriate mapping or 
   * address calculations.
   */
  pfnum = hat_getkpfnum(bp->b_un.b_addr);
  cs->cs_mbinfo = 0;

#ifdef	sun4m
  vme_addr = (unsigned) ctob(pfnum) |
    ((unsigned)bp->b_un.b_addr & PAGEOFFSET);
#else
  vme_addr = (unsigned) ctob(((pfnum & PG_PFNUM) & (~PG_TYPE))) | 
    ((unsigned)bp->b_un.b_addr & PAGEOFFSET);
#endif
  
#ifdef DEBUG
  if (vmeio_printfs) {
    printf("VMEIO%d %s vaddr 0x%x pfnum 0x%x paddr 0x%x count %d\n",
	   VMEIO_UNIT(bp->b_dev), 
	   (bp->b_flags & B_READ) ? "READ" : "WRITE",
	   bp->b_un.b_addr, 
	   pfnum & PG_PFNUM,
	   vme_addr,
	   bp->b_bcount);
  }
#endif
  
  /*
   * Rah Rah! program the transfer!
   */
  
  /* 
   * Before starting, shut everything down 
   */
  /* Set vme count to zero */
  regs->vmeio_vme_count = 0;
  
  /* Shut off VME master enable */
  regs->vmeio_master_enable = 0;
  
  /* Clear existing interrupt conditions */
  regs->vmeio_status = 0;
  
  if (!(bp->b_flags & B_READ)) {
    /* Writing to the CMIO bus */
    cs->cs_mode = XFR_DRAM_CMIO;
    sender_id = cs->cs_config.cf_sta_id; /* ourselves */ 
  } else {
    cs->cs_mode = XFR_CMIO_DRAM;
    sender_id = cs->cs_ss.ts_target_id[0];
  }
  
  /*
   * Now loop through the strides
   */
  iovcnt = bp->b_bcount/stride;

  for (i=0; i<iovcnt; ++i)
    {
      address = vme_addr + (i * nstrides + stripe) * stride;
      
      if (i >= (iovcnt - 1))
	not_last_iov = 0;
      
      command = ENABLE_VME_TIMEOUT|IMASK_RAM_PARERR|
	IMASK_CMIO_TIMER_ERROR|IMASK_CMIO_EXCEPTION|IMASK_CMIO_PARERR|
	  IMASK_CMIO_ARBITER_TIMEOUT|VMEIO_RAM_PARITY_ENA;
      
      if (!(bp->b_flags & B_READ)) {
	/* Writing to the CMIO bus */
	regs->vmeio_read_pointer = (address - md->md_flags)>>3;
	command |= VME_TO_IOP_DIRECTION|
	  (((address - md->md_flags) & MASK_8MB)>>1);
	setup = CMIO_MASTER_ENABLE;
	if (not_last_iov)
	  command |= CMIO_MASTER_HOLD; /* stay on CMIO bus between strides */
      } else {
	regs->vmeio_write_pointer = (address - md->md_flags)>>3;
	command |= IOP_TO_VME_DIRECTION|
	  (((address - md->md_flags) & MASK_8MB)>>1);
	setup = 0;
      }
      
      /*
       * See if transfer will cross a VMEIO bank boundary.
       */
      cs->cs_resid = stride -
	(VMEIO_BANK_SIZE-((address - md->md_flags)%VMEIO_BANK_SIZE));
      if (cs->cs_resid < 0)
	cs->cs_resid = 0;
      
      if (!not_last_iov)
	/*
	 * Enable CMIO done interrupt only if this is the last stride
	 */
	command |= IMASK_CMIO_DONE;
      
      /*
       * Now set the command and vmeio_word_count registers.
       * Set initial wordcount in cs_wordcount so timeout
       * routine can check for data movement.
       */
      regs->vmeio_command = command;
      regs->vmeio_word_count = -((stride-cs->cs_resid)>>3);
      cs->cs_wordcount = -((stride-cs->cs_resid)>>3) | 0xfff00000;

      /* Set up the CMIO side to do the transfer */
      if (i == 0)
	regs->vmeio_cmio_setup = setup|CMIO_ENABLE_PARITY|CMIO_ENABLE_TIMER|
	  ((cs->cs_config.cf_flags & VMEIO_CF_ARBITER)
	   ? CMIO_ENABLE_ARBITER :0) | 
	     ((cs->cs_config.cf_bus_speed << CMIO_SPEED_SHIFT) 
	      & CMIO_SPEED_MASK) |
		((cs->cs_config.cf_sta_id << CMIO_STATION_ID_SHIFT)
		 & CMIO_STATION_ID_MASK) |
		   ((cs->cs_ts.ts_target_id[0] << CMIO_TARGET_ID_SHIFT) 
		    & CMIO_TARGET_ID_MASK) |
		      ((sender_id << CMIO_SENDER_ID_SHIFT)
		       & CMIO_SENDER_ID_MASK) |
			 CMIO_ENABLE_SLAVE | CMIO_FIFO_ENABLE;

      else
	regs->vmeio_cmio_setup |= setup;

      /* Wait for an interrupt! */
      
#ifdef DEBUG
      if (vmeio_printfs && i == 0)
	printf("\tTransfer mode %d\n", cs->cs_mode);
#endif
      (void) splx(s);
      
      if (not_last_iov)
	{
	  /*
	   * We're trying to avoid interrupt latencies by not enabling
	   * interrupts if there are more iov entries yet to go.  In
	   * this case, we spin-wait on either CMIO_DONE, or the word
	   * count register, depending on whether CMIO_MASTER_HOLD is set.
	   */
	  
	  while (cs->cs_state & CS_BUSY)
	    {
	      if (command & CMIO_MASTER_HOLD)
		{
		  if (regs->vmeio_word_count != 0xfff00000)
		    continue;
		  if (regs->vmeio_cmio_status & CMIO_INTERLOCK_FLAG)
		    continue;
		  break;
		}
	      else
		{
		  if ((regs->vmeio_status & IC_CMIO_DONE) == 0)
		    continue;
		  break;
		}
	    }
      
	  s = splr(pritospl(md->md_intpri));
	  
	  if (!(cs->cs_state & CS_BUSY))
	    {
	      /*
	       * A timeout must have occured -
	       * everything's already been cleared out
	       */
	      (void) splx(s);
	      return;
	    }
	  
	  /* Clear existing interrupt conditions */
	  regs->vmeio_status = 0;

	  if (cs->cs_resid > 0)
	    {
	      /*
	       * Bank switch required.  This is essentially the same
	       * code as that in the vmeio_intr() routine, replicated
	       * here so we don't have to deal with the interrupt routine's
	       * shutting everything down on completion.
	       */
	      int nextbank;
	      int newcount;
#ifdef DEBUG
	      if (vmeio_printintr)
		printf("old cs_resid = %d\n",cs->cs_resid);
#endif
	      nextbank =
		((regs->vmeio_command&VMEIO_BANK_MASK)>>VMEIO_BANK_SHIFT) + 1;
	      
	      if (nextbank > 3)
		{
		  printf("vmeio%d: mode=%d DRAM bank bit overflow\n",
			 unit,cs->cs_mode);
		  
		  newcount = regs->vmeio_word_count;
		  if (newcount + 1024*1024 == 0)
		    newcount = 0;
		  
		  printf("\t%d bytes out of %d transferred.\n",
			 bp->b_bcount - ((0 - newcount) << 3) - cs->cs_resid,
			 bp->b_bcount);
		  
		  bp->b_error = EIO;
		  vmeio_reset(md);
		  (void)splx(s);
		  return;
		}
	      
	      /* point to next 8MB bank of VMEIO DRAM */
	      
	      regs->vmeio_command &= ~VMEIO_BANK_MASK;
	      regs->vmeio_command |=
		(nextbank<<VMEIO_BANK_SHIFT)&VMEIO_BANK_MASK;
	      
	      if (cs->cs_mode == XFR_DRAM_CMIO)
		{
		  /* Writing to the CMIO bus */
		  regs->vmeio_read_pointer = 0;
		  setup = CMIO_MASTER_ENABLE;
		}
	      else
		{
		  regs->vmeio_write_pointer = 0;
		  setup = CMIO_ENABLE_SLAVE;
		}
	      
	      newcount = min(cs->cs_resid,VMEIO_BANK_SIZE);
	      cs->cs_resid -= newcount;
	      
	      /*
	       * Set new wordcount in cs so timeout routine can
	       * check for data movement.  Do this before we
	       * program the vmeio_word_count, since the transfer
	       * may resume immediately afterwards.
	       */
	      cs->cs_wordcount = -(newcount >> 3) | 0xfff00000;
	      
	      regs->vmeio_word_count = -(newcount >> 3);
	      regs->vmeio_cmio_setup |= setup;
	      
#ifdef DEBUG
	      if (vmeio_printintr)
		{
		  printf("new cs_resid = %d\n",cs->cs_resid);
		  vmeio_printregs("vmeio_intr: resid restart",regs);
		}
#endif
	      (void) splx(s);
	      
	      /*
	       * Now wait for second completion.  It's assumed that
	       * only one bank switch per stride will ever be necessary.
	       * Since a stride is 1024 bytes, and a bank is 8MB, this
	       * is a safe assumption.
	       */
	      while (cs->cs_state & CS_BUSY)
		{
		  if (command & CMIO_MASTER_HOLD)
		    {
		      if (regs->vmeio_word_count != 0xfff00000)
			continue;
		      if (regs->vmeio_cmio_status & CMIO_INTERLOCK_FLAG)
			continue;
		      break;
		    }
		  else
		    {
		      if ((regs->vmeio_status & IC_CMIO_DONE) == 0)
			continue;
		      break;
		    }
		}
	      
	      s = splr(pritospl(md->md_intpri));
	      
	      if (!(cs->cs_state & CS_BUSY))
		{
		  /*
		   * A timeout must have occured -
		   * everything's already been cleared out
		   */
		  (void) splx(s);
		  return;
		}
	      
	      /* Clear existing interrupt conditions */
	      regs->vmeio_status = 0;
	    }
	}
    }
}

/*=====================================================================*/

/*
 * INTERRUPT HANDLER.
 */
vmeio_intr(unit)
{
    register struct mb_device *md = vmeio_info[unit];
    register struct vmeio_regs *regs = 
	(struct vmeio_regs *)md->md_addr;
    register struct vmeio_softc *cs = &vmeio_softc[unit];
    register struct buf *bp;
    struct proc *pfind();
    int cmio_status;
    int cmio_setup;
    int status;
    int old_setup;

    old_setup = regs->vmeio_setup;
    regs->vmeio_setup = old_setup & ~VMEIO_INTR_LEVEL_MASK;
    
#ifdef DEBUG
    if (vmeio_printintr) {
	printf("VMEIO interrupt, mode = %d\n", cs->cs_mode);
	vmeio_printregs("vmeio_intr", regs);
    }
#endif

    if (cs->cs_state & CS_DIAG_INTR) {
	register struct proc *p = (struct proc *)0;
	/*
	 * for diag mode, just disable interrupts and hand off to
	 * diagnostic in user mode
	 */
	p = pfind(cs->cs_diag_pid);
	
	if (p) {
	    psignal(p, cs->cs_diag_sig);
	}
	return;
    }
    
    status = regs->vmeio_status;
    cmio_status = regs->vmeio_cmio_status;
    cmio_setup = regs->vmeio_cmio_setup;
    bp = cs->cs_buf;

    if (bp == (struct buf *)NULL) {
	printf("\r\nvmeio%d orphan interrupt, status = %b\n", 
	       unit, 
	       status, VMEIO_ERROR_STATUS_BITS);
	printf("\tcmio status %b, (%s)\n",
	       cmio_status, VMEIO_CMIO_ERROR_STATUS_BITS,
	       vmeio_bus_phases[(cmio_status & CMIO_BUS_CONTROL_MASK)
				>> CMIO_BUS_CONTROL_SHIFT]);
	printf("\tcmio setup = %b\n",
	       cmio_setup, VMEIO_CMIO_ERROR_SETUP_BITS);
        printf("\t\t<speed 0x%x sender 0x%x target 0x%x station 0x%x>\n",
	   (cmio_setup & CMIO_SPEED_MASK) >> CMIO_SPEED_SHIFT,
	   (cmio_setup & CMIO_SENDER_ID_MASK) >> CMIO_SENDER_ID_SHIFT,
	   (cmio_setup & CMIO_TARGET_ID_MASK) >> CMIO_TARGET_ID_SHIFT,
	   (cmio_setup & CMIO_STATION_ID_MASK) >> CMIO_STATION_ID_SHIFT);
	   
	vmeio_reset(md);
	return;
    }

    if (status & VMEIO_ERROR) {
	/*
	 * If we're not using the I/O bus and it's a strict I/O bus
	 * error, ignore it and proceed with the transfer
	 */
	if ((cs->cs_state & CS_DIAG_LOOP) &&
	    (status & IC_CMIO_INTERRUPT) &&
	    ! (status & (IC_RAM_PAR_ERR|IC_VME_TIMEOUT)))
	    goto noerr;
    
	bp->b_error = EIO;	/* Generic */

	/*
	 * Decode error for errno
	 */
	if (status & IC_CMIO_INTERRUPT) {	
	    /*
	     * CMIO error, have to decode CMIO status and setup regs
	     */
	    if (cmio_setup & IC_CMIO_EXCEPTION)
		bp->b_error = XCPRCV;
	    else if (cmio_setup & IC_CMIO_TIMER_ERROR) {
		int err;

		/* Check these error codes with Eric & Soroush */
		switch ((cmio_status & CMIO_BUS_CONTROL_MASK) 
			>> CMIO_BUS_CONTROL_SHIFT) {
		case BUS_IDLE_PHASE:
		    err = EIO;
		    break;
		case BUS_ARB_PHASE:
		    err = EIO;
		    break;
		case BUS_GRANT_PHASE:
		    err = EIO;
		    break;
		case BUS_TARGET_SEL_PHASE:
		    err = NOTSEL;
		    break;
		case BUS_XFR_PHASE:
		    err = BUSBSY;
		    break;
		}
		bp->b_error = err;
	    }
	    else if (cmio_setup & IC_CMIO_PARITY_ERROR)
		bp->b_error = BADPAR;
	    else if (cmio_setup & IC_CMIO_NO_ARBITER)
		bp->b_error = NOARBT;
	} else {
	    /*
	     * VMEIO error, decode based on status reg.
	     */
	    
	    if (status & IC_RAM_PAR_ERR)
		bp->b_error = BADPAR;
	    else if (status & IC_VME_TIMEOUT)
		bp->b_error = EIO;
	}

	/*
	 * Log the error
	 */
	printf("\r\nvmeio%d %s Error, status = %b\n", 
	       unit, (bp->b_flags & B_READ) ? "READ" : "WRITE",
	       status, VMEIO_ERROR_STATUS_BITS);
	printf("\tcmio status %b, (%s)\n",
	       cmio_status, VMEIO_CMIO_ERROR_STATUS_BITS,
	       vmeio_bus_phases[(cmio_status & CMIO_BUS_CONTROL_MASK)
				>> CMIO_BUS_CONTROL_SHIFT]);
	printf("\tcmio setup = %b\n",
	       cmio_setup, VMEIO_CMIO_ERROR_SETUP_BITS);
	if (cmio_setup & IC_CMIO_PARITY_ERROR)
	    printf("\tCM I/O bus parity error: parity=<0x%x>\n",
		   (cmio_status & CMIO_PARITY_MASK) >> CMIO_PARITY_SHIFT);

	if ((cs->cs_mode == XFR_DRAM_CMIO) ||
	    (cs->cs_mode == XFR_CMIO_DRAM)) {	
	    int count = regs->vmeio_word_count;
	    if (count + 1024*1024 == 0)
	      count = 0;

	    printf("\t%d bytes out of %d transferred.\n",
		   bp->b_bcount - ((0 - count) << 3) - cs->cs_resid,
		   bp->b_bcount);
	} else {
	    int count = regs->vmeio_vme_count & 0xffffff;
	    printf("\t%d bytes out of %d transferred.\n",
		   bp->b_bcount - count,
		   bp->b_bcount);
	}

	/* vmeio_reset errors out any pending I/O requests */
	vmeio_reset(md);

	/*
	 * Wake up anyone waiting for errors to happen
	 */
	if (cs->cs_xsel) {
	    selwakeup(cs->cs_xsel,
		      cs->cs_sel_coll & CM_XSEL_COLL);
	    cs->cs_xsel = (struct proc *) 0;
	    cs->cs_sel_coll &= ~CM_XSEL_COLL;
	}
	return;
    }

 noerr:
    /*
     * Looks like something finished.
     * processing depends on the transfer mode we were in
     */

    /*
     * First clear interrupt(s)
     */
    regs->vmeio_status = 0;


    switch (cs->cs_mode) {
    default:
	printf("vmeio%d: invalid state (%d) at interrupt\n",
	       unit, cs->cs_mode);
    case IDLE:
	vmeio_printregs("interrupt while idle", regs);
	break;
    case XFR_DRAM_CMIO:
    case XFR_CMIO_DRAM:
	/* MAPPED mode: We asked for a CMIO done interrupt */
	if ((status & IC_CMIO_DONE) == 0) {
	    printf("vmeio%d: mode=%d interrupt status incorrect!\n",
		   unit, cs->cs_mode);
	    vmeio_printregs("Wrong interrupt status", regs);
	}
	else if (cs->cs_resid)
	  {
	    int nextbank;
	    int newcount;
	    int setup;
#ifdef DEBUG
	    if (vmeio_printintr)
	      printf("old cs_resid = %d\n",cs->cs_resid);
#endif
	    nextbank =
	      ((regs->vmeio_command&VMEIO_BANK_MASK)>>VMEIO_BANK_SHIFT) + 1;
	    
	    if (nextbank > 3)
	      {
		printf("vmeio%d: mode=%d DRAM bank bit overflow\n",
		       unit,cs->cs_mode);

		newcount = regs->vmeio_word_count;
		if (newcount + 1024*1024 == 0)
		  newcount = 0;

		printf("\t%d bytes out of %d transferred.\n",
		       bp->b_bcount - ((0 - newcount) << 3) - cs->cs_resid,
		       bp->b_bcount);
		
		bp->b_error = EIO;
		bp->b_flags |= B_ERROR;
		iodone(bp);
		vmeio_reset(md);
		return;
	      }

	    /* point to next 8MB bank of VMEIO DRAM */

	    regs->vmeio_command &= ~VMEIO_BANK_MASK;
	    regs->vmeio_command |=
	      (nextbank<<VMEIO_BANK_SHIFT)&VMEIO_BANK_MASK;

	    if (cs->cs_mode == XFR_DRAM_CMIO)
	      {
		/* Writing to the CMIO bus */
		regs->vmeio_read_pointer = 0;
		setup = CMIO_MASTER_ENABLE;
	      }
	    else
	      {
		regs->vmeio_write_pointer = 0;
		setup = CMIO_ENABLE_SLAVE;
	      }
	    
	    newcount = min(cs->cs_resid,VMEIO_BANK_SIZE);
	    cs->cs_resid -= newcount;

	    /*
	     * Set new wordcount in cs so timeout routine can
	     * check for data movement.  Do this before we
	     * program the vmeio_word_count, since the transfer
	     * may resume immediately afterwards.
	     */
	    cs->cs_wordcount = -(newcount >> 3) | 0xfff00000;

	    regs->vmeio_word_count = -(newcount >> 3);
	    regs->vmeio_cmio_setup |= setup;

#ifdef DEBUG
	    if (vmeio_printintr)
	      {
		printf("new cs_resid = %d\n",cs->cs_resid);
		vmeio_printregs("vmeio_intr: resid restart",regs);
	      }
#endif
	    /* wait for next interrupt */
	    regs->vmeio_setup = old_setup;
	    return;
	  }
	break;
    case XFR_VME_DRAM:
    case XFR_DRAM_VME:
	/* We asked for a VME Master Done interrupt */
	if ((status & IC_VME_MASTER_DONE) == 0) {
	    printf("vmeio%d: mode=%d interrupts status incorrect!\n",
		   unit, cs->cs_mode);
	    vmeio_printregs("Wrong interrupt status", regs);
	}
	break;

    case XFR_VME_CMIO:
	/*
	 * FIFO mode, outbound. Wait for CMIO DONE
	 */
	if ((status & IC_CMIO_DONE) == 0) {
	    printf("vmeio%d: mode=%d interrupts status incorrect!\n",
		   unit, cs->cs_mode);
	    vmeio_printregs("Wrong interrupt status", regs);
	}
	break;
    case XFR_CMIO_VME:
	/*
	 * FIFO mode, inbound.  Wait for VME done.
	 */
	/* We asked for a VME Master Done interrupt */
	if ((status & IC_VME_MASTER_DONE) == 0) {
	    printf("vmeio%d: mode=%d interrupts status incorrect!\n",
		   unit, cs->cs_mode);
	    vmeio_printregs("Wrong interrupt status", regs);
	}
	break;
    }

    /*
     * Mark the buffer as done.
     */
    cs->cs_state &= ~CS_BUSY;
    wakeup((caddr_t) cs);
    if (cs->cs_mbinfo)
	mbrelse(md->md_hd, &cs->cs_mbinfo);
    bp->b_error = 0;
    iodone(bp);
    cs->cs_buf = 0;
    cs->cs_mode = IDLE;

    /* Shut off VME master enable */
    regs->vmeio_master_enable = 0;
    /* Disable further interrupts */
    regs->vmeio_command &= ~(IMASK_VME_TIMEOUT|IMASK_VME_MASTER_DONE|
			     IMASK_RAM_PARERR|IMASK_CMIO_TIMER_ERROR|
			     IMASK_CMIO_EXCEPTION|IMASK_CMIO_DONE|
			     IMASK_CMIO_SLAVE_DONE|IMASK_CMIO_PARERR|
			     IMASK_CMIO_ARBITER_TIMEOUT);

    /*
     * Clear auto CMIO master enable bit and
     * setup to clear CMIO master enable bit
     */
    regs->vmeio_command &= ~(VMEIO_AUTO_MASTER_ENA);
    cmio_setup &= ~(CMIO_MASTER_ENABLE);

    /*
     * Note who the last sender was in case he doesn't get
     * off the bus and we have to send an exception
     */
    cs->cs_last_sender = 
	(cmio_setup & CMIO_SENDER_ID_MASK) >> CMIO_SENDER_ID_SHIFT;

    /*
     * Set SENDER_ID in the CMIO setup reg to current station ID
     * so we NAK further incoming target selects
     */
    cmio_setup &= ~CMIO_SENDER_ID_MASK;
    cmio_setup |=
      (cs->cs_config.cf_sta_id << CMIO_SENDER_ID_SHIFT) & CMIO_SENDER_ID_MASK;
    regs->vmeio_cmio_setup = cmio_setup;

    regs->vmeio_setup = old_setup;
}

/*=====================================================================*/

/*
 * VMEIOCTL
 * Lots of good stuff here!
 */
vmeio_ioctl(dev, cmd, data)
dev_t dev;
register caddr_t data;
{
    int unit = VMEIO_UNIT(dev);
    register struct vmeio_softc *cs = &vmeio_softc[unit];
    register struct mb_device *md = vmeio_info[unit];
    register struct vmeio_regs *regs = 
	(struct vmeio_regs *)md->md_addr;
    int pri;
    int ret = 0;
    

    switch (cmd) {
    case VMEIO_READ_STRIDE:
    case VMEIO_WRITE_STRIDE:
      {
	int i;
	unsigned int tot_len;
	register struct uio *stride_uio;
	register struct vmeio_stride_rw *stride_rw = (struct vmeio_stride_rw *)data;

	if (stride_rw->rw_iovcnt < 1 || stride_rw->rw_iovcnt > 3)
	  {
	    stride_rw->rw_result = 0;
	    return(EINVAL);
	  }

	if ((stride_uio = (struct uio *)kmem_alloc(sizeof (struct uio))) == 0)
	  {
	    stride_rw->rw_result = 0;
	    return(ENOMEM);
	  }

	stripe = stride_rw->rw_stripe;
	stride = stride_rw->rw_stride;
	nstrides = stride_rw->rw_nstrides;

	stride_uio->uio_resid = 0;
	stride_uio->uio_iovcnt = stride_rw->rw_iovcnt;

	for (i=0; i<stride_rw->rw_iovcnt; ++i)
	  {
	    if (stride_rw->rw_iov[i].iov_len % stride_rw->rw_stride)
	      {
		stride_rw->rw_result = 0;
		return(EINVAL);
	      }
	    stride_uio->uio_resid += stride_rw->rw_iov[i].iov_len;
	  }

	tot_len = stride_uio->uio_resid;

	if ((stride_uio->uio_iov =
	     (struct iovec *)kmem_alloc
	     ((sizeof (struct iovec)) * stride_uio->uio_iovcnt)) == 0)
	  {
	    stride_rw->rw_result = 0;
	    return(ENOMEM);
	  }

	for (i=0; i<stride_rw->rw_iovcnt; ++i)
	  {
	    stride_uio->uio_iov[i].iov_base = stride_rw->rw_iov[i].iov_base;
	    stride_uio->uio_iov[i].iov_len = stride_rw->rw_iov[i].iov_len;
	  }

	stride_xfer = 1;
	ret=vmeio_rw(dev, stride_uio, (cmd==VMEIO_READ_STRIDE)?B_READ:B_WRITE);
	stride_rw->rw_result = tot_len - stride_uio->uio_resid;
	kmem_free(stride_uio->uio_iov,
		  (sizeof (struct iovec)) * stride_uio->uio_iovcnt);
	kmem_free(stride_uio, sizeof (struct uio));
	break;
      }

    case VMEIO_SET_TARGETS:
	cs->cs_ts = *(struct vmeio_target_set *)data;
	break;

    case VMEIO_GET_TARGETS:
	*(struct vmeio_target_set *)data = cs->cs_ts;
	break;

    case VMEIO_SET_SOURCES:
	cs->cs_ss = *(struct vmeio_target_set *)data;
	break;

    case VMEIO_GET_SOURCES:
	*(struct vmeio_target_set *)data = cs->cs_ss;
	break;


    case VMEIO_SET_CONFIGURATION:
	cs->cs_config = *(struct vmeio_config *)data;
	/* reset the board to set new config info */
	pri = splr(pritospl(md->md_intpri));
	ret = vmeio_reset(md);
	(void) splx(pri);
	break;
	
    case VMEIO_GET_CONFIGURATION:
	*(struct vmeio_config *)data = cs->cs_config;
	break;

    case VMEIO_RECEIVE_ENABLE:
	/*
	 * Enable reception of data from expected sender
	 */
	vmeio_setup_for_new_transfer(md);
	break;

    case VMEIO_GET_RAM_PTR:
	/*
	 * Return the current ram pointer for this section
	 */
	*(int *)data = cs->cs_dram_ptr & ~(DRAM_FIFO_SIZE - 1);
	break;

    case VMEIO_SET_RAM_PTR:
	/*
	 * Set the current ram pointer for this section
	 */
	cs->cs_dram_ptr = *(int *)data & ~(DRAM_FIFO_SIZE - 1);
	break;

    case VMEIO_ALLOCATE_DRAM:
	{
            int len;
            int cookie;

            len = *(int *)data;
            if (len <= 0 || len > max_dram) {
                return (EINVAL);
            }
            cookie = (int) vmeio_allocate_dbuf(cs, len);
            if (cookie == 0)
                return (ENOBUFS);     /* rmalloc failure */

            *(int *)data = cookie;
            break;
        }

    case VMEIO_FREE_DRAM:
	{
	    register struct dram_buf *dp;
	    int thing = *(int *)data;
	    
	    for (dp = vmeio_active_dbufs.dbuf_next; dp; dp = dp->dbuf_next) {
		if (dp->dbuf_vmeio != cs)
		    continue;
		if (dp->dbuf_base == (caddr_t) thing)
		    break;
	    }
	    if (dp == NULL)
		return ESRCH;
	    vmeio_free_dbuf(dp);	/* No way to force an unmap */
	    break;
	}

	/*
	 * Given an address, return success iff the address is
	 * in the VMEIO board's memory
	 */
    case VMEIO_IS_DRAM:
	{
	    unsigned addr = *(unsigned *)data;
	    int pfnum;
	    unsigned vme_addr;
	    faultcode_t fault_err;

	    /*
	     * We need to at least fault the PTE for addr in
	     * so we can see if it is in VME space.  But I can't figure
	     * out how to fault just the PTE.  So we fault a whole page.  Sigh
	     */
	    fault_err = as_fault(u.u_procp->p_as, addr, (u_int) 4,
			 F_SOFTLOCK, S_READ);
	    if (fault_err)
		return (EFAULT);

	    pfnum = hat_getkpfnum(addr);
	    (void) as_fault(u.u_procp->p_as, addr, (u_int)4,
			    F_SOFTUNLOCK, S_READ);

#ifdef	sun4m
	    vme_addr = (unsigned) ctob(pfnum) |
	      ((unsigned)addr & PAGEOFFSET);
#else
	    vme_addr = (unsigned) ctob(((pfnum & PG_PFNUM) & (~PG_TYPE))) | 
		((unsigned)addr & PAGEOFFSET);
#endif

	    if ((vme_addr >= md->md_flags) &&
		(vme_addr < (md->md_flags + vmeio_dram_size))) 
		return (0);

	    return (ENOENT);
	}


    case VMEIO_DIAG_SET_LOOP:
	cs->cs_state |= CS_DIAG|CS_DIAG_LOOP;
	break;

    case VMEIO_DIAG_INTR:
	cs->cs_state |= CS_DIAG|CS_DIAG_INTR;
	cs->cs_diag_pid = u.u_procp->p_pid;
	break;
	
    case VMEIO_DIAG_SET_SIG:
	cs->cs_diag_sig = *(int *)data;
	break;

    case VMEIO_DIAG_GET_SIG:
	*(int *)data = cs->cs_diag_sig;
	break;

    case VMEIO_RESET:
	pri = splr(pritospl(md->md_intpri));
	ret = vmeio_reset(md);
	(void) splx(pri);
	break;

    case VMEIO_SOFT_RESET:
	vmeio_soft_reset(md);
	break;

    case VMEIO_DIAG_ALLOC:
	{ 
	    int count;
	    count = *(int *)data;
	    if (vmeio_printfs)
		printf("VMEIO_DIAG_ALLOC %d\n", count);
	    return (vmeio_getbuf(cs, md, count));
        }

    case VMEIO_DIAG_FREE:
	if (vmeio_printfs)
	    printf("VMEIO_DIAG_FREE\n");
	vmeio_freebuf(cs, md);
	break;

    case VMEIO_DIAG_SETUP_DMA:
	if (!(cs->cs_state & CS_DIAG_BUF_OK))
	    return (ENOBUFS);
	if (cs->cs_diag_cookie)
	    return (EADDRINUSE);
	cs->cs_diag_cookie = mbsetup(md->md_hd, &cs->cs_diag_buf, 0);
	*(int *)data = MBI_ADDR(cs->cs_diag_cookie);
	if (vmeio_printfs)
	    printf("VMEIO_DIAG_SETUP_DMA returns 0x%x\n", 
		   MBI_ADDR(cs->cs_diag_cookie));
	break;

    case VMEIO_DIAG_RELEASE_DMA:
	if (!(cs->cs_state & CS_DIAG_BUF_OK))
	    return (ENOBUFS);
	if (cs->cs_diag_cookie == 0)
	    return (ENOENT);
	if (cs->cs_diag_cookie)
	    mbrelse(md->md_hd, &cs->cs_diag_cookie);
	cs->cs_diag_cookie = 0;
	if (vmeio_printfs)
	    printf("VMEIO_DIAG_RELEASE_DMA\n");
	break;

    case VMEIO_DIAG_CPYIN:
	{
	    int ret;
	    if (!(cs->cs_state & CS_DIAG_BUF_OK))
		return (ENOBUFS);
	    if (cs->cs_diag_cookie)
		return (EBUSY);
	
	    if (((struct iovec *)data)->iov_len > cs->cs_diag_buf.b_bufsize )
		return (EINVAL);

	    ret = copyin(((struct iovec *)data)->iov_base, 
			 cs->cs_diag_buf.b_un.b_addr, 
			 ((struct iovec *)data)->iov_len);

	    if (vmeio_printfs)
		printf("VMEIO_DIAG_CPYIN iov_base 0x%x iov_len 0x%x kbuf 0x%x returns 0x%x\n",
		       ((struct iovec *)data)->iov_base, 
		       ((struct iovec *)data)->iov_len,
		       cs->cs_diag_buf.b_un.b_addr,
		       ret);

	    return (ret);
	}

    case VMEIO_DIAG_CPYOUT:
	{
	    int ret;
	    if (!(cs->cs_state & CS_DIAG_BUF_OK))
		return (ENOBUFS);
	    if (cs->cs_diag_cookie)
		return (EBUSY);

	    if (((struct iovec *)data)->iov_len > cs->cs_diag_buf.b_bufsize )
		return (EINVAL);

	    ret = copyout(cs->cs_diag_buf.b_un.b_addr, 
			  ((struct iovec *)data)->iov_base, 
			  ((struct iovec *)data)->iov_len);

	    if (vmeio_printfs)
		printf("VMEIO_DIAG_CPYOUT iov_base 0x%x iov_len 0x%x kbuf 0x%x returns 0x%x\n",
		       ((struct iovec *)data)->iov_base, 
		       ((struct iovec *)data)->iov_len,
		       cs->cs_diag_buf.b_un.b_addr,
		       ret);

	    return (ret);
	}
    case VMEIO_DIAG_READ_REG:
	{
	    register struct vmeio_reg_acc *acc;
	    u_long val;
	    int result;

	    acc = (struct vmeio_reg_acc *)data;
	    
	    if (acc->offset % acc->mode)
		return(EFAULT);
	    if (acc->offset > sizeof (struct vmeio_regs))
		return(EINVAL);

	    switch(acc->mode) {
	    case MODE_BYTE:
		val = result = peekc(md->md_addr + acc->offset);
		break;
	    case MODE_WORD:
		val = result = peek(md->md_addr + acc->offset);
		break;
	    case MODE_LONG:
		result = peekl(md->md_addr + acc->offset, &val);
		break;
	    default:
		return EINVAL;
	    }
#ifdef DEBUG
	    if (vmeio_printfs)
		printf("VMEIO_DIAG_READ_REG offset %d mode %d got 0x%x\n", acc->offset,
		       acc->mode, val);
#endif
	    if (result == -1)
		return EIO;

	    acc->value = val;
	}
	break;

    case VMEIO_DIAG_WRITE_REG:
	{
	    register struct vmeio_reg_acc *acc;
	    int res;

	    acc = (struct vmeio_reg_acc *)data;
	    
	    if (acc->offset % acc->mode)
		return(EFAULT);
	    if (acc->offset > sizeof (struct vmeio_regs))
		return(EINVAL);

#ifdef DEBUG
	    if (vmeio_printfs)
		printf("VMEIO_DIAG_WRITE_REG offset %d mode %d val 0x%x\n", acc->offset,
		       acc->mode, acc->value);
#endif

	    switch(acc->mode) {
	    case MODE_BYTE:
		res = pokec(md->md_addr + acc->offset, acc->value);
		break;
	    case MODE_WORD:
		res = poke(md->md_addr + acc->offset, acc->value);
		break;
	    case MODE_LONG:
		res = pokel(md->md_addr + acc->offset, acc->value);
		break;
	    default:
		return EINVAL;
	    }
	    if (res == -1)
		return EIO;
	}
	break;

    default:
	return EINVAL;
    }
	    
    return ret;
}

/*
 * Prepare the board to do a transfer.
 * This is sort of a soft reset. We clear all fifos and 
 * set up the CMIO side, but we set SLAVE_BUSY because
 * we don't yet know where to put any incoming data.
 */
vmeio_setup_for_new_transfer(md)
struct mb_device *md;
{
    register struct vmeio_regs *regs = (struct vmeio_regs *)md->md_addr;
    int unit = md->md_unit;
    register struct vmeio_softc *cs = &vmeio_softc[unit];
    int pri;
    int setup;

    pri = splr(pritospl(md->md_intpri));

    /* Shut everything down, preserving map/FIFO mode and FIFO size */
    regs->vmeio_command &= (VMEIO_RAM_FIFO_MODE|VMEIO_BLOCK_SIZE_MASK);

    regs->vmeio_master_enable = 0;	/* Disable VME master */
    
    regs->vmeio_read_pointer = 0;	/* Reset RAM fifo */
    regs->vmeio_write_pointer = 0;
    regs->vmeio_word_count = 0;

    setup = (cs->cs_config.cf_sta_id << CMIO_STATION_ID_SHIFT) |
	(cs->cs_ts.ts_target_id[0] << CMIO_TARGET_ID_SHIFT) |
	    (cs->cs_ss.ts_target_id[0] << CMIO_SENDER_ID_SHIFT) |
		(cs->cs_config.cf_bus_speed << CMIO_SPEED_SHIFT) |
		    ((cs->cs_config.cf_flags & VMEIO_CF_ARBITER) ?
		     CMIO_ENABLE_ARBITER : 0) |
			 CMIO_ENABLE_PARITY | CMIO_ENABLE_TIMER |
			     CMIO_ENABLE_SLAVE | CMIO_SLAVE_BUSY;


    regs->vmeio_cmio_setup = setup;	/* Reset CMIO input fifo */
    regs->vmeio_cmio_setup = setup | CMIO_FIFO_ENABLE;
    (void) splx(pri);
    /*
    if (vmeio_printfs)
	vmeio_printregs("setup_for_new_transfer", regs);
     */
}

/*
 * hard reset the VMEIO card
 *
 * The goal here is to abort any operations in progress, 
 * reset the board, and put the board in the proper initial state.
 */
vmeio_reset(md)
struct mb_device *md;
{
    register struct vmeio_regs *regs = (struct vmeio_regs *)md->md_addr;
    int unit = md->md_unit;
    register struct vmeio_softc *cs = &vmeio_softc[unit];
    int foo, bar;
    int ret = 0;

    regs->vmeio_master_enable = 0;	/* Disable VME master */
    regs->vmeio_reset = 1;		/* Reset the board */
    DELAY(1000);

    /*
     * clear any interrupt conditions, and all the registers.
     */
    regs->vmeio_status = 0;
    regs->vmeio_vme_addr = 0;
    regs->vmeio_vme_count = 0;
    regs->vmeio_read_pointer = 0;
    regs->vmeio_write_pointer = 0;
    regs->vmeio_word_count = 0;
    regs->vmeio_cmio_setup = 0;	/* Reset CMIO input fifos */

    /*
     * Initialize vme setup register (VME bus parameters)
     */
    regs->vmeio_setup =
	md->md_intr->v_vec |
	    (VME_ENP_DATA << VMEIO_ADDR_MOD_SHIFT) |
		(VMEIO_BR_LEVEL << VMEIO_VME_REQ_SHIFT) |
		    (md->md_intpri << VMEIO_INTR_LEVEL_SHIFT) ;
    /*
     * If we've been told our station id,
     * set up the CMIO side of the board.
     * Set up bus speed, arbiter, station id,
     * and put our station id in sender_id and target_id fields.
     */
    if (cs->cs_config.cf_busid >= 0) 
	regs->vmeio_cmio_setup = 
	    (cs->cs_config.cf_sta_id << CMIO_STATION_ID_SHIFT) |
		(cs->cs_config.cf_sta_id << CMIO_TARGET_ID_SHIFT) |
		    (cs->cs_config.cf_sta_id << CMIO_SENDER_ID_SHIFT) |
			(cs->cs_config.cf_bus_speed << CMIO_SPEED_SHIFT) |
			    ((cs->cs_config.cf_flags & VMEIO_CF_ARBITER) ?
			     CMIO_ENABLE_ARBITER : 0) | CMIO_ENABLE_SLAVE |
				 CMIO_ENABLE_PARITY | CMIO_ENABLE_TIMER;

    /* restore config info for rev and DRAM size */
    cs->cs_config.cf_rev = (regs->vmeio_status & VMEIO_REV_MASK) >> VMEIO_REV_SHIFT;
    cs->cs_config.cf_dram_size = (regs->vmeio_status & VMEIO_DRAM_32MB) >> VMEIO_DRAM_SHIFT;

 cleanup:
    /* clear DIAG mode */
    cs->cs_state &= ~(CS_DIAG|CS_DIAG_INTR|CS_DIAG_LOOP);

    if (cs->cs_state & CS_BUSY) {
#ifdef DEBUG
	if (vmeio_printfs)
	    printf("vmeio_reset unit%d abort I/O buf=0x%x\n",	
		   unit, cs->cs_buf);
#endif DEBUG
	cs->cs_buf->b_flags |= B_ERROR;
	cs->cs_state &= ~CS_BUSY;
	wakeup((caddr_t) cs);
	if (cs->cs_mbinfo)
	    mbrelse(md->md_hd, &cs->cs_mbinfo);
	iodone(cs->cs_buf);
	cs->cs_buf = 0;
    }

    return (ret);
}
/*
 * soft reset the VMEIO card
 *
 * Put the board back in a sane state, but don't
 * abort any transfers and be careful not
 * to disturb the arbiter, etc.
 */
vmeio_soft_reset(md)
struct mb_device *md;
{
    register struct vmeio_regs *regs = (struct vmeio_regs *)md->md_addr;
    int unit = md->md_unit;
    register struct vmeio_softc *cs = &vmeio_softc[unit];
    int pri, setup;

    pri = splr(pritospl(md->md_intpri));

    regs->vmeio_command = 0;		/* Shut everything down */
    regs->vmeio_master_enable = 0;	/* Disable VME master */
    
    regs->vmeio_read_pointer = 0;	/* Reset RAM fifo */
    regs->vmeio_write_pointer = 0;
    regs->vmeio_word_count = 0;

    /*
     * Initialize vme setup register (VME bus parameters)
     */
    regs->vmeio_setup =
	md->md_intr->v_vec |
	    (VME_ENP_DATA << VMEIO_ADDR_MOD_SHIFT) |
		(VMEIO_BR_LEVEL << VMEIO_VME_REQ_SHIFT) |
		    (md->md_intpri << VMEIO_INTR_LEVEL_SHIFT) ;
    /*
     * If we've been told our station id,
     * set up the CMIO side of the board.
     * Set up bus speed, arbiter, station id,
     * and put our station id in sender_id and target_id fields.
     */
    if (cs->cs_config.cf_busid >= 0) 
	regs->vmeio_cmio_setup = 
	    (cs->cs_config.cf_sta_id << CMIO_STATION_ID_SHIFT) |
		(cs->cs_config.cf_sta_id << CMIO_TARGET_ID_SHIFT) |
		    (cs->cs_config.cf_sta_id << CMIO_SENDER_ID_SHIFT) |
			(cs->cs_config.cf_bus_speed << CMIO_SPEED_SHIFT) |
			    ((cs->cs_config.cf_flags & VMEIO_CF_ARBITER) ?
			     CMIO_ENABLE_ARBITER : 0) | CMIO_ENABLE_SLAVE |
				 CMIO_ENABLE_PARITY | CMIO_ENABLE_TIMER;

    /* restore config info for rev and DRAM size */
    cs->cs_config.cf_rev = (regs->vmeio_status & VMEIO_REV_MASK) >> VMEIO_REV_SHIFT;
    cs->cs_config.cf_dram_size = (regs->vmeio_status & VMEIO_DRAM_32MB) >> VMEIO_DRAM_SHIFT;

    /* CMIO input fifo held reset */

    (void) splx(pri);
}

/*
 * get a DMA buffer for diagnostics
 * This buffer can then be mapped into the user's address space
 * via mmap
 */
caddr_t vmeio_diag_buf;

static int
vmeio_getbuf(cs, md, size)
    struct vmeio_softc *cs;
    struct mb_device *md;
    int size;
{
    caddr_t p;
    struct buf *bp = &cs->cs_diag_buf;

#ifdef DEBUG
    if (vmeio_printfs)
	printf("vmeio_getbuf %d bytes\n", size);
#endif

    if (size > MAX_VMEIO_BSIZE )
	size = MAX_VMEIO_BSIZE;
    if (size < PAGESIZE)
	size = PAGESIZE;

    if (cs->cs_state & CS_DIAG_BUF_OK) {
	if (size <= cs->cs_dbuf_sz)
	    return 0;
	vmeio_freebuf(cs, md);
    }

    if ((cs->cs_diag_space = kmem_alloc(size + (VMEIO_ALIGN - 1))) == 0)
	return ENOSPC;

    cs->cs_state |= CS_DIAG_BUF_OK;
    cs->cs_dbuf_sz = size;
    p = (caddr_t) ((int) (cs->cs_diag_space + (VMEIO_ALIGN - 1)) & ~(VMEIO_ALIGN - 1));
    if (p < cs->cs_diag_space)
	printf("vmeio_getbuf screwed up, p=0x%x, cs_diag_space = 0x%x\n",
	       p, cs->cs_diag_space);
    
#ifdef DEBUG
    if (vmeio_printfs)
	printf("vmeio_getbuf addr 0x%x, diag_space 0x%x, size %d\n",
	       p, cs->cs_diag_space, size);
#endif
    bzero(bp, sizeof (struct buf));
    bp->b_un.b_addr = p;
    bp->b_flags = 0;
    bp->b_bcount = size;
    bp->b_bufsize = size;

    vmeio_diag_buf = p;

    return 0;
}

static
vmeio_freebuf(cs, md)
    struct vmeio_softc *cs;
    struct mb_device *md;
{
    caddr_t p;

    if (!(cs->cs_state & CS_DIAG_BUF_OK))
	return;

    cs->cs_state &= ~CS_DIAG_BUF_OK;
    if (cs->cs_diag_cookie)
	mbrelse(md->md_hd, &cs->cs_diag_cookie);
    cs->cs_diag_cookie = 0;
    kmem_free(cs->cs_diag_space);
    if (vmeio_printfs)
	printf("vmeio_freebuf\n");
    vmeio_diag_buf = 0;
}

/*
 * Deal with timeouts
 * Run once per second via timeout().
 * Assumes that all VMEIO boards have same interrupt priority!
 */
static void
vmeio_timer(val)
int val;
{
    int intf, s;
    int cmio_status;
    int cmio_setup;
    int vmeio_status;
    register struct vmeio_softc *cs;
    register struct mb_device *md = vmeio_info[0];
    register struct vmeio_regs *regs;
    
    s = splr(pritospl(md->md_intpri));
    for (intf = 0; intf < nVMEIO; ++intf) { 
	cs = &vmeio_softc[intf];
	md = vmeio_info[intf];
	regs = 	(struct vmeio_regs *)md->md_addr;

	if ((cs->cs_state & CS_ATTACHED) == 0) {
	    continue;
	}

	if ((cs->cs_state & CS_BUSY) == 0) {
	    /*
	     * If we're running diags, just continue
	     */
	    if (cs->cs_state & CS_DIAG) {
		continue;
	    }

	    /*
	     * no operation currently in progress
	     * Deal with bus timeout.
	     * If we're selected slave for > VMEIO_OP_TIMEOUT seconds,
	     * and no data is moving, pull exception to get
	     * the current master off the bus.
	     * We have to do this even if there's no operation currently
	     * in progress.
	     */

	    /* Take this out for production ... */
	    if (peekl((long *)&regs->vmeio_cmio_status, &cmio_status) == -1)
		continue;

	    cmio_setup = regs->vmeio_cmio_setup;
	    
	    if ((cmio_status & CMIO_SLAVE_ACTIVE) == 0) {
		/*
		 * We're not currently active.  Just keep track of
		 * the bus status
		 */
		if (cmio_setup & IC_CMIO_NO_ARBITER) {
		    if (cs->cs_state & CS_ARBITER_OK) {
			printf("vmeio%d: CMIO bus arbiter down <%s>.\n", 
			       intf, 
			       vmeio_bus_phases[(cmio_status & CMIO_BUS_CONTROL_MASK)
						>> CMIO_BUS_CONTROL_SHIFT]);
			cs->cs_state &= ~CS_ARBITER_OK;
		    }
		} else {
		  if ((cmio_setup & IC_CMIO_NO_ARBITER) == 0) {
		    if ((cs->cs_state & CS_ARBITER_OK) == 0) {
			printf("vmeio%d: CMIO bus arbiter up.\n", intf);
			cs->cs_state |= CS_ARBITER_OK;
		    }
		  }
		}
		
		cs->cs_slave_time = 0;
		continue;
	    }

	    /*
	     * Currently selected slave.  Keep track of how long
	     */
	    if (cs->cs_slave_time++ > VMEIO_OP_TIMEOUT) {
		printf("\r\nvmeio%d: Idle but still slave after %d seconds.\n",
		       intf, VMEIO_OP_TIMEOUT);
		printf("\ttarget = %d, sender = %d, my id = %d, last sender = %d\n",
		       (cmio_setup & CMIO_TARGET_ID_MASK) >> CMIO_TARGET_ID_SHIFT,
		       (cmio_setup & CMIO_SENDER_ID_MASK) >> CMIO_SENDER_ID_SHIFT,
		       cs->cs_config.cf_sta_id,
		       cs->cs_last_sender);
		printf("\tvmeio status %b\n", 
		       regs->vmeio_status, VMEIO_ERROR_STATUS_BITS);
		printf("\tcmio status %b, (%s)\n",
		       cmio_status, VMEIO_CMIO_ERROR_STATUS_BITS,
		       vmeio_bus_phases[(cmio_status & CMIO_BUS_CONTROL_MASK)
					>> CMIO_BUS_CONTROL_SHIFT]);
		printf("Exception sent\n");

		regs->vmeio_command = 0;	/* disable interrupts */
		regs->vmeio_cmio_setup |= CMIO_REQ_EXCEPTION;
		vmeio_reset(md);
	    }
	    continue;
	} 

	/*
	 * Deal with timeouts for operations currently in progress.
	 */

	if ((time.tv_sec - cs->cs_start_time) <= VMEIO_OP_TIMEOUT) {
	    continue;			/* ok so far */
	}

	/* We're doing something.  Has any data moved? */
	switch (cs->cs_mode)
	  {
	  case XFR_DRAM_CMIO:
	  case XFR_CMIO_DRAM:
	    if (regs->vmeio_word_count != cs->cs_wordcount)
	      {
		/*
		 * Some data has moved
		 */
		cs->cs_start_time = time.tv_sec;
		cs->cs_wordcount = regs->vmeio_word_count;
		continue;
	      }
	    break;
	  case XFR_VME_DRAM:
	  case XFR_DRAM_VME:
	  case XFR_VME_CMIO:
	  case XFR_CMIO_VME:
	    if (regs->vmeio_vme_count != cs->cs_wordcount)
	      {
		/*
		 * Some data has moved
		 */
		cs->cs_start_time = time.tv_sec;
		cs->cs_wordcount = regs->vmeio_vme_count;
		continue;
	      }
	    break;

	  case IDLE:
	    printf("vmeio%d: state is BUSY (%d), but mode is IDLE at timeout\n",
		   cs->cs_state);
	    break;

	  default:
	    printf("vmeio%d: invalid transfer mode at timeout (%d)\n",
		   cs->cs_mode);
	  }

	/* Something is hung, we should have gotten a DONE interrupt by now */
	vmeio_status = 0;
	cmio_status = regs->vmeio_cmio_status;
	cmio_setup = regs->vmeio_cmio_setup;

	printf("vmeio%d %s timeout\n", 
	       intf, 
	       (cs->cs_buf->b_flags & B_READ) ? "read" : "write");

	printf("\tvmeio status %b\n", 
	       regs->vmeio_status, VMEIO_ERROR_STATUS_BITS);
	printf("\tcmio status %b, \n\t\t<%s>\n",
	       cmio_status, VMEIO_CMIO_ERROR_STATUS_BITS,
	       vmeio_bus_phases[(cmio_status & CMIO_BUS_CONTROL_MASK) 
				>> CMIO_BUS_CONTROL_SHIFT]);

	if ((cs->cs_mode == XFR_DRAM_CMIO) ||
	    (cs->cs_mode == XFR_CMIO_DRAM)) {	
	    int count = regs->vmeio_word_count;
	    if (count + 1024*1024 == 0)
	      count = 0;

	    printf("\tread pointer 0x%x\n\twrite pointer 0x%x\n\tword count 0x%x\n\tcs_resid 0x%x\n",
		   regs->vmeio_read_pointer,
		   regs->vmeio_write_pointer,
		   count,
		   cs->cs_resid);

	    printf("\t%d bytes out of %d transferred.\n",
		   cs->cs_buf->b_bcount - ((0 - count) << 3) - cs->cs_resid,
		   cs->cs_buf->b_bcount);
	} else {
	    int count = regs->vmeio_vme_count & 0xffffff;

	    /*
	    printf("\tvme address 0x%x\n\tvme count 0x%x\n",
		   regs->vmeio_vme_addr,
		   count);
	     */
	    printf("\t%d bytes out of %d transferred.\n",
		   cs->cs_buf->b_bcount - count,
		   cs->cs_buf->b_bcount);
	}
	printf("\tcmio setup %b\n", cmio_setup, VMEIO_CMIO_ERROR_SETUP_BITS);
	printf("\t<speed 0x%x sender 0x%x target 0x%x station 0x%x>\n",
	       (cmio_setup & CMIO_SPEED_MASK) >> CMIO_SPEED_SHIFT,
	       (cmio_setup & CMIO_SENDER_ID_MASK) >> CMIO_SENDER_ID_SHIFT,
	       (cmio_setup & CMIO_TARGET_ID_MASK) >> CMIO_TARGET_ID_SHIFT,
	       (unsigned)(cmio_setup & CMIO_STATION_ID_MASK) >> CMIO_STATION_ID_SHIFT);

	/* If we're current selected slave, pull exception */
	if (cmio_status & CMIO_SLAVE_ACTIVE) {
	    printf("\texception sent\n");
	    regs->vmeio_command = 0;	/* disable interrupts */
	    regs->vmeio_cmio_setup |= CMIO_REQ_EXCEPTION;
	}

	/* Figure out appropriate error code */
	cs->cs_buf->b_error = DATIME;	/* ??? good guess */
	vmeio_reset(md);
    }
    splx(s);
    timeout(vmeio_timer, (caddr_t) 0, hz);
}

    
    
#ifdef DEBUG
/*
 * For debugging; 
 * print everything which can safely be printed.
 */
vmeio_printregs(s, regs)
char *s;
register struct vmeio_regs *regs;
{
    register int foo, i, nchan;
 
    printf("\n=======VMEIO register dump (%s)========\n", s);
    printf("\tvmeio status %b\n", regs->vmeio_status, VMEIO_STATUS_BITS);
    printf("\tvmeio command %b\n", regs->vmeio_command, VMEIO_COMMAND_BITS);

    foo = regs->vmeio_setup;
    printf("\tvmeio setup 0x%x <vec 0x%x amod 0x%x br 0x%x level 0x%x>\n",
	   foo,
	   foo & VMEIO_VEC_MASK,
	   (foo & VMEIO_ADDR_MOD_MASK) >> VMEIO_ADDR_MOD_SHIFT,
	   (foo & VMEIO_VME_REQ_MASK) >> VMEIO_VME_REQ_SHIFT,
	   (foo & VMEIO_INTR_LEVEL_MASK) >> VMEIO_INTR_LEVEL_SHIFT);

    printf("\tvme address 0x%x\n\tvme count 0x%x\n",
	   regs->vmeio_vme_addr,
	   regs->vmeio_vme_count);

    printf("\tread pointer 0x%x\n\twrite pointer 0x%x\n\tword count 0x%x\n",
	   regs->vmeio_read_pointer,
	   regs->vmeio_write_pointer,
	   regs->vmeio_word_count);

    foo = regs->vmeio_cmio_status;
    printf("\tcmio status %b, <%s>\n",
	   foo, VMEIO_CMIO_STATUS_BITS,
	   vmeio_bus_phases[(foo & CMIO_BUS_CONTROL_MASK)
			    >> CMIO_BUS_CONTROL_SHIFT]);

    foo = regs->vmeio_cmio_setup;
    printf("\tcmio setup %b\n", foo, VMEIO_CMIO_SETUP_BITS);
    printf("\t\t<speed 0x%x sender 0x%x target 0x%x station 0x%x>\n",
	   (foo & CMIO_SPEED_MASK) >> CMIO_SPEED_SHIFT,
	   (foo & CMIO_SENDER_ID_MASK) >> CMIO_SENDER_ID_SHIFT,
	   (foo & CMIO_TARGET_ID_MASK) >> CMIO_TARGET_ID_SHIFT,
	   (foo & CMIO_STATION_ID_MASK) >> CMIO_STATION_ID_SHIFT);
	   
    printf("============================================================\n");
}
#endif DEBUG

#endif
