[Return to Library]  [TOC]  [PREV]  SECT--  [NEXT]  [INDEX] [Help]

B    VMEbus Device Driver Source Listing

This appendix contains the source listing for the /dev/dmaex device driver, preceded by listings of four associated files:


[Return to Library]  [TOC]  [PREV]  SECT--  [NEXT]  [INDEX] [Help]

B.1    VMEbus /dev/dmaex DM100.list Registration File

The following NAME.list registration file specifies the location of the /dev/dmaex driver product files. (Typically the device driver kit does not include this file.)
# HOSTNAME.list
# Registration file which contains a one line entry for kernel layered products
# The format of each line is as follows. Empty lines are ignored. Any line
# with a '#' as the first character on the line is a comment. Other
# entries have the following fields:
#
# Module Path:Subset ID:Date:Company:Product:Version
#
# This file specifies the pathname to the area where your files are.
# .........................................................................
/usr/examples/devdriver/dm100:


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

B.2    VMEbus /dev/dmaex files File Fragment

The following files file fragment assumes the /dev/dmaex third-party device driver kit has been installed in the /usr/examples/devdriver/dm100 directory:
MODULE/STATIC/dmaex            standard Binary
/usr/examples/devdriver/dm100/dmaex.c        module dmaex


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

B.3    VMEbus /dev/dmaex sysconfigtab File Fragment

The following sysconfigtab file fragment includes the CMA_Option and VBA_Option entries for the /dev/dmaex driver. These entries must be supplied on contiguous lines, each on a single line with no newline characters inserted. (Line breaks have been added to the example listing for clarity.)

The CMA_Option entry provides information that the Digital-provided cma_dd subsystem needs. The cma_dd subsystem allocates physically contiguous memory on behalf of a device driver that is dynamically configured into the system. Refer to Writing Device Drivers: Tutorial and Writing Device Drivers: Reference for information about CMA_Option and the related kernel interfaces contig_malloc and contig_free.

The VBA_Option entry describes the device driver and its VMEbus configuration. See Section 4.3 for a detailed description of the VBA_Option entry.

dmaex:
        Subsystem_Description =DMAEX device driver
        Module_Config_Name = dmaex
        Device_Char_Major = Any
        Device_Char_Minor = 0
        Device_Char_Files = dmaex0
        Device_Major_Req = Same
        CMA_Option = Size - 0x20000, Alignment - 0x10000, Addrlimit - 0,
                     Type - 0x1D, Flag - 2
        VBA_Option = Manufact_Name - 'Digital', Product_Name - 'DMAEX Driver',
                     Bus_Instance - 0, Driver_Name - dmaex, Driver_Instance - 0,
                     Csr1 - 0x00800000, Csr2 - 0x0, Vector - 0xF0,
                     Bus_Priority - 1, Type - C, Adpt_Config - N


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

B.4    VMEbus /dev/dmaex dmaexreg.h Device Register Header File

The following dmaexreg.h header file example is also available on the Digital UNIX Version 4.0B operating system CD-ROM:
/*
 * @DEC_COPYRIGHT@
 */
/*
 * HISTORY
 * $Log: dmaexreg.h,v $
 * Revision 1.1.2.3  1996/10/03  17:41:49  Julie_McGray
 *      Added additional definitions for device driver
 *      ioctls, modes, and contiguous physical memory
 *      constants.
 *      [1996/10/01  18:57:35  Julie_McGray]
 *
 * Revision 1.1.2.2  1996/09/06  16:28:46  Julie_McGray
 *      .h file for dmaex.c and dmaex_test.c example
 *      VME device driver.
 *      [1996/09/03  21:56:20  Julie_McGray]
 * 
 * $EndLog$
 */
/*
 * @(#)$RCSfile: dmaexreg.h,v $ $Revision: 1.1.2.3 $ (DEC)
   $Date: 1996/10/03 17:41:49 $
 */

#include <sys/ioctl.h>

/***************************************************
 * dmaexreg.h   Device Register Header File for    *
 * dmaex.c 09-July-1996                            *
 *                                                 *
 * Device register offsets for DMAEX device        * 
 ***************************************************/

#define DMAEX_CSR_OFF   0       /* One byte control/status register */
#define DMAEX_COUNT_OFF 1       /* Short byte count */
#define DMAEX_ADDR_OFF  4       /* 32-bit VMEbus transfer address */ 

/****************************************************
 * Convenience defines for calls to sizeof operator *
 ****************************************************/

#define DMAEX_CSR_SIZE          sizeof(char)
#define DMAEX_COUNT_SIZE        sizeof(short)
#define DMAEX_ADDR_SIZE         sizeof(vme_addr_t)

/***************************************************
 * Bits for csr device offset register             *
 ***************************************************/

#define  IE         0001   /* Interrupt Enable */
#define  DMA_GO     0002   /* Start DMA */
#define  RESET      0010   /* Ready for data transfer */
#define  ERROR      0020   /* Indicate error */
#define  READ       0040   /* Indicate data transfer is read */

/***************************************************
 *         Define the ioctl macros                 *
 *                                                 *
 * This instructs the kernel on how much data will *
 * be moved between the device driver and the user *
 * program as a result of a call to the ioctl      *
 * interface.  Note they are all read/write in     *
 * this case, meaning that the dmaex_ioctl         *
 * structure parameter can be written into by the  *
 * user code and by the driver itself.             *
 ***************************************************/

struct dmaex_ioctl_data {
  unsigned long data[6];
};

/*
 * PHYS_CONTIG_BUF_SIZE defines the number of bytes
 * of physically contiguous memory that will be allocated
 * by the the driver. This number MUST match the value
 * specified in the CMA_Option of the sysconfigtab file
 * fragment.
 */ 
#define PHYS_CONTIG_BUF_SIZE (128 * 1024)
#define CONTIG_RD_WRT_BUF_SIZE (PHYS_CONTIG_BUF_SIZE / 2) 


/*
 * The following definitions define the data transfer
 * mode to be used by dmaex_read and dmaex_write. These
 * interfaces calls the physio interface which then invokes
 * dmaex_minphys and dmaex_strategy interfaces. The
 * dmaex_strategy interface then performs the data transfer
 * using the specified transfer mode.
 */ 
#define PIO_XFER_MODE   0
#define DEVICE_DMA_MODE 1
#define BLOCK_DMA_MODE  2

/*
 * The following definitions define how dmaex_mmap will
 * interpert the requested mmap call.
 *   MMAP_VME_TO_U_MEM   - This mode causes the dmaex_mmap interface to
 *                         return a kernel page frame number corresponding
 *                         to an outbound mapped VMEbus address.
 *   MMAP_K_TO_U_MEM_WRT - This mode causes the dmaex_mmap interface to
 *                         return a kernel page frame number corresponding
 *                         to the physically contiguous write buffer
 *                         allocated by contig_malloc and the cma_dd interface.
 *   MMAP_K_TO_U_MEM_RD  - This mode causes the dmaex_mmap interface to
 *                         return a kernel page frame number corresponding
 *                         to the physically contiguous read buffer
 *                         allocated by contig_malloc and the cma_dd interface.
 */
#define MMAP_VME_TO_U_MEM   0
#define MMAP_K_TO_U_MEM_WRT 1
#define MMAP_K_TO_U_MEM_RD  2

enum dmaex_commands {  C1, C2, C3, C4, C5, C6, C7,
                       C8, C9, C10, C11, C12, C13,
                       C14, C15, C16, C17, C18, C19,
                       C20, C21, C22, C23, C24, C25,
                       C26, C27, C28 };

#define SET_MMAP_MODE              _IOWR('t', C1,  struct dmaex_ioctl_data)
#define GET_MMAP_MODE              _IOWR('t', C2,  struct dmaex_ioctl_data)
#define SET_STRATEGY_XFER_MODE     _IOWR('t', C3,  struct dmaex_ioctl_data)
#define GET_STRATEGY_XFER_MODE     _IOWR('t', C4,  struct dmaex_ioctl_data)
#define SETUP_VME_FOR_MMAP_PIO     _IOWR('t', C5,  struct dmaex_ioctl_data)
#define GET_VME_INFO_FOR_MMAP_PIO  _IOWR('t', C6,  struct dmaex_ioctl_data)
#define UNMAP_VME_FOR_MMAP_PIO     _IOWR('t', C7,  struct dmaex_ioctl_data)
#define SET_STRATEGY_INFO_BLK_DMA  _IOWR('t', C8,  struct dmaex_ioctl_data)
#define GET_STRATEGY_INFO_BLK_DMA  _IOWR('t', C9,  struct dmaex_ioctl_data)
#define CLR_STRATEGY_INFO_BLK_DMA  _IOWR('t', C10, struct dmaex_ioctl_data)
#define SETUP_VME_FOR_STRATEGY_PIO    _IOWR('t', C11, struct dmaex_ioctl_data)
#define GET_VME_INFO_FOR_STRATEGY_PIO _IOWR('t', C12, struct dmaex_ioctl_data)
#define UNMAP_VME_FOR_STRATEGY_PIO    _IOWR('t', C13, struct dmaex_ioctl_data)
#define MAP_SYS_MEM_TO_VME    _IOWR('t', C14, struct dmaex_ioctl_data)
#define GET_SYS_MEM_INFO      _IOWR('t', C15, struct dmaex_ioctl_data)
#define UNMAP_SYS_MEM_TO_VME  _IOWR('t', C16, struct dmaex_ioctl_data)
#define SETUP_DMA_BLK_WRT     _IOWR('t', C17, struct dmaex_ioctl_data)
#define GET_DMA_BLK_WRT       _IOWR('t', C18, struct dmaex_ioctl_data)
#define DO_DMA_BLK_WRT        _IOWR('t', C19, struct dmaex_ioctl_data)
#define CLR_DMA_BLK_WRT       _IOWR('t', C20, struct dmaex_ioctl_data)
#define SETUP_DMA_BLK_RD      _IOWR('t', C21, struct dmaex_ioctl_data)
#define GET_DMA_BLK_RD        _IOWR('t', C22, struct dmaex_ioctl_data)
#define DO_DMA_BLK_RD         _IOWR('t', C23, struct dmaex_ioctl_data)
#define CLR_DMA_BLK_RD        _IOWR('t', C24, struct dmaex_ioctl_data)
#define SET_INT_HANDLER       _IOWR('t', C25, struct dmaex_ioctl_data)
#define CLR_INT_HANDLER       _IOWR('t', C26, struct dmaex_ioctl_data)
#define VME_POST_IRQ          _IOWR('t', C27, struct dmaex_ioctl_data)
#define VME_CLR_IRQ           _IOWR('t', C28, struct dmaex_ioctl_data)


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

B.5    VMEbus /dev/dmaex dmaex.c Source File

The following dmaex.c source file example is also available on the Digital UNIX Version 4.0B operating system CD-ROM, along with a program named dmaex_test.c that exercises the driver:
/*
 * @DEC_COPYRIGHT@
 */
/*
 * HISTORY
 * $Log: dmaex.c,v $
 * Revision 1.1.2.3  1996/10/03  17:41:45  Julie_McGray
 *      The dmaex_mmap interface caused a system panic on Digital
 *      Alpha VME 2100 system. The mmap driver interface was
 *      dropping bits of the page frame number being returned.
 *
 *      Cleaned up documentation and provided support for additional
 *      VME functionality.
 *      [1996/10/01  19:19:43  Julie_McGray]
 *
 * Revision 1.1.2.2  1996/09/06  16:28:42  Julie_McGray
 *      Changed dmaex_mmap to return int instead of caddr_t.
 * 
 *      Added more documentation comments.
 *      [1996/09/04  17:42:58  Julie_McGray]
 * 
 *      Example VME device driver.
 *      [1996/09/03  22:07:25  Julie_McGray]
 * 
 * $EndLog$
 */
#pragma ident "@(#)$RCSfile: dmaex.c,v $ $Revision: 1.1.2.3 $ (DEC)
               $Date: 1996/10/03 17:41:45 $"

/****************************************************
 * dmaex.c  Driver for dmaex device                 *
 *                                                  *
 * The /dev/dmaex device driver is an example       *
 * driver that supports a fictitious VME `dmaex'    *
 * device.                                          *
 *                                                  *
 * The /dev/dmaex driver shows you how to write a   *
 * a single binary model device driver that can be  *
 * statically or dynamically configured into the    *
 * kernel. The dev/dmaex driver is implemented to   *
 * operate on the VMEbus.                           *
 *                                                  *
 * The /dev/dmaex device driver assumes that a      *
 * device is present at the VMEbus address          *
 * specified in the VBA_Option's Csr1 entry of the  *
 * sysconfigtab file fragment. The Csr1 VMEbus      *
 * address and addr1_size and addr1_atype in the    *
 * driver structure provides information to the     *
 * auto configuration software. This information    *
 * is used by the auto configuration software to    *
 * map system I/O space to VMEbus address space.    *
 * The result of the mapping is an io_handle_t      *
 * that is saved in the controller structure's addr *
 * element and passed to the /dev/dmaex device      *
 * driver's probe interface.                        *
 *                                                  *
 ********************** NOTE ************************
 *                                                  *
 * In the absence of 'dmaex' hardware and to allow  *
 * the driver to be statically or dynamically       *
 * loaded for testing between two single board      *
 * computers, a Dmaex_No_Dev attribute has been     *
 * initialized to indicate that no 'dmaex' device   *
 * hardware is present. The dmaex_no_dev flag will  *
 * be used to skip over BADADDR, read_io_port and   *
 * write_io_port accesses to the fictitious 'dmaex' *
 * device hardware. The flag will be checked in     *
 * dmaex_probe, dmaex_close, dmaex_strategy and     *
 * dmaex_intr1 driver interfaces.                   *
 *                                                  *
 ****************************************************/

/****************************************************
 * The following list highlights some features of   *
 * the /dev/dmaex driver:                           *
 *                                                  *
 ********************** NOTE ************************
 *                                                  *
 * Refer to Digital UNIX V4.0 Writing Device        *
 * Driver:Tutorial for discussions on writing       *
 * device drivers and the single binary module      *
 * technology.                                      *
 *                                                  *
 * Refer to Writing VMEbus device drivers for       *
 * VME specific information.                        *
 *                                                  *
 ****************************************************
 *                                                  *
 * o Single Binary module                           *
 *                                                  *
 *   The /dev/dmaex is written to produce a single  *
 *   driver image. This single driver image has a   *
 *   a file extension of .mod.  You compile a       *
 *   device driver to produce a single binary       *
 *   module that can be statically or dynamically   *
 *   configured into a Digital UNIX kernel at any   *
 *   point in time.                                 *
 *                                                  *
 * o Static Configuration                           *
 *                                                  *
 *   The /dev/dmaex driver's single binary module   *
 *   (the dmaex.mod) can be:                        *
 *                                                  *
 *     -  Statically configured into Digital UNIX   *
 *        kernel (/vmunix).                         *
 *                                                  *
 *     -  Statically configured into a sysconfigtab *
 *        database image when a user boots a        *
 *        Digital UNIX kernel (/vmunix) or when the *
 *        OSFboot links the sysconfigtab image.     *
 *                                                  *
 * o Dynamic configuration                          *
 *                                                  *
 *   The /dev/dmaex driver's single binary module   *
 *   (the dmaex.mod file) can be dynamically        *
 *   configured into a Digital UNIX kernel (/vmunix)*
 *   when the user makes a request (by using the    *
 *   sysconfig utility) at single user or multiuser *
 *   time.                                          *
 *                                                  *
 ****************************************************/

/****************************************************
 *   Interfaces the /dev/dmaex implement            *
 *                                                  *
 * o dmaex_configure                                *
 * o register_configuration                         *
 * o register_major_number                          *
 * o callback_register_configuration                *
 * o callback_register_major_number                 *
 * o dmaex_ctlr_unattach                            *
 *                                                  *
 * o dmaex_probe                                    *
 * o dmaex_cattach                                  *
 * o dmaex_open                                     *
 * o dmaex_close                                    *
 * o dmaex_mmap                                     *
 * o dmaex_ioctl                                    *
 * o dmaex_read                                     *
 * o dmaex_write                                    *
 * o dmaex_minphys                                  *
 * o dmaex_strategy                                 *
 * o dmaex_intr1                                    *
 * o dmaex_intr2                                    *
 * o dmaex_iack_isr                                 *
 * o dmaex_post_signal                              *
 * o dmaex_rcv_int_srv                              *
 ****************************************************/

/****************************************************
 *            Include Files Section                 *
 ****************************************************/

#include        <sys/param.h>
#include        <sys/systm.h>
#include        <sys/ioctl.h>
#include        <sys/tty.h>
#include        <sys/dir.h>
#include        <sys/user.h>
#include        <sys/proc.h>
#include        <sys/map.h>
#include        <sys/buf.h>
#include        <sys/vm.h>
#include        <sys/file.h>
#include        <sys/uio.h>
#include        <sys/types.h>
#include        <sys/errno.h>
#include        <sys/conf.h>
#include        <sys/kernel.h>
#include        <sys/devio.h>
#include        <hal/cpuconf.h>
#include        <sys/exec.h>
#include        <sys/malloc.h>
#include        <kern/event.h>
#include        <io/common/devdriver.h>
#include        <io/common/handler.h>
#include        <sys/sysconfig.h>

/****************************************************
 * If you are writing a device driver to operate on *
 * the VMEbus, you must include the following       *
 * bus-specific header file.                        *
 *                                                  *
 *   #include <io/dec/vme/vbareg.h>                 *
 *                                                  *
 * Because the fictitious DMAEX device controller   *
 * can be connected to a TURBOchannel or PCI bus,   *
 * the tc.h and pci.h files are also included.      *
 * Typically, you will not need these files for     *
 * VMEbus device driver development.                *
 *                                                  *
 *   #include <io/dec/tc/tc.h>                      *
 *   #include <io/dec/pci/pci.h>                    *
 ****************************************************/

#include        <io/dec/tc/tc.h>
#include        <io/dec/pci/pci.h>

#include        <io/dec/vme/vbareg.h> /* VMEbus definitions */
#include        <machine/cpu.h>

#include        "dmaexreg.h"          /* Device register header file */

/****************************************************
 * The /dev/dmaex driver's register_major_number    *
 * interface uses the following #defines when       *
 * obtaining a major number                         *
 ****************************************************/

#define NO_DEV -1
#define MAJOR_INSTANCE 1 

/****************************************************
 *       Data structure sizing approach             *
 ****************************************************
 *                                                  *
 * The following #define will be used to allocate   *
 * data structures needed to the /dev/dmaex driver. *
 * There can be at most 8 instances of the DMAEX    *
 * device controller on the system. This means that *
 * MAX_NDMAEX is the maximum number of controllers  *
 * that the /dev/dmaex can support. This is a small *
 * number of instances of the driver and the data   *
 * structures themselves are not large, so it is    *
 * acceptable to allocate for the maximum con-      *
 * figuration.                                      *
 *                                                  *
 * Note that MAX_NDMAEX is used in the              *
 * dmaex_attribute table, specifically as the       *
 * maximum value for the numunit attribute field.   *
 * The NDMAEX variable is the data value address    *
 * for the numunit attribute field. It is the       *
 * number of controllers to create for this driver. *
 *                                                  *
 ****************************************************/

#define MAX_NDMAEX 8

/****************************************************
 * Declarations Section                             *
 ****************************************************/

/****************************************************
 *     Defines for softc structure members          *
 ****************************************************/


#define  DMAEXOPEN   1
#define  DMAEXCLOSE  0

/****************************************************
 *     Forward declarations of driver interfaces    *
 ****************************************************/

static int     register_configuration();
static int     register_major_number();
static void    callback_register_configuration();
static void    callback_register_major_number();

int     dmaex_configure(),  dmaex_ctlr_unattach(), 
        dmaex_probe(),      dmaex_cattach(),
        dmaex_open(),       dmaex_close(),  dmaex_mmap(),
        dmaex_read(),       dmaex_write(),  dmaex_ioctl(),
        dmaex_minphys(),    dmaex_strategy(),
        dmaex_intr1(),      dmaex_intr2(),
        dmaex_iack_isr(),   dmaex_post_signal(), dmaex_rcv_int_srv();

/****************************************************
 *                                                  *
 * Declare an array of controller structure pointers*
 * MAX_NDMAEX is used as the maximum number of      *
 * controllers that this driver can support.        *
 ****************************************************/

struct controller *dmaex_ctlr[MAX_NDMAEX];

/****************************************************
 * Initialize the driver structure. If the driver   *
 * writer requires the VME auto configuration       *
 * software to map VMEbus address space into kernel *
 * space for the purpose of device register access, *
 * the driver writer must provide a VMEbus address  *
 * in Csr1 of the VBA_Option sysconfigtab file      *
 * fragment and specify a size and VME address      *
 * modifiers in the addr1_size and addr1_atype      *
 * fields of the driver structure. The VME auto     *
 * configuration software will use these combined   *
 * entries to map VMEbus adddress space to kernel   *
 * address space. The result of this mapping will   *
 * be an io_handle_t that the driver will use with  *
 * read_io_port and write_io_port kernel interfaces *
 * to access the device. The io_handle_t for Csr1   *
 * will be passed to the driver's probe interface   *
 * and also saved in the controller structure's     *
 * addr element (ctlr->addr).                       *
 *                                                  *
 * The driver writer can also request the auto      *
 * configuration software to map a second VMEbus    *
 * address space, by specifying a value in Csr2 of  *
 * the VBA_Option sysconfigtab entry and values in  *
 * addr2_size and addr2_atype. The VME auto         *
 * configuration software will perform the          *
 * appropriate mapping and store the io_handle_t in *
 * the controller structure's addr2 element.        *
 ****************************************************/

struct  driver dmaexdriver = {
        dmaex_probe,         /* probe         */
        0,                   /* slave         */
        dmaex_cattach,       /* cattach       */
        0,                   /* dattach       */
        0,                   /* go            */
        0,                   /* addr_list     */
        0,                   /* dev_name      */
        0,                   /* dev_list      */
        "dmaex",             /* ctlr_name     */
        dmaex_ctlr,          /* ctlr_list     */
        0,                   /* xclu          */
        0x8,                 /* addr1_size    */ 
        VME_A24_UDATA_D32,   /* addr1_atype   */
        0,                   /* addr2_size    */
        0,                   /* addr2_atype   */
        dmaex_ctlr_unattach, /* ctlr_unattach */
        0                    /* dev_unattach  */
}; 

/****************************************************
 * Declare softc structure to allow the /dec/dmaex  *
 * device driver's interface to share data.         *
 ****************************************************/

struct dmaex_softc {
        int            sc_open;            /* DMAEXOPEN, DMAEXCLOSE          */
        io_handle_t    csr_handle;         /* Device CSR I/O handle          */
        event_t        isi_event;          /* Used by device ISI interrupts  */
        event_t        post_iack_event[8]; /* Used to acknowledge posted IRQs*/

        dma_handle_t   dma_handle;         /* handle used by dmaex_strategy  */

                                           /* Entries for mmap support       */
        io_handle_t    pio_handle;         /* I/O handle for VMEbus mapping  */
        vme_addr_t     pio_vme_addr;       /* VMEbus address                 */
        vme_atype_t    pio_vme_am;         /* VMEbus address modifiers       */
        unsigned int   pio_vme_size;       /* VMEbus byte count              */
        unsigned int   pio_access_type;    /* Access type                    */

                                           /* Strategy block mode DMA entries*/
        int            dma_is_set;         /* Flag for DMA transfers         */
        vme_addr_t     dma_vme_addr;       /* DMA VMEbus address             */
        vme_atype_t    dma_vme_am;         /* DMA VMEbus address modifiers   */
        unsigned int   dma_vme_size;       /* DMA VMEbus byte count          */
        vme_addr_t     dma_wrk_vme_addr;   /* Updated DMA bus address        */
        unsigned int   dma_wrk_vme_size;   /* Updated transfer byte count    */

        int            strategy_xfer_mode; /* Strategy transfer mode         */
        int            mmap_mode;          /* mmap mapping mode              */

                                           /* Strategy PIO entries           */
        io_handle_t    vme_handle;         /* I/O handle for VMEbus mapping  */
        vme_addr_t     vme_addr;           /* VMEbus address                 */
        vme_atype_t    vme_am;             /* VMEbus address modifiers       */
        unsigned int   vme_size;           /* VMEbus byte count              */
        io_handle_t    vme_wrk_handle;     /* Updated VME io_handle_t        */
        unsigned int   vme_wrk_size;       /* Updated transfer byte count    */

        dma_handle_t   sys_mem_dma_handle; /* Mapped system memory DMA handle*/
        vme_addr_t     sys_mem_vme_addr;   /* VMEbus address for sys memory  */
        vme_atype_t    sys_mem_vme_am;     /* VMEbus address modifiers       */
        unsigned int   sys_mem_vme_size;   /* VMEbus/system memory byte count*/
        caddr_t        sys_mem_vir_addr;   /* System memory virtual address  */

        vme_addr_t     blk_wrt_vme_addr;   /* MBLT write VMEbus address      */
        vme_atype_t    blk_wrt_vme_am;     /* MBLT write VMEbus addr mod     */
        unsigned int   blk_wrt_vme_size;   /* MBLT write VMEbus byte count   */
        dma_handle_t   blk_wrt_dma_handle; /* MBLT write DMA handle          */
        caddr_t        blk_wrt_buf_ptr;    /* MBLT write memory buffer       */
        struct proc   *blk_wrt_proc_p;     /* process pointer for write      */

        vme_addr_t     blk_rd_vme_addr;    /* MBLT read VMEbus address       */
        vme_atype_t    blk_rd_vme_am;      /* MBLT read VMEbus addr mod      */
        unsigned int   blk_rd_vme_size;    /* MBLT read VMEbus byte count    */
        dma_handle_t   blk_rd_dma_handle;  /* MBLT read DMA handle           */
        caddr_t        blk_rd_buf_ptr;     /* MBLT read memory buffer        */
        struct proc   *blk_rd_proc_p;      /* process pointer for read       */

        ihandler_id_t *rcvisr_id_t;        /* handler ID for VME receive ISI */
        struct proc   *rcvisr_proc_p;      /* isr user task process pointer  */
        int           rcvisr_irq;          /* VMEbus irq for recive ISI      */

        struct buf     io_buf;             /* read/write i/o request buffer  */
} dmaex_softc[MAX_NDMAEX]; 

int dmaex_config = FALSE;  /* state flag indicating driver configured */
int dmaex_devno  = NO_DEV; /* No major number assigned yet */

/****************************************************
 * The handler_add interface is used to register    *
 * the interrupt handler for the dev/dmaex driver.  *
 * the dmaex_id_t array contains IDs that are used  *
 * to enable, disable and deregister the interrupt  *
 * handlers.                                        *
 *                                                  *
 * This fictitious device driver needs to install   *
 * two interrupts per controller. The device can    *
 * present two unique interrupts to the system.     *
 ****************************************************/

#define NINTS_PER_DMAEX 2
ihandler_id_t *dmaex_id_t[MAX_NDMAEX][NINTS_PER_DMAEX]; /* handler ID's */

/****************************************************
 * The following list describes the use of the      *
 * dmaex_is_dynamic, dmaexcallback_return_status,   *
 * and num_dmaex variables.                         *
 *                                                  *
 * o dmaex_is_dynamic                               *
 *                                                  *
 *   The dmaex_is_dynamic variable will be used to  *
 *   control any differences in tasks performed by  *
 *   statically or dynamically configured           *
 *   /dev/dmaex driver.  Implementing a device      *
 *   driver to handle a static or dynamic           *
 *   configuration request gives customers maximum  *
 *   flexibility in how they configure the driver   *
 *   into the kernel.                               *
 *                                                  *
 * o dmaexcallback_return_status                    *
 *                                                  *
 *   The callback return status variable is used by *
 *   the callback_register_major_number interface   *
 *   to determine if a previous failure has occurred*
 *   in statically configuring the /dev/dmaex       *
 *   driver.                                        *
 *                                                  *
 * o num_dmaex                                      *
 *                                                  *
 *   The num_dmaex variable is used to count the    *
 *   number of controllers probed. The dmaex_probe  *
 *   interface increments this variable each time   *
 *   it probes a DMAEX controller.                  *
 ****************************************************/

int num_dmaex    = 0;
int dmaex_is_dynamic = 0;
int dmaexcallback_return_status = ESUCCESS;

/****************************************************
 * External function references. These are needed   *
 * for the devsw declaration.                       *
 ****************************************************/
extern int nodev();

/****************************************************
 * External reference needed for event_wait timeout *
 ****************************************************/
extern int hz;            /* The value for hz is defined in param.c */

/****************************************************
 * Typically, third-party device drivers specify    *
 * the bus-specific option data in a sysconfigtab   *
 * file fragment. See Writing Device Drivers:       *
 * Tutorial and the bus specific books for informa- *
 * tion on the syntax associated with VMEbus option *
 * data attribute fields (VBA_Option).              * 
 ****************************************************/

/****************************************************
 * The following code sets the members of the       *
 * dsent structure to appropriate values for the    *
 * dev/dmaex driver.  The address of this structure *
 * is passed to the devsw_add interface, which      *
 * registers the I/O services interfaces in the     *
 * dsent table and reserves a major number for the  *
 * /dev/dmaex driver.
 ****************************************************/

struct dsent dmaex_devsw_entry = {
        dmaex_open,       /* d_open */
        dmaex_close,      /* d_close */
        dmaex_strategy,   /* d_strategy */
        dmaex_read,       /* d_read */
        dmaex_write,      /* d_write */
        dmaex_ioctl,      /* d_ioctl */
        nodev,            /* d_dump */
        nodev,            /* d_psize */
        nodev,            /* d_stop */
        nodev,            /* d_reset */
        nodev,            /* d_select */
        dmaex_mmap,       /* d_mmap */
        0,                /* d_segmap */
        NULL,             /* d_ttys */
        DEV_FUNNEL_NULL,  /* d_funnel */
        0,                /* d_bflags */
        0,                /* d_cflags */
}; 

/****************************************************
 *                   NOTE                           *
 *--------------------------------------------------*
 *                                                  *
 * All VMEbus device drivers MUST specify "vba" as  *
 * the bus name. This constant is passed to the     *
 * configure driver interface, which the /dev/dmaex *
 * driver's dmaex_configure interface calls.        * 
 *                                                  *
 ****************************************************/

#define DMAEX_BUSNAME1 "vba"

/****************************************************
 * The following code declares variables that are   *
 * required by the cfgmgr framework's driver        *
 * method. You use these variables as fields in     *
 * the driver's attribute table.  The attribute     *
 * table for the /dev/dmaex driver is called        *
 * dmaex_attributes.                                *
 ****************************************************/

static int   NDMAEX  = 1;  /* Number of controllers to create for driver */
static int   majnum  = NO_DEV;
static int   begunit = 0;
static int   dmaex_version = 1;
static int   dmaex_no_dev = 1;
static int   dmaex_int_wait = 1;
static u_int dmaex_max_dma = (1024 * 1024);
static int   dmaex_developer_debug = 0;
static char  *dmaex_contig_buf = NULL;
static char  *dmaex_contig_rd_buf = NULL;
static char  *dmaex_contig_wrt_buf = NULL;
static unsigned char subsysname[CFG_ATTR_NAME_SZ] = "";
static unsigned char mcfgname[CFG_ATTR_NAME_SZ] = ""; 
static unsigned char devmajor[CFG_ATTR_NAME_SZ] = "";
static unsigned char unused[300] = "";
static unsigned char cma_dd[120] = "";
static unsigned char vba_option[300] = "";

/****************************************************
 * Setup dmaex device driver debugging macros.      *
 *                                                  *
 * dmaex_developer_debug = 0 - no debug messages    *
 * dmaex_developer_debug = 1 - level 1 messages     *
 * dmaex_developer_debug = 2 - level 2 messages     *
 * dmaex_developer_debug = 3 - level 1 & 2 messages *
 ****************************************************/

#define DMAEX_DEBUG

#ifdef DMAEX_DEBUG
#define DMAEX_DBG1  if (dmaex_developer_debug & 0x01) printf
#define DMAEX_DBG2  if (dmaex_developer_debug & 0x02) printf
#else
#define DMAEX_DBG1 ;
#define DMAEX_DBG2 ;
#endif

/*****************************************************
 * The cfgmgr framework uses the driver's attribute  *
 * table (a structure of type cfg_subsys_attr_t)     *
 * during static and dynamic configuration of a      *
 * device driver into the kernel.  The cfgmgr        *
 * framework initializes the attributes table early  *
 * in the kernel boot process and whenever it        *
 * receives a sysconfig command request from         *
 * either the kernel or the user command line.       *
 *                                                   *
 *        NOTE                                       *
 *                                                   *
 * There are a number of fields (attributes) that    *
 * appear in the sysconfigtab file fragment that     *
 * that do not appear in the device driver's         *
 * attribute table.  The cfgmgr framework's device   *
 * driver method consumes these fields (attributes)  *
 * to perform kernel configuration work and to make  *
 * device special files. To avoid warning messages   *
 * from the cfgmgr framework, you should add these   *
 * attributes to the driver's attribute table        *
 *****************************************************/

/*****************************************************
 * Declares an array of cfg_subsys_attr_t structures *
 * and calls it dmaex_attributes.  The device driver *
 * method of cfgmgr fills in the elements in         *
 * dmaex_attributes because the operation types      *
 * designated by these are CFG_OP_CONFIGURE.  The    *
 * device driver method of cfgmgr reads the entries  *
 * for the /dev/dmaex driver from the sysconfigtab   *
 * database.  (A sysconfigtab file fragment was      *
 * created for the /dev/dmaex driver.                *
 * The sysconfigdb utility appends this sysconfigtab *
 * file fragment to the /etc/sysconfigtab database.) *
 *                                                   *
 * To determine if any of these element reads        *
 * failed, the /dev/dmaex driver verifies each from  *
 * the cfg_attr_t structure passed into the          *
 * dmaex_configure interface.                        *
 *                                                   *
 * Several of the following fields are used in the   *
 * /dev/dmaex device driver while other fields exist *
 * to illustrate what can be loaded by cfgmgr into   *
 * the driver attributes table.                      *
 *                                                   *
 * These fields can represent tunable parameters to  *
 * indicate whether the driver is statically or      *
 * dynamically configured into the kernel.           *
 *****************************************************/

cfg_subsys_attr_t dmaex_attributes[] = { 

/* Fields used in this driver */
{"Subsystem_Description",CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)subsysname,0,CFG_ATTR_NAME_SZ,0},
{"Module_Config_Name",  CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)mcfgname,2,CFG_ATTR_NAME_SZ,0},
{"Device_Char_Major",   CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)devmajor,0,CFG_ATTR_NAME_SZ,0},
{"Device_Char_Minor",   CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)unused,0,300,0},
{"Device_Char_Files",   CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)unused,0,300,0},
{"Device_Major_Req",    CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)unused,0,300,0},
{"Device_User",         CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)unused,0,300,0},
{"Device_Group",        CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)unused,0,300,0},
{"Device_Mode",         CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)unused,0,300,0},
{"CMA_Option",          CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)cma_dd,0,120,0},
{"numunit",              CFG_ATTR_INTTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                        (caddr_t)&NDMAEX,0,MAX_NDMAEX,0},
{"VBA_Option",          CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)vba_option,0,300,0},

/*****************************************************
 * The /dev/dmaex device driver modifies the         *
 * following attributes during a configure or        *
 * unconfigure operation.  The cfgmgr framework's    *
 * device driver method uses these attributes to     *
 * provide the device special files that device      * 
 * drivers need.                                     *
 *****************************************************/

{"majnum",                 CFG_ATTR_INTTYPE, CFG_OP_QUERY,
                           (caddr_t)&majnum,0,512,0},
{"begunit",                CFG_ATTR_INTTYPE, CFG_OP_QUERY,
                           (caddr_t)&begunit,0,8,0},
{"Dmaex_version",          CFG_ATTR_INTTYPE, CFG_OP_QUERY,
                           (caddr_t)&dmaex_version,0,9999999,0},
{"Dmaex_No_Dev",           CFG_ATTR_INTTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
                           (caddr_t)&dmaex_no_dev,0,1,0},
{"Dmaex_Int_Wait",         CFG_ATTR_INTTYPE, CFG_OP_QUERY |
                           CFG_OP_RECONFIGURE | CFG_OP_CONFIGURE,
                           (caddr_t)&dmaex_int_wait,1,9999999,0},
{"Dmaex_Max_DMA",          CFG_ATTR_UINTTYPE, CFG_OP_QUERY |
                           CFG_OP_RECONFIGURE | CFG_OP_CONFIGURE,
                           (caddr_t)&dmaex_max_dma,8,0xFFFFFFFF,0},
{"Dmaex_Contig_Mem",       CFG_ATTR_ULONGTYPE,CFG_OP_CONFIGURE | CFG_OP_QUERY,
                           (caddr_t)&dmaex_contig_buf,0,0xFFFFFFFFFFFFFFFF,0},
{"Dmaex_Developer_Debug",  CFG_ATTR_INTTYPE, CFG_OP_QUERY |
                           CFG_OP_RECONFIGURE | CFG_OP_CONFIGURE,
                           (caddr_t)&dmaex_developer_debug,0,7,0},
{"",0,0,0,0,0,0}
};

/*****************************************************
 *           dmaex_configure()                       *
 *****************************************************
 * Name:       dmaex_configure                       *
 *                                                   *
 * Arguments:                                        *
 *                                                   *
 * op          Configure operation                   *
 * indata      Input data structure, cfg_attr_t      *
 * indatalen   Size of input data structure          *
 * outdata     Formal parameter not used             *
 * outdatalen  Formal parameter not used             *
 *                                                   *
 * Called by:                                        *
 *                                                   *
 *             o cfgmgr framework                    *
 *                                                   *
 * Return codes:                                     *
 *                                                   *
 *   Success   ESUCCESS                              *
 *                                                   *
 *   Failure   EBUSY                                 *
 *             EINVAL                                *
 *             ESRCH                                 *
 *             ENOTSUP                               *
 * Description:                                      *
 *                                                   *
 * The dmaex_configure interface is called           *
 * indirectly by the cfgmgr framework.  The cfgmgr   *
 * framework is responsible for calling all single   *
 * binary modules for registration and integration   *
 * into the kernel.  The cfgmgr framework requires   *
 * that a single binary module has both an           *
 * attributes table and a configure interface before *
 * it (the single binary module) can be registered   *
 * as part of the cfgmgr framework and the Digital   *
 * UNIX kernel.                                      *
 *                                                   *
 * The dmaex_configure cooperates with the cfgmgr    *
 * framework to complete configure, unconfigure,     *
 * query, and other requests. These request are      *
 * differentiated by the "op" arguments.             *
 *                                                   *
 * Specifically, the dmaex_configure interface shows *
 * the code the device driver must supply to produce *
 * a single binary module. This code allows the      *
 * /dev/dmaex driver to be statically configured     *
 * into the kernel.                                  *
 *****************************************************/

int
dmaex_configure(cfg_op_t   op,
                cfg_attr_t *indata,
                size_t     indatalen,
                cfg_attr_t *outdata,
                size_t     outdatalen)
{
  /*****************************************************
   * The dmaex_configure interface declares the        *
   * following variables:                              *
   *                                                   *
   * i                 Used for loop control           *
   * driver_cfg_state  Stores the configuration state  *
   *                   (either static or dynamic)      *
   *****************************************************/

  int     retval, i;
  int     driver_cfg_state;

  /***************************************************
   * MAX_DEVICE_CFG_ENTRIES represents maximum       *
   * number of attributes that the cfgmgr frameworks *
   * configures on behalf of the devie driver.       *
   ***************************************************/

#define MAX_DEVICE_CFG_ENTRIES 19

  /***************************************************
   * The cfg_attr_t list passed into the             *
   * dmaex_configure interface's indata argument     *
   * contains the strings that are stored in the     *
   * sysconfigtab database for this subsystem (the   *
   * /dev/dmaex driver).                             *
   ***************************************************/

  cfg_attr_t cfg_buf[MAX_DEVICE_CFG_ENTRIES]; 

  switch (op) {

    /***************************************************
     *            Configure (load) the driver.         *
     ***************************************************/

  case CFG_OP_CONFIGURE: 

    DMAEX_DBG1("dmaex_configure: CFG_OP_CONFIGURE.\n");

    /***************************************************
     *      Pass Attributes Verification               *
     *                                                 *
     * Attributes passed through the cfg_attr_t        *
     * structure are not known to be valid until the   *
     * /dev/dmaex driver can check their status in     *
     * the indata argument.  A status other than       *
     * CFG_FRAME_SUCCESS is an error condition.  You   *
     * can use this code to:                           *
     *                                                 *
     *  o Check cfgmgr loading problems with the       *
     *    dmaex_attributes (struct type                *
     *    cfg_subsys_attr_t).                          *
     *                                                 *
     *  o Display the contents and status of the       *
     *    attributes in the sysconfigtab database for  *
     *    the /dev/dmaex driver                        *
     *                                                 *
     *  o Report cfgmgr's status that indicates a      *
     *    failure to load any of the attribute fields  *
     *    into the dmaex_attributes structure.         *
     ***************************************************/

    bcopy(indata, cfg_buf[0].name,
          indatalen * (sizeof(cfg_attr_t)));

    for( i = 0; i < indatalen; i++)
      switch(cfg_buf[i].type) {
        case CFG_ATTR_STRTYPE:
          break;
        default:
          switch(cfg_buf[i].status) {
            case CFG_FRAME_SUCCESS:
              break;
            default:
              printf("%s:",cfg_buf[i].name);
              switch(cfg_buf[i].status) {
                case CFG_ATTR_EEXISTS:
                  printf("**Attribute does not exist\n");
                  break;
                case CFG_ATTR_EOP:
                  printf("**Attribute does not support operation\n");
                  break;
                case CFG_ATTR_ESUBSYS:
                  printf("**Subsystem Failure\n");
                  break;
                case CFG_ATTR_ESMALL:
                  printf("**Attribute size/value too small\n");
                  break;
                case CFG_ATTR_ELARGE:
                  printf("**Attribute size/value too large\n");
                  break;
                case CFG_ATTR_ETYPE:
                  printf("**Attribute invalid type\n");
                  break;
                case CFG_ATTR_EINDEX:
                  printf("**Attribute invalid index\n");
                  break;
                case CFG_ATTR_EMEM:
                  printf("**Attribute memory allocation error\n");
                  break;
                default:
                  printf("**Unknown attribute: ");
                  printf("%x\n", cfg_buf[i].status);
              }
              /*
               * An error status has been detected
               *   return an error of invalid
               */
             return(EINVAL);
          }
          break;
      }

    /*****************************************************
     * If this device driver has already been configured *
     * either statically or dynamically then return this *
     * dmaex_configure call indicating an error when the *
     * cfgmgr framework calls the CFG_OP_CONFIGURE entry *
     * point of the driver's configure interface.        *
     *****************************************************/

    if(dmaex_config == TRUE) {
      printf("dmaex_configure: already configured\n");
      return(EINVAL); 
    }

    /***************************************************
     * The following code performs a check on the      *
     * device driver name that the cfgmgr framework    *
     * uses to statically or dynamically configure the *
     * driver into the kernel.  If the device driver   *
     * name is NULL, subsequent code uses the          *
     * controller name stored in driver structure's    *
     * ctlr_name member.  The driver structure for the *
     * /dev/dmaex driver is called dmaexdriver. This   *
     * structure was declared and initialized to       *
     * appropriate values in the Declarations Section  *
     * of the /dev/dmaex driver.                       *
     *                                                 *
     * The name specified in the ctlr_name member will *
     * be replaced if the mcfgname variable is not     *
     * NULL.  The value stored in mcfgname supersedes  *
     * the controller name stored in ctlr_name during  *
     * configuration of the driver.                    * 
     ***************************************************/

    if ( strcmp(mcfgname,"") == 0 ) { 
      /*
       * If the operator is interested in knowing
       * the configuration name of this driver,
       * you can set this attribute to CFG_OP_QUERY in
       * the driver's cfg_subsys_attr_t structure.
       *
       * The mcfgname variable is important because
       * this name is later matched to the driver
       * name in the Driver_Name field of the option
       * data attribute of a specific bus. This
       * mechanism is how a hardware device is matched
       * to its associated driver.
       */
      strcpy(mcfgname,"dmaex");
    } else { 
      /*
       * mcfgname from sysconfigtab is used to configure
       * the device driver in the following calls to the
       * configure_driver interface.
       */
    }

    if (cfgmgr_get_state(mcfgname, &driver_cfg_state) != ESUCCESS) {
      /*
       * CFGMGR fatal error determining the state of the "dmaex" subsystem
       */
      printf("dmaex_configure: cfgmgr_get_state failed\n");
      return(EINVAL);
    }

    if (driver_cfg_state == SUBSYSTEM_STATICALLY_CONFIGURED)  {
      /*
       * During Static cfgmgr configuration subsystem callbacks 
       * scheduled to run in the boot path have no way to determine
       * if the previous callbacks worked. This  global flag is looked
       * at in each of the callback jackets to determine if this
       * subsystem should still be configured in the boot path.
       */

      dmaexcallback_return_status = ESUCCESS;

      /*
       * During Static configuration the cfgmgr schedules work
       * to be done at pre and post autoconfiguration but it
       * does does not actualy know at this time whether a subsystem
       * will successfull configure into a kernel. The callbacks
       * are responsible for determining the status of a subsystems
       * configuration in the boot path and reporting failure to
       * framework via the cfgmgr_set_state() call.
       */

      dmaex_is_dynamic = SUBSYSTEM_STATICALLY_CONFIGURED;

      register_callback( callback_register_configuration,
                         CFG_PT_PRECONFIG, CFG_ORD_NOMINAL, (long) 0 );

      register_callback( callback_register_major_number,
                         CFG_PT_POSTCONFIG, CFG_ORD_NOMINAL, (long) 0 );
    } else {
      /* 
       * Dynamic Loadable configuration
       */
      DMAEX_DBG1("dmaex_configure: CFG_OP_CONFIGURE - Dynamic loadable\n");

      dmaex_is_dynamic = SUBSYSTEM_DYNAMICALLY_CONFIGURED;

      retval = register_configuration();
      if (retval != ESUCCESS) {
        printf("dmaex_configure: register_configuration error status = 0x%x\n",
               retval);
        return(retval);
      }

      /*
       * Configure this driver into the system
       */
      retval = configure_driver(mcfgname, DRIVER_WILDNUM, DMAEX_BUSNAME1,
                                &dmaexdriver);
      if (retval != ESUCCESS) {
        printf("dmaex_configure: configure_driver error status = 0x%x\n",
               retval);
        return(retval);
      }

      /*
       * Register the major number with the system
       */
      retval = register_major_number();
      if (retval != ESUCCESS) {
        printf("dmaex_configure: register_major_number error status = 0x%x\n",
               retval);
        return(retval);
      }
    }

    break;

    /***************************************************
     * Unconfigure (unload) the driver.                *
     ***************************************************/

  case CFG_OP_UNCONFIGURE: 

    DMAEX_DBG1("dmaex_configure: CFG_OP_UNCONFIGURE.\n");

    /***************************************************
     * Return ENOTSUP if the driver is not currently   *
     * dynamically configured. A statically configured *
     * driver CANNOT be unconfigured/unloaded.         *
     *                                                 *
     * Static drivers will all physically remain in    *
     * kernel whether or not they are configured.      *
     * Dynamic drivers when they are unconfigured are  *
     * also unloaded from the kernel.                  *
     ***************************************************/

    if(dmaex_is_dynamic == SUBSYSTEM_STATICALLY_CONFIGURED) {
      return(ENOTSUP);
    } 


    /***************************************************
     *         Do not allow the driver to be unloaded  *
     *         if it is currently active. To see if    *
     *         the driver is active look to see if     *
     *         any users have the device open.         *
     ***************************************************/

    for (i = 0; i < num_dmaex; i++) {
      if (dmaex_softc[i].sc_open != 0) {
        return(EBUSY);
      }
    }

    /***************************************************
     *       Call devsw_del to remove the driver       *
     *       entry points from the in-memory resident  *
     *       cdevsw table.  This is done prior to      *
     *       deleting the loadable configuration       *
     *       and handlers to prevent users from        *
     *       accessing the device in the middle of     *
     *       deconfigure operation.                    *
     ***************************************************/

    retval = devsw_del(mcfgname, MAJOR_INSTANCE);
    if (retval == NO_DEV) {
      printf("dmaex_configure: devsw_del failed - error status 0x%x\n",
             retval);
      return(ESRCH);
    }

    DMAEX_DBG1("dmaex_configure: CFG_OP_UNCONFIGURE: devsw_del = %d\n",retval);

    /***************************************************
     *       Deregister the driver's configuration     *
     *       data structures from the hardware         *
     *       topology and cause the interrupt handlers *
     *       to be deleted.                            *
     ***************************************************/

    /***************************************************
     *       The bus number is wildcarded to           *
     *       deregister on all instances of the vba    *
     *       bus.  The controller name and number are  *
     *       wildcarded.  This causes all instances    *
     *       that match the specified driver structure *
     *       to be deregistered.  Through the          *
     *       bus-specific code, this interface results *
     *       in a call to the dmaex_ctlr_unattach      *
     *       interface for each instance of the        *
     *       controller.                               *
     ***************************************************/

    retval = unconfigure_driver(DMAEX_BUSNAME1,DRIVER_WILDNUM,&dmaexdriver,
                                mcfgname,DRIVER_WILDNUM);
    if (retval != 0) {
      printf("dmaex_configure: unconfigure driver failed - status 0x%x\n",
             retval);
      return(ESRCH);
    }

    num_dmaex        = 0;
    dmaex_is_dynamic = 0;
    dmaex_config     = FALSE;
    break;

    /**************************************************
     *  Requests to query a loadable subsystem will   *
     *  only succeed if the  CFG_OP_QUERY: entry      *
     *  point returns success.                        *
     **************************************************/

  case CFG_OP_QUERY: 
    DMAEX_DBG1("dmaex_configure: CFG_OP_QUERY.\n");
    break;

    /**************************************************
     *  Code to perform the tasks associated with a   *
     *  system manager request to reconfigure the     *
     *  currently loaded device driver.               *
     **************************************************/

  case CFG_OP_RECONFIGURE: 

    DMAEX_DBG1("dmaex_configure: CFG_OP_RECONFIGURE.\n");
    break;

  default:
    printf("**Unknown operation type\n");
    return(ENOTSUP); 
  }

  /***************************************************
   *       The driver's configure interface has      *
   *       completed successfully.  Return a success *
   *       status.                                   *
   ***************************************************/
  
  return(ESUCCESS); 
}

/***************************************************
 *       register_configuration()                  *
 ***************************************************
 * Name:    register_configuration                 *
 *                                                 *
 * Arguments:   None                               *
 *                                                 *
 * Kernel support interface calls:                 *
 *                                                 *
 *      o create_controller_struct                 *
 *      o create_device_struct                     *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 *      o During dynamic configuration             *
 *          o dmaex_configure                      *
 *                                                 *
 *      o During static configuration              *
 *          o cfgmgr framework callback            *
 *            This interface registered with the   *
 *            cfgmgr to be called at a specific    *
 *            point of the configuration process.  *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Failure:        ENOMEM                     *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * The register_configuration interface is         *
 * responsible for registering the controller and  *
 * and device information for the /dev/dmaex       *
 * driver. The dynamically configured /dev/dmaex   *
 * driver directly calls the register_configuration*
 * interface.  The statically configured /dev/dmaex*
 * driver calls register_configuration through a   *
 * register callback called callback_register_     *
 * configuration.  (The dmaex_configure interface  *
 * calls the register_callback interface to        *
 * register the register_configuration interface   *
 * so that it can be called at some later time.    *
 *                                                 *
 * Writing an interface similar to                 *
 * register_configuration is the method for        *
 * specifying the controller and device information*
 * associated with a device driver.                *
 *                                                 *
 * The register_configuration interface causes     *
 * the appropriate controller and device structures*
 * to be created and integrated into the system    *
 * (hardware) configuration tree for statically and*
 * dynamically configured drivers.                 *
 *                                                 *
 * This method requires device driver writers to   *
 * determine how much operator control should be   * 
 * allowed over the system's topology creation     *
 * support code.  If appropriate, driver writers   *
 * can define sysconfigtab database fields that    *
 * will allow  the operator to control what        *
 * hardware-related information that this          *
 * interface passes to the appropriate structures. *
 ***************************************************/

static int
register_configuration()
{
  /***************************************************
   * Declare controller_config and device_config     *
   * structures and structure pointers.  These       *
   * structures are the storage mechanism for        *
   * populating the controller and device structures *
   * associated with a specific device driver.       * 
   ***************************************************/

  struct controller_config ctlr_register; 
  struct controller_config *ctlr_register_ptr = &ctlr_register;
  struct device_config     device_register;
  struct device_config     *device_register_ptr = &device_register;
  int                      i,status; 

  DMAEX_DBG1("dmaex: register_configuration:\n");

  /***************************************************
   * Register a controller structure for the         *
   * /dev/dmaex driver. The system manager can       *
   * specify attribute fields for the dmaex: entry   *
   * in /etc/sysconfigtab database that determine    *
   * the number of controller structures to be       *
   * created. It is here that the user-configurable  *
   * attribute NDMAEX would be used to regulate the  *
   * number of controller structures to be created   *
   * up to MAX_NDMAEX value defined by the driver.   *
   ***************************************************/

  for (i = 0; i < NDMAEX; i++) {

    ctlr_register_ptr->revision = CTLR_CONFIG_REVISION; 
    strcpy(ctlr_register_ptr->subsystem_name, mcfgname); 
    strcpy(ctlr_register_ptr->bus_name,DMAEX_BUSNAME1); 
    ctlr_register_ptr->devdriver = &dmaexdriver; 

    /***************************************************
     * Call the create_controller_struct to create the *
     * controller structure associated with the        *
     * /dev/dmaex driver.                              *
     ***************************************************/

    status = create_controller_struct(ctlr_register_ptr); 
    if(status != ESUCCESS) {
      printf("register_configuration: create_controller_struct failed\n");
      return (status);
    }

    /***************************************************
     * Register a device structure for the /dev/dmaex  *
     * driver.  This driver does not require a device  *
     * structure but the code is included here for     *
     * reference and is commented out                  *
     ***************************************************/
/*
    device_register_ptr->revision = DEVICE_CONFIG_REVISION;
    strcpy(device_register_ptr->device_type,"disk");
    strcpy(device_register_ptr->device_name,"dmaexdev");
    strcpy(device_register_ptr->controller_name,mcfgname);
    device_register_ptr->phys_unit_num       = 0;
    device_register_ptr->logical_unit_number = 0;
    device_register_ptr->controller_num      = i;
    status = create_device_struct(device_register_ptr);
    if (status != ESUCCESS) {
      printf("register_configuration: create_device_struct failed - 0x%x\n",
             status);
      return(status);
    }
*/
  }

  /***************************************************
   * Request a 128kb physically contiguous buffer    *
   * aligned to a 64kb boundry. This buffer will be  *
   * divided into half. The first half will be used  *
   * to perform DMA block writes with the VME        *
   * adapters DMA engine. The second half will be    *
   * used to perform DMA block mode reads with the   *
   * VME adapters DMA engine.  Dynamically loaded    *
   * drivers specify the size and alignment of the   *
   * physically contiguous buffer using the          *
   * sysconfigtab file fragment's CMA_Option entry.  *
   * The kernel interface, contig_malloc, will       *
   * return a pointer to the physically contiguous   *
   * memory buffer. Statically loaded drivers will   *
   * also be able to obtain a pointer to a physically*
   * contiguous buffer using the contig_malloc       *
   * interface.                                      * 
   ***************************************************/
  dmaex_contig_buf = (char *)contig_malloc(PHYS_CONTIG_BUF_SIZE, 0x10000, 0,
                                           M_DEVBUF, M_ZERO | M_WAITOK);
  if (!(dmaex_contig_buf))
    printf("register_configuration: No physically contig memory allocated\n");
  else {
    dmaex_contig_wrt_buf = dmaex_contig_buf;
    dmaex_contig_rd_buf  = dmaex_contig_buf + CONTIG_RD_WRT_BUF_SIZE;
  }
  return(ESUCCESS);
}

/***************************************************
 *        register_major_number()                  *
 ***************************************************
 * Name:  register_major_number                    *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 * Kernel support interface calls:                 *
 *                                                 *
 *      o devsw_add                                *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 *          o cfgmgr framework                     *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        ESUCCESS                   *
 *                                                 *
 *      Failure:        ENODEV                     *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * The register_major_number interface registers   *
 * a drivers I/O services interface (and other     *
 * information) and receives a major number. It    *
 * accomplishes these tasks by calling devsw_add   *
 * interface.                                      *
 ***************************************************/

static int
register_major_number()
{
  /***************************************************
   * If there are no devices present in the system   *
   * after driver configuration completes, set the   *
   * cfgmgr framework status to unconfigured and     *
   * exit the callback                               *
   ***************************************************/

  DMAEX_DBG1("dmaex: register_major_num:\n");

  if (num_dmaex == 0) {
    printf("register_major_number: num_dmaex not updated by probe\n");
    return (ENODEV);
  }

  /***************************************************
   * Call devsw_add interface to register the        *
   * driver's I/O services interfaces (and other     *
   * information) and to reserve a major number in   *
   * the device switch table.  Each allocation       *
   * provides a block and character entry.           *
   ***************************************************/ 

  majnum = devsw_add(mcfgname, MAJOR_INSTANCE, 
                     majnum, &dmaex_devsw_entry); 

  /***************************************************
   * call to devsw_add could fail if the driver      *
   * requests a specific major number and that       *
   * major number is currently in use. The call      *
   * could also fail if the device switch table is   *
   * currently full.                                 *
   ***************************************************/

  if (majnum == NO_DEV) {
    printf("register_major_num: no major number assigned\n");
    return (ENODEV);
  }

  /***************************************************
   * Store the major number returned by the          *
   * devsw_add interface so that is can be used      *
   * later to unconfigure the device.               
   ***************************************************/

  dmaex_devno = majnum; 

  /***************************************************
   * This member indicates that the beginning minor  *
   * number will be zero (0).                        *
   ***************************************************/

  begunit = 0; 

  /***************************************************
   * Set this state field to indicate that the       *
   * driver has successfully configured.             *
   ***************************************************/

  dmaex_config = TRUE; 
  
  return(ESUCCESS);
}

/***************************************************
 *   callback_register_configuration()             *
 ***************************************************
 * Name: callback_register_configuration           *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 *    int    point  - driver callback point        *
 *    int    order  - Priority at callback         *
 *    ulong  args   - User argument                *
 *                                                 * 
 * Kernel support interface calls:                 *
 *                                                 *
 *      o cfgmgr_set_status                        *
 *      o register_configuration                   *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 *      o cfgmgr framework called through a call   *
 *        to the register_callback interface       *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        ESUCCESS                   *
 *                                                 *
 *      Failure:        EBUSY                      *
 *                      EINVAL                     *
 *                      ESRCH                      *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * This is the Static cfgmgr jacket used to do     *
 * configuration registration in the boot path.    *
 * This jacket routine handles the static callback *
 * specific error handling but utilizes the same   *
 * configuration routine used by the driver when   *
 * dynamically loading.                            *
 *                                                 *
 ***************************************************/

static void
callback_register_configuration(int point,
                                int order,
                                ulong args,
                                ulong event_arg)
{
  int  status;

  DMAEX_DBG1("dmaex: callback_register_configuration:\n");

  /************************************************
   * Detect if a previous failure has occurred in *
   * statically configuring this driver. If so,   *
   * exit the callback without doing any          *
   * configuration work.                          *
   ************************************************/

  if(dmaexcallback_return_status != ESUCCESS) {
    printf
      ("callback_register_configuration: dmaexcallback_return_status error\n");
    return;
  }

  /************************************************
   * Register this drivers configuration with the *
   * kernel's topology.                           *
   ************************************************/

  status  = register_configuration();

  /************************************************
   * If configuration registration is successful, *
   * then just return from this callback. Other-  *
   * wise signal the cfgmgr framework that this   *
   * driver has failed to configure and set the   *
   * variable callback_return_status to the       *
   * failure condition.                           *
   *                                              *
   ***************** NOTE *************************
   *                                              *
   * A side effect of calling cfgmgr_set_status   *
   * interface is that the configure interface or *
   * this driver is called with the unconfigure   *
   * op passed. The unconfigure interface must be *
   * be prepared for this event.                  *
   ************************************************/

  if(status != ESUCCESS) {
    printf
         ("callback_register_configiguration: register_configuration error\n");
    cfgmgr_set_status(mcfgname);
    dmaexcallback_return_status = status;
    return;
  }
}

/***************************************************
 *     callback_register_major_number()            *
 ***************************************************
 * Name:  callback_register_major_number           *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 *    int    point  - driver callback point        *
 *    int    order  - Priority at callback         *
 *    ulong  args   - User argument                *
 *                                                 *
 * Kernel support interface calls:                 *
 *                                                 *
 *      o cfgmgr_set_status                        *
 *      o register_major_number                    *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 *          o cfgmgr framework as static callback  *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        ESUCCESS                   *
 *                                                 *
 *      Failure:        EBUSY                      *
 *                      EINVAL                     *
 *                      ESRCH                      *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * This is the Static cfgmgr jacket used to        *
 * do major number registration in the boot path.  *
 * This jacket routine handles the static callback *
 * specific error handling but utilizes the same   *
 * configuration routine used by the driver when   *
 * dynamically loading.                            *
 *                                                 *
 ***************************************************/

static void
callback_register_major_number(int   point, 
                               int   order, 
                               ulong args,
                               ulong event_arg)
{
  int  status;

  DMAEX_DBG1("dmaex: callback_register_major_number:\n");

  /*************************************************
   * Detect if a previous failure has occurred in  *
   * statically configuring this driver. If so,    *
   * exit the callback without doing any           *
   * configuration work.                           *
   *************************************************/

  if(dmaexcallback_return_status != ESUCCESS) {
    printf
       ("callback_register_major_number: dmaexcallback_return_status error\n");
    return;
  }


  /*************************************************
   * Call driver-specific register_major_number    *
   * interface to register the driver's I/O        *
   * services interfaces (and other information)   *
   * and reserve a major number                    *
   *************************************************/

  status  = register_major_number();

  /*************************************************
   * If configuration registration is successful,  *
   * return from this callback. Otherwise, signal  *
   * the cfgmgr framework that this driver has     *
   * failed to configure and set the variable      *
   * callback_return_status to the failure         *
   * condition.                                    *
   *                                               *
   ***************  Note ***************************
   *                                               *
   * A side effect of calling the cfgmgr_set_status*
   * interface is that the configure interface or  *
   * this driver is called with the unconfigure op *
   * passed. The unconfigure routine must be       *
   * prepared for this event.                      *
   *************************************************/

  if(status != ESUCCESS) {
    printf("callback_register_major_number: register_major_number error \n");
    cfgmgr_set_status(mcfgname);
    dmaexcallback_return_status = status;
    return;
  }
}

/*****************************************************
 *     Autoconfiguration Support Section             *
 *                                                   *
 *--------------- dmaex_probe -----------------------*
 *                                                   *
 * The bus configuration interface calls the         *
 * driver's dmaex_probe interface during dynamic     *
 * and static configuration.                         *
 *                                                   *
 * The dmaex_probe interface calls the BADADDR       *
 * interface during static configration to determine *
 * if the device is present.  If the device is not   *
 * present then the interface returns with error.    *
 * Dynamic configuration assumes the device to be    *
 * present.                                          *
 *                                                   *
 * Dmaex_probe registers the interrupt service       *
 * interfaces(s) and returns a one (1) for success   *
 * or zero (0) for failure.                          *
 *****************************************************/

dmaex_probe(io_handle_t       addr,   /* Base I/O handle for DMAEX device */
            struct controller *ctlr)  /* Pointer to controller structure  */ 

{

  /***************************************************
   * Declarations:                                   *
   *                                                 *
   * o An ihandler_t structure called handler to     *
   *   contain information associated with device    *
   *   driver interrupt handling.                    *
   *                                                 *
   * o A handler_intr_info structure called info to  *
   *   contain interrupt handler information.        *
   *                                                 *
   * o A unit variable to contain the unit number    *
   *   associated with this DMAEX device. The        *
   *   unit number is obtained from ctlr->ctlr_num.  *
   *                                                 *
   * o A csr variable to obtain the return value     *
   *   from read_io_port.                            *
   ***************************************************/

  ihandler_t                  handler;
  struct vme_handler_info     info;
  int                         unit,i;
  char                        csr;
  u_long                      phys_addr;
  caddr_t                     sva;
  register struct dmaex_softc *sc = &dmaex_softc[ctlr->ctlr_num];

  unit = ctlr->ctlr_num;

  DMAEX_DBG1("dmaex_probe: dmaex%d\n",unit);

  sc->csr_handle = (io_handle_t)addr;

  if(dmaex_is_dynamic == SUBSYSTEM_STATICALLY_CONFIGURED) {

    /****************************************************
     * Calls the iohandle_to_phys interface to convert  *
     * an I/O handle to a valid system physical address *
     * present.                                         *
     ****************************************************/

    phys_addr = iohandle_to_phys(sc->csr_handle + DMAEX_CSR_OFF, 
                                 HANDLE_BYTE | HANDLE_SPARSE_SPACE);
    sva = (caddr_t)PHYS_TO_KSEG((vm_offset_t)phys_addr);

    /***************************************************
     * Call BADADDR to determine if device is present. *
     * BADADDR returns the value zero (0) if the device*
     * is present. BADADDR returns a nonzero value if  *
     * the device is not present.                      *
     *                                                 *
     * It is recommended that BADADDR only be called   *
     * for drivers that are statically configured into *
     * kernel. During static configurations, driver    *
     * probe interfaces are single threaded by the     *
     * kernel. Therfore, BADADDR failures are          *
     * guaranteed to be that of the driver issuing the *
     * BADADDR. This is not true for dynamically       *
     * loaded drivers. It is not guaranteed that a     *
     * BADADDR failure in this case is the result of   *
     * this driver's BADADDR access. It could be an    *
     * access caused by another driver in the system   *
     * to an invalid address or failing hardware.      *
     ***************************************************/

    if (!dmaex_no_dev) {
      /***************************************************
       * The software flag indicates that the 'dmaex'    *
       * device hardware is present. Perform the BADADDR *
       ***************************************************/
      if (BADADDR(sva, DMAEX_CSR_SIZE, ctlr))
        return(0);
    }
  }

  /***************************************************
   * If the dmaex_no_dev flag is set, bypass all     *
   * accesses to the device's control and status     *
   * hardware registers. There is no hardware device * 
   ***************************************************/

  if (!dmaex_no_dev) {

    /***************************************************
     * Reset the device.  The write_io_port interface  *
     * is called to write the data, and it will        *
     * perform a memory barrier to insure sequential   *
     * writes to I/O space.                            *
     ***************************************************/

    write_io_port (sc->csr_handle + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0, RESET);
    mb(); /* perform a memory barrier */

    /***************************************************
     * Read the byte from the VMEbus by calling the    *
     * read_io_port interface.  If the result of the   *
     * bitwise AND is nonzero, dmaex_probe returns the *
     * value zero (0) to the configuration code to     *
     * indicate that the device is not responding.     *
     ***************************************************/

    csr = (char) read_io_port (sc->csr_handle + DMAEX_CSR_OFF, 
                               DMAEX_CSR_SIZE, 0);
    if (csr & ERROR)
      return(0);

    /***************************************************
     * Otherwise, the result of the bitwise AND is     *
     * zero indicating that the error bit is not set.  *
     * The dmaex_probe interface calls write_io_port   *
     * to write a byte to the VMEbus. Clear csr reg.   *
     ***************************************************/

    write_io_port (sc->csr_handle + DMAEX_CSR_OFF,
                 DMAEX_CSR_SIZE, 0, 0);
    mb(); /* perform a memory barrier */

  }

  /***************************************************
   * Register the driver's interrupt service         *
   * interface by calling handler_add.  Then enable  *
   * it by calling handler_enable.                   *
   ***************************************************/

  /* initialize information for handler_add() */
  info.gen_intr_info.configuration_st = (caddr_t) ctlr;
  info.gen_intr_info.intr = dmaex_intr1;
  info.gen_intr_info.param = (caddr_t) unit;
  info.gen_intr_info.config_type = CONTROLLER_CONFIG_TYPE;
  info.vec = ctlr->ivnum;                    /* VME vector */
  info.irq = ctlr->bus_priority;             /* VME IPL */

  handler.ih_bus = ctlr->bus_hd;
  handler.ih_bus_info = (char *) &info;

  /* note: handler.ih_id is initialized in handler_add() */
  dmaex_id_t[unit][0] = handler_add (&handler);
  if (dmaex_id_t[unit][0] == (ihandler_id_t *) NULL) {
    return (0);
  }
  if (handler_enable (dmaex_id_t[unit][0]) != 0) {
    handler_del (dmaex_id_t[unit][0]);
    return (0);
  }

  /***************************************************
   * Adding another interrupt service routine:       *
   * Generally the next interrupt vector is          * 
   * sequentially incremented from what is provided  * 
   * in ctlr->ivnum                                  *
   ***************************************************/
  info.gen_intr_info.intr = dmaex_intr2;
  info.vec = ctlr->ivnum + 1;               /* VME vector */
  dmaex_id_t[unit][1] = handler_add (&handler);
  if (dmaex_id_t[unit][1] == (ihandler_id_t *) NULL) {
    handler_disable(dmaex_id_t[unit][0]);
    handler_del(dmaex_id_t[unit][0]);
    return (0);
  }
  if (handler_enable (dmaex_id_t[unit][1]) != 0) {
    handler_del (dmaex_id_t[unit][1]);
    handler_disable(dmaex_id_t[unit][0]);
    handler_del(dmaex_id_t[unit][0]);
    return (0);
  }

  /***************************************************
   * Create an event for the device to signal when   *
   * an interrupt has been received                  *
   ***************************************************/
  event_init(  (event_t *)&sc->isi_event);
  event_clear( (event_t *)&sc->isi_event);

  /***************************************************
   * Create events to be used by the ioctl interface *
   * and the iack isr when posting VMEbus interrupts.*
   *                                                 *
   * Interrupts can be posted on VME IRQs 1 to 7,    *
   * therefore, create an event for each IRQ.        *
   ***************************************************/
  for (i = 1; i < 8; i++) {
    event_init( (event_t *)&sc->post_iack_event[i]);
    event_clear( (event_t *)&sc->post_iack_event[i]);
  }

  /***************************************************
   * update the number of /dev/dmaex devices probed  *
   ***************************************************/

  num_dmaex++;

  /***************************************************
   * Return a value of 1 to indicate that the        *
   * driver's probe interface succeeded.             *
   ***************************************************/

  return (1); /* return success status */
}

/***************************************************
 *     Controller Initialization Section           *
 *                                                 *
 *--------------- dmaex_cattach -------------------*
 *                                                 *
 * The bus configuration interface calls the       *
 * driver's attach interface during dynamic and    *
 * static configuration. This interface is usually *
 * used to perform tasks necessary to establish    *
 * communication with the actual device.           *
 *                                                 *
 * The dmaex_cattach interface does not currently  *
 * perform any tasks (other then to return to the  *
 * bus configuration code with success).  It is    *
 * provided here as a stub for future development. *
 ***************************************************/

int
dmaex_cattach(struct controller *ctlr)  
{
  struct dmaex_softc *sc = &dmaex_softc[ctlr->ctlr_num];

  DMAEX_DBG1("dmaex_cattach: dmaex%d\n",ctlr->ctlr_num);

  return;
}

/***************************************************
 *        Controller Unattach Section              *
 *                                                 *
 *----------- dmaex_ctlr_unattach -----------------*
 *                                                 *
 * The device driver's controller unattach         *
 * interface is called directly from the bus       *
 * configuration code's controller unconfigure     *
 * interface when a driver is being unloaded.      *
 *                                                 *
 * Statically configured device drivers can not be *
 * unloaded. The interface returns ENODEV for this *
 * unconfigure request.                            *
 *                                                 *
 * Dynamically loaded drivers need to deregister   *
 * interrupt handlers, return physical memory      *
 * allocated with contig_malloc back to the        *
 * system, unwire memory that may have been wired  *
 * down by vm_map_pageable, unmap VME program I/O  *
 * space, unmap VME DMA resources, and return any  *
 * other system resources back to the system.      *
 ***************************************************/

int
dmaex_ctlr_unattach(struct bus        *bus,
                    struct controller *ctlr)  
{
  struct dmaex_softc  *sc  = &dmaex_softc[ctlr->ctlr_num];
  register int        unit = ctlr->ctlr_num;
  int                 i;

  DMAEX_DBG1("dmaex_ctlr_unattach: dmaex%d\n",unit);

  /***************************************************
   * Validate the unit number.                       *
   ***************************************************/

  if ((unit > num_dmaex) || (unit < 0)) {
    printf("dmaex_ctlr_unattach: unit %d not valid\n",unit);
    return(ENODEV);
  }

  /***************************************************
   * A statically configured device driver can not   *
   * be unloaded. Therefore return ENODEV as an      *
   * error condition                                 *
   ***************************************************/

  if (dmaex_is_dynamic == SUBSYSTEM_STATICALLY_CONFIGURED) {
    printf("dmaex_ctlr_unattach: statically configured driver\n");
    return(ENODEV);
  }

  /***************************************************
   * The deregistration of interrupt handlers        *
   * consists of a call to handler_disable to        *
   * disable any further interrupts.  Then, call     *
   * handler_del to remove the interrupt handler.    *
   ***************************************************/

  for (i = 0; i < NINTS_PER_DMAEX; i++) {
    if (dmaex_id_t[unit][i]) {
      if (handler_disable(dmaex_id_t[unit][i]) != 0) {
        printf("dmaex_ctlr_unattach: dmaex%d handler_disable failure\n",unit);
        return(ENODEV);
      }
      if (handler_del(dmaex_id_t[unit][i]) != 0) {
        printf("dmaex_ctlr_unattach: dmaex%d handler_del failure\n",unit);
        return(ENODEV);
      }
    } else {
      printf("dmaex_ctlr_unattach: dmaex[%d][%d] no interrupt handler\n",
             unit,i);
    }
  }

  /***************************************************
   * Remove interrupt handler installed by ioctl     *
   * SET_INT_HANDLER                                 *
   ***************************************************/

  if (sc->rcvisr_id_t) {
    if (handler_disable(sc->rcvisr_id_t) != 0) {
      printf("dmaex_ctlr_unattach: dmaex%d handler_disable failed\n",unit);
      return(ENODEV);
    } else {
      if (handler_del(sc->rcvisr_id_t) != 0) {
        printf("dmaex_ctlr_unatttach: dmaex%d handler_del failed\n",unit);
        return(ENODEV);
      } else {
        sc->rcvisr_irq    = 0;
        sc->rcvisr_id_t   = (ihandler_id_t *)NULL;
        sc->rcvisr_proc_p = (struct proc *)NULL;
      }
    }
  }


  /***************************************************
   * If the 128kb physically contiguous 64kb aligned *
   * memory was allocated, return the memory to the  *
   * systems physical memory resource pool.          *
   ***************************************************/
  if (dmaex_contig_buf) {
    contig_free(dmaex_contig_buf,PHYS_CONTIG_BUF_SIZE,M_DEVBUF);
    dmaex_contig_buf = NULL;
  }

  /***************************************************
   * If VMEbus to processor cpu I/O space mapping    *
   * was performed for the use of mmaping from user  *
   * space, return VME system resources.             *
   ***************************************************/
  if (sc->pio_handle) {
    vba_unmap_csr(ctlr,sc->pio_handle);
    sc->pio_handle      = (io_handle_t)0;
    sc->pio_vme_addr    = 0;
    sc->pio_vme_am      = 0;
    sc->pio_vme_size    = 0;
    sc->pio_access_type = 0;
  }

  /***************************************************
   * If VMEbus to processor cpu I/O space mapping    *
   * was performed for the use by UNIX's read or     *
   * write file system interfaces, return VME system *
   * resources.                                      *
   ***************************************************/
  if (sc->vme_handle) {
    vba_unmap_csr(ctlr,sc->vme_handle);
    sc->vme_handle     = (io_handle_t)0;
    sc->vme_addr       = 0;
    sc->vme_am         = 0;
    sc->vme_size       = 0;
    sc->vme_wrk_handle = (io_handle_t)0;
    sc->vme_wrk_size   = 0;
  }

  /***************************************************
   * Free VME DMA resources that still may be mapped *
   * from the VMEbus to the system's memory.         *
   ***************************************************/
  if (sc->dma_handle) {
    dma_map_unload (DMA_DEALLOC, sc->dma_handle);
    sc->dma_handle = (dma_handle_t) NULL;
  }
  if (sc->sys_mem_dma_handle) {
    dma_map_unload (DMA_DEALLOC, sc->sys_mem_dma_handle);
    sc->sys_mem_dma_handle = (dma_handle_t) NULL;
    /*
     * Unwire the memory associated with the mapping
     */
    vm_map_pageable(current_task()->map,
                    trunc_page(sc->sys_mem_vir_addr),
                    round_page(sc->sys_mem_vir_addr +
                               sc->sys_mem_vme_size),
                    VM_PROT_NONE);
  }

  /***************************************************
   * Free DMA block mode write resources that still  *
   * may be mapped for the hardware DMA engine       *
   ***************************************************/
  if (sc->blk_wrt_dma_handle) {
    dma_map_unload(DMA_DEALLOC, sc->blk_wrt_dma_handle);
  }
  if (sc->blk_rd_dma_handle) {
    dma_map_unload(DMA_DEALLOC, sc->blk_rd_dma_handle);
  }

  /***************************************************
   * Release system resource used by devices         *
   * interrupt service interface.                    *
   ***************************************************/
  event_terminate( (event_t *)&sc->isi_event);

  /***************************************************
   * Release system resources reserved for posting   *
   * VMEbus interrupt request.                       *
   ***************************************************/
  for (i = 1; i < 8; i++)
    event_terminate( (event_t *)&sc->post_iack_event[i]);

  return(ESUCCESS);
}

/***************************************************
 *     Open and Close Device Section               *
 *                                                 *
 *--------------- dmaex_open ----------------------*
 *                                                 *
 * The dmaex_open interface checks that the device *
 * is open uniquely.                               *
 ***************************************************/

int
dmaex_open(dev_t  dev,    /* Major/minor device number */ 
           int    flag,   /* Flags from file.h */
           int    format) /* Format of special device */
{

  /***************************************************
   * The following code initializes a unit variable  *
   * to the minor device number and declares:        *
   *                                                 *
   *   o A pointer to a controller structure         *
   *     associated with this DMAEX device           *
   *                                                 *
   *   o A pointer to a dmaex_softc structure        *
   *     associated with this DMAEX device           *
   ***************************************************/

  register int unit = minor(dev); 
  register struct controller  *ctlr; 
  register struct dmaex_softc *sc; 

  /***************************************************
   * The following code makes sure that:             *
   *                                                 *
   *   o The unit number is no more than the maximum * 
   *     number of devices configured on the system  *
   *                                                 *
   *   o The open is unique                          *
   *                                                 *
   * Note: that this sequence of code also initial-  *
   * izes the pointer sc to the address of the       *
   * dmaex_softc structure associated with this      *
   * DMAEX device.                                   *
   ***************************************************/

  DMAEX_DBG1("dmaex_open: dmaex%d\n",unit);

  if ((unit >= NDMAEX ) || (unit < 0))
    return (EIO); 

  sc = &dmaex_softc[unit]; 

  if (sc == (struct dmaex_softc *)NULL)
    return (ENODEV);
  if (sc->sc_open == DMAEXOPEN)
    return (EBUSY); 

  /***************************************************
   * This sequence of code initializes the           *
   * controller structure pointer to its associated  *
   * DMAEX device.                                   *
   *                                                 *
   * If the DMAEX device is initialized, set the     *
   * sc_open member to the open bit DMAEXOPEN and    *
   * returns the value zero (0) to indicate success. *
   *                                                 *
   * Otherwise if the device does not exist, return  *
   * an error.                                       *
   ***************************************************/

  ctlr = dmaex_ctlr[unit]; 
  if ((ctlr) && (ctlr->alive == ALV_ALIVE)) {
    sc->sc_open = DMAEXOPEN;
    return(ESUCCESS);
  } else
    return(ENXIO); 
}

/***************************************************
 *                                                 *
 *--------------- dmaex_close ---------------------*
 *                                                 *
 * The dmaex_close interface clears the DMAEXOPEN  * 
 * flag to allow other processes to use the        *
 * device.                                         *
 ***************************************************/

dmaex_close(dev_t  dev,    /* Major/minor device number */
            int    flag,   /* Flags from file.h */
            int    format) /* Format of special device */
{

  /***************************************************
   * The following code initializes a unit variable  *
   * to the minor device number and declares:        *
   *                                                 *
   *   o A pointer to a controller structure         *
   *     associated with this DMAEX device           *
   *                                                 *
   *   o A pointer to a dmaex_softc structure        *
   *     associated with this DMAEX device           *
   ***************************************************/

  register int unit = minor(dev); 
  register struct controller  *ctlr; 
  register struct dmaex_softc *sc; 

  /***************************************************
   * The following code:                             *
   *                                                 *
   *   o Initializes the sc pointer to the           *
   *     dmaex_softc structure associated with this  *
   *     DMAEX device                                *
   *                                                 *
   *   o Initializes the ctlr pointer to the         *
   *     controller structure associated with this   *
   *     DMAEX device                                *
   *                                                 *
   *   o Calls write_io_port to write a byte to      *
   *     the VMEbus                                  *
   *                                                 *
   *   o Returns the value zero (0) to indicate a    *
   *     successful close of the DMAEX device        *
   ***************************************************/

  DMAEX_DBG1("dmaex_close: dmaex%d\n",unit);

  sc = &dmaex_softc[unit]; 

  sc->sc_open = DMAEXCLOSE; 

  ctlr = dmaex_ctlr[unit]; 
  if (!(ctlr))
    return (ENODEV);

  /***************************************************
   * This driver assumes that the driver writer      *
   * requested the configuration software to map     *
   * VMEbus address space for device register access *
   ***************************************************/
  if (sc->csr_handle == (io_handle_t)0) 
    return (ENODEV);

  /***************************************************
   * If the software flag indicates that the 'dmaex' *
   * device is not present, bypass the writing of    *
   * device's control and status register.           * 
   ***************************************************/
  if (!dmaex_no_dev) {
    write_io_port (sc->csr_handle + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0, 0);
    mb();
  }

  return(ESUCCESS); 
}

/***************************************************
 *                                                 *
 *--------------- dmaex_mmap ----------------------*
 *                                                 *
 * The dmaex_mmap interface is invoked by the      * 
 * kernel as a result of the application calling   *
 * the mmap(2) system call.  dmaex_mmap returns    *
 * the page frame number corresponding to the page *
 * at the specified offset.                        *
 *                                                 *
 * Depending on the device drivers mmap mode       *
 * sc->mmap_mode, a mapped VMEbus address can be   *
 * mapped into user space for user PIO operations  *
 * to the device, or a kernel physically           *
 * contiguous write or read buffer mapped to       *
 * VMEbus DMA space can be mapped to user space.   *
 ***************************************************/

int
dmaex_mmap(dev_t dev,
           off_t off,
           int prot)
{
  long                        kpfnum,flags;
  register int                unit      = minor(dev);
  register struct controller  *ctlr     = dmaex_ctlr[unit];
  register struct dmaex_softc *sc       = &dmaex_softc[unit]; 
  caddr_t                     phys_addr = 0;
  int                         error     = 0;

  DMAEX_DBG1("dmaex_mmap: dmaex%d\n",unit);

  switch (sc->mmap_mode) {
    case MMAP_VME_TO_U_MEM:
      /***************************************************
       * Prior to the user level device driver calling   *
       * the mmap interface, it must invoke the ioctl    *
       * interface with SETUP_VME_FOR_MMAP_PIO ioctl     *
       * parameters. This ioctl interface allows the     *
       * user level driver to specify the VMEbus address,*
       * the VMEbus address modifiers, the swap modes,   *
       * the data size (ignored on some VME adapters)    *
       * and number of bytes to be mapped onto VMEbus.   * 
       ***************************************************/
      if (!sc->pio_handle) {
        printf("dmaex_mmap: You must do an outbound mapping first. \n");
        error++;
      } else {
        if ((sc->pio_vme_am) & VME_DENSE)
          flags = HANDLE_DENSE_SPACE;
        else
          flags = HANDLE_SPARSE_SPACE;

        /*
         * OR in the access type : eg.  HANDLE_LONGWORD
         */
        flags |= (u_int)(sc->pio_access_type);

        /***************************************************
         * Convert the io_handle that represents the cpu   *
         * I/O space to VMEbus mapping to a kernel KSEG    *
         * memory address.                                 *
         ***************************************************/
        phys_addr = ((caddr_t)iohandle_to_phys(sc->pio_handle, flags));

        DMAEX_DBG2("*** io_handle = 0x%lx\n",(u_long)sc->pio_handle);
        DMAEX_DBG2("*** Bus physical address = 0x%lx\n",phys_addr);

        phys_addr = (char *)PHYS_TO_KSEG(phys_addr);
      }
      break;
    case MMAP_K_TO_U_MEM_WRT:
      /***************************************************
       * Prior to the user level device driver calling   *
       * the mmap interface, it must invoke the ioctl    *
       * interface with SETUP_DMA_BLK_WRT ioctl          *
       * parameters. This ioctl interface allows the     *
       * the user level driver to specify the VMEbus     *
       * address, the VMEbus address modifiers, the swap *
       * modes, the data size, the number of bytes to be *
       * written during the DMA transfer. This interface *
       * allows the physically contiguous kernel memory  *
       * buffer to be mapped into user space. The memory *
       * buffer was allocated by this driver with        *
       * contig_malloc interface.                        *
       ***************************************************/
      if (!sc->blk_wrt_dma_handle) {
        printf("dmaex_mmap: need to call SETUP_DMA_BLK_WRT ioctl\n");
        error++;
      } else {
        if (sc->blk_wrt_proc_p) {
          /*
           * When SETUP_DMA_BLK_WRT ioctl interface was called, the
           * user specified his own buffer and chose not to use the
           * physically contiguous memory buffer.
           */
          printf("dmaex_mmap: user buffer mapped - SETUP_DMA_BLK_WRT ioctl\n");
          error++;
        } else {
          /*
           * retrieve the kernel KSEG address saved in the
           * SETUP_DMA_BLK_WRT ioctl
           */
          phys_addr = sc->blk_wrt_buf_ptr;
        }
      }
      break;
    case MMAP_K_TO_U_MEM_RD:
      /***************************************************
       * Prior to the user level device driver calling   *
       * the mmap interface, it must invoke the ioctl    *
       * interface with SETUP_DMA_BLK_RD ioctl           *
       * parameters. This ioctl interface allows the     *
       * the user level driver to specify the VMEbus     *
       * address, the VMEbus address modifiers, the swap *
       * modes, the data size, the number of bytes to be *
       * read during the DMA transfer. This interface    *
       * allows the physically contiguous kernel memory  *
       * buffer to be mapped into user space. The memory *
       * buffer was allocated by this driver with        *
       * contig_malloc interface.                        *
       ***************************************************/
      if (!sc->blk_rd_dma_handle) {
        printf("dmaex_mmap: need to call SETUP_DMA_BLK_RD ioctl\n");
        error++;
      } else {
        if (sc->blk_wrt_proc_p) {
          /*
           * When SETUP_DMA_BLK_RD ioctl interface was called, the
           * user specified his own buffer and chose not to use the
           * physically contiguous memory buffer.
           */
          printf("dmaex_mmap: user buffer mapped - SETUP_DMA_BLK_RD ioctl\n");
          error++;
        } else {
          /*
           * retrieve the kernel KSEG address saved in the
           * SETUP_DMA_BLK_RD ioctl
           */
          phys_addr = sc->blk_rd_buf_ptr;
        }
      }
      break;
    default:
      printf("dmaex_mmap: Invalid mmap selection mode\n");
      error++;
  }

  /*
   * return -1 to caller if an error condtion was detected.
   */
  if (error)
    return(-1);

  DMAEX_DBG2("*** Kseg address = 0x%lx\n",phys_addr);

  /***************************************************
   * Calculate the ALPHA page frame number from the  *
   * KSEG memory address plus the byte offset        *
   * provided by the system. Return the frame number *
   * to the file system calling interface            *
   ***************************************************/
  kpfnum = (long) ((((u_long)phys_addr + off) >> 13) & (0x1fffffffL));

  DMAEX_DBG2("*** Page frame number = 0x%lx\n",kpfnum);

  return((int)kpfnum);

}

/***************************************************
 *                                                 *
 *--------- dmaex_setup_blk_mode ------------------*
 *                                                 *
 * This interface sets up the VME adapters DMA     *
 * engine for either a DMA block mode write or     *
 * read operation. The actual DMA is performed by  *
 * DO_DMA_BLK_WRT or DO_DMA_BLK_RD ioctl call.     *
 ***************************************************/

int
dmaex_setup_blk_mode(struct controller       *ctlr,
                     struct dmaex_softc      *sc,
                     struct dmaex_ioctl_data *data,
                     int                     direction)
{
  struct proc        *proc_p = (struct proc *)NULL;
  unsigned long      b_count;
  sg_entry_t         dmaex_sg;
  vme_atype_t        flags;

  DMAEX_DBG2("dmaex_setup_blk_mode: - direction = 0x%x\n",direction);

  /***************************************************
   * If the caller specified a system virtual        *
   * address, then the process pointer is needed for *
   * dma_map_load. The process pointer is not needed *
   * if a kernel physical buffer is to be used.      *
   ***************************************************/
  if (data->data[3])
    proc_p = task_to_proc(current_task());
  else {
    /***************************************************
     * The caller did not specify a user buffer, check *
     * that the requested byte count does not exceed   *
     * the kernel's physically contiguous memory       *
     * buffer allocated with contig_malloc.            *        
     ***************************************************/
    if ((data->data[0] > (unsigned long)(CONTIG_RD_WRT_BUF_SIZE)) ||
        (!dmaex_contig_buf)) {
      printf("dmaex_setup_blk_mode: buffer to large or no kernel buffer\n");
      return(EINVAL);
    }
  }

  /***************************************************
   * Update the VME address modifiers with DMA       *
   * specific flags                                  *
   ***************************************************/
  flags = (vme_atype_t)
    ((data->data[2] & VME_BITS_MASK) | DMA_SLEEP | DMA_GUARD_UPPER | DMA_ALL);

  /***************************************************
   * Obtain the mapping information from the ioctl   *
   * data parameters, update VME address modifiers   *
   * with the direction of transfer (DMA_IN or       *
   * DMA_OUT, and save the buffer address. The       *
   * buffer address will either be user supplied     *
   * buffer or a kernel physically contiguous buffer *
   ***************************************************/
  if (direction & DMA_OUT) {
    /*
     * Setting up for a DMA block mode write
     */
    sc->blk_wrt_vme_size   = (unsigned int)data->data[0];
    sc->blk_wrt_vme_addr   = (vme_addr_t)data->data[1];
    sc->blk_wrt_vme_am     = (vme_atype_t)(data->data[2] & VME_BITS_MASK);
    sc->blk_wrt_dma_handle = (dma_handle_t)NULL;
    sc->blk_wrt_proc_p     = proc_p;
    flags                 |= DMA_OUT;
    /*
     * If caller specified a user buffer, use the callers buffer,
     * otherwise, use the physical contiguous kernel write buffer.
     */
    if (proc_p)
      sc->blk_wrt_buf_ptr = (caddr_t)data->data[3];
    else
      sc->blk_wrt_buf_ptr = dmaex_contig_wrt_buf;
  } else {
    /*
     * Setting up for a DMA block mode read
     */
    sc->blk_rd_vme_size   = (unsigned int)data->data[0];
    sc->blk_rd_vme_addr   = (vme_addr_t)data->data[1];
    sc->blk_rd_vme_am     = (vme_atype_t)(data->data[2] & VME_BITS_MASK);
    sc->blk_rd_dma_handle = (dma_handle_t)NULL;
    sc->blk_rd_proc_p     = proc_p;
    flags                |= DMA_IN;
    /*
     * If caller specified a user buffer, use the callers buffer,
     * otherwise, use the physical contiguous kernel write buffer.
     */
    if (proc_p)
      sc->blk_rd_buf_ptr = (caddr_t)data->data[3];
    else
      sc->blk_rd_buf_ptr = dmaex_contig_rd_buf;
  }

  /***************************************************
   * When using the DMA engine, the VMEbus address   *
   * and flags with DMA_IN or DMA_OUT must be        *
   * provided to the vba_set_dma_addr interface.     *
   ***************************************************/
  flags = vba_set_dma_addr(ctlr, flags, (vme_addr_t)data->data[1]);

  /***************************************************
   * Allocate, load and set system resources for the *
   * DMA block mode transfer.                        *
   ***************************************************/
  if (direction == DMA_OUT) {
    b_count = dma_map_load( (unsigned long)sc->blk_wrt_vme_size,
                            (vm_offset_t)sc->blk_wrt_buf_ptr,
                            proc_p,
                            ctlr,
                            &sc->blk_wrt_dma_handle,
                            0,
                            flags);

    if ( (!b_count) || (!sc->blk_wrt_dma_handle) ) {
      printf("dmaex_setup_blk_mode: - write - dma_map_load failed.\n");
      return(EIO);
    }

    /***************************************************
     * Obtain the VMEbus address and byte count of DMA *
     * block write request                             *
     ***************************************************/
    dmaex_sg = dma_get_curr_sgentry(sc->blk_wrt_dma_handle);
  } else {
    b_count = dma_map_load( (unsigned long)sc->blk_rd_vme_size,
                            (vm_offset_t)sc->blk_rd_buf_ptr,
                            proc_p,
                            ctlr,
                            &sc->blk_rd_dma_handle,
                            0,
                            flags);
    if ( (!b_count) || (!sc->blk_rd_dma_handle) ) {
      printf("dmaex_setup_blk_mode: - read - dma_map_load failed.\n");
      return(EIO);
    }

    /***************************************************
     * Obtain the VMEbus address and byte count of DMA *
     * block read request                              *
     ***************************************************/
    dmaex_sg = dma_get_curr_sgentry(sc->blk_rd_dma_handle);
  }

  DMAEX_DBG2("dmaex_setup_blk_mode: sg.ba = 0x%lx sg.bc = 0x%x\n",
              dmaex_sg->ba, dmaex_sg->bc);

  return(ESUCCESS);
}


/***************************************************
 *                                                 *
 *--------------- dmaex_ioctl ---------------------*
 *                                                 *
 * The dmaex_ioctl interface performs driver       *
 * specific ioctl functions.                       *
 *                                                 *
 ***************************************************/

int
dmaex_ioctl(dev_t                   dev,    /* fd                          */
            int                     cmd,    /* ioctl command               */
            struct dmaex_ioctl_data *data,  /* user command-specified data */
            int                     flag)   /* access mode                 */
{
  int                     unit    = minor(dev);
  struct dmaex_softc      *sc     = &dmaex_softc[unit];
  struct controller       *ctlr   = dmaex_ctlr[unit];
  int                     ret_val = ESUCCESS;
  struct proc             *proc_p;
  sg_entry_t              dmaex_sg;
  ihandler_t              handler;
  struct vme_handler_info info;
  unsigned int            irq;
  unsigned int            vector;

  DMAEX_DBG1("dmaex_ioctl: dmaex%d\n",unit);
  DMAEX_DBG2("param0 = 0x%lx param1 = 0x%lx param2 = 0x%lx\n",
              data->data[0],data->data[1],data->data[2]);

  if ((unit >= NDMAEX) || (unit < 0))
    return(ENODEV);

  switch(cmd) {
    case SET_MMAP_MODE:
      /***************************************************
       * The SET_MMAP_MODE ioctl defines how dmaex_mmap  *
       * will interpret a mmap request. The mmap mode is *
       * passed in the ioctl data parameter.             *
       *   data->data[0] mmap mode selection             *
       *   MMAP_VME_TO_U_MEM -  will return page frame   *
       *                       number representing a     *
       *                       mapped VMEbus address     *
       *   MMAP_K_TO_U_MEM_WRT - will return page frame  *
       *                       number representing a     *
       *                       physical contiguous write *
       *                       cma_dd kernel buffer.     *
       *   MMAP_K_TO_U_MEM_RD  - will return page frame  *
       *                       number representing a     *
       *                       physical contiguous read  *
       *                       cma_dd kernel buffer.     *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: SET_MMAP_MODE\n");
      switch ((int)data->data[0]) {
        case MMAP_VME_TO_U_MEM:
        case MMAP_K_TO_U_MEM_WRT:
        case MMAP_K_TO_U_MEM_RD:
          sc->mmap_mode = (int)data->data[0];
          break;
        default:
          ret_val = EINVAL;
      }
      break;
    case GET_MMAP_MODE:
      /***************************************************
       * The GET_MMAP_MODE ioctl returns mmap mode       *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: GET_MMAP_MODE\n");
      data->data[0] = (unsigned long)sc->mmap_mode;
      break;
    case SET_STRATEGY_XFER_MODE:
      /***************************************************
       * The SET_STRATEGY_XFER_MODE ioctl sets the data  *
       * transfer mode of the dmaex_strategy interface.  *
       * The dmaex_strategy interface is invoked by      *
       * physio when dmaex_read or dmaex_write is called *
       * by the user application through Digital UNIX's  *
       * read or write interfaces. The dmaex_strategy    *
       * interface can perform the transfer using        *
       * programmed I/O, using the VME adapters DMA      *
       * engine ot using the VME device's DMA engine.    *
       * The transfer mode is specified in the ioctl     *
       * data parameter.                                 *
       *   data->data[0] = data transfer mode            *
       * (PIO_XFER_MODE, DEVICE_DMA_MODE, BLOCK_DMA_MODE)*
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: SET_STRATEGY_XFER_MODE\n");
      switch ((int)data->data[0]) {
        case PIO_XFER_MODE:
        case DEVICE_DMA_MODE:
        case BLOCK_DMA_MODE:
          sc->strategy_xfer_mode = (int)data->data[0];
          break;
        default:
          ret_val = EINVAL;
      }
      break;
    case GET_STRATEGY_XFER_MODE:
      /***************************************************
       * The GET_STRATEGY_XFER_MODE ioctl returns the    *
       * current data transfer mode that was specified   *
       * by the SET_STRATEGY_XFER_MODE ioctl. The        *
       * transfer mode is used by the dmaex_strategy     *
       * interface to determine how the data is to be    *
       * transferred. The transfer mode is returned in   *
       * the ioctl data interface.                       *
       *   data->data[0] = data transfer mode            *
       * (PIO_XFER_MODE, DEVICE_DMA_MODE, BLOCK_DMA_MODE)*
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: GET_STRATEGY_XFER_MODE\n");
      data->data[0] = (unsigned long)sc->strategy_xfer_mode;
      break;
    case SETUP_VME_FOR_MMAP_PIO:
      /***************************************************
       * The SETUP_VME_FOR_MMAP_PIO ioctl maps outbound  *
       * to a user specified VME address, size and       *
       * address modifier. The information provided to   *
       * this ioctl will be used to perform a user mmap  *
       * request when the kernel invokes the dmaex_mmap  *
       * interface. The VMEbus configuration data is     *
       * passed in the data parameter of the ioctl call. *
       *   data->data[0] = number of bytes to map        *
       *   data->data[1] = VMEbus address to be mapped   *
       *   data->data[2] = VMEbus address modifiers      *
       *   data->data[3] = PIO access (HANDLE_LONGWORD)  *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: SETUP_VME_FOR_MMAP_PIO\n");

      if (sc->pio_handle) {
        printf("SETUP_VME_FOR_MMAP_PIO failed, a PIO mapping exists\n");
        ret_val = EBUSY;
        break;
      }

      /*
       * Perform the requested outbound mapping
       */
      sc->pio_handle = vba_map_csr(ctlr,
                                   (vme_addr_t)data->data[1],
                                   (u_int)data->data[0],
                                   (u_int)(data->data[2] & VME_BITS_MASK));

      if (!(sc->pio_handle)) {
        printf("SETUP_VME_FOR_MMAP_PIO: vba_map_csr interface failed\n");
        printf("  pio_addr = 0x%x pio_size = 0x%x pio_am = 0x%x\n",
               (vme_addr_t)data->data[1],
               (u_int)data->data[0],
               (vme_atype_t)(data->data[2] & VME_BITS_MASK));
        ret_val = EINVAL;
        break;
      }

      /*
       * Save the PIO mapping information for a mmap request
       */
      sc->pio_vme_size = (unsigned int)data->data[0];
      sc->pio_vme_addr = (vme_addr_t)data->data[1];
      sc->pio_vme_am   = (vme_atype_t)(data->data[2] & VME_BITS_MASK);
      sc->pio_access_type = (unsigned int)data->data[3];
      break;
    case GET_VME_INFO_FOR_MMAP_PIO:
      /***************************************************
       * The GET_VME_INFO_FOR_MMAP_PIO ioctl is used to  *
       * return mmap specific VMEbus mapping information.*
       *   data->data[0] = number of bytes mapped        *
       *   data->data[1] = VMEbus address mapped         *
       *   data->data[2] = VMEbus address modifiers      *
       *   data->data[3] = PIO access (HANDLE_LONGWORD)  *
       *   data->data[4] = io_handle_t representing the  *
       *                   VMEbus mapped address         *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: GET_VME_INFO_FOR_MMAP_PIO\n");

      data->data[0] = (unsigned long)sc->pio_vme_size;
      data->data[1] = (unsigned long)sc->pio_vme_addr;
      data->data[2] = (unsigned long)sc->pio_vme_am;
      data->data[3] = (unsigned long)sc->pio_access_type;
      data->data[4] = (unsigned long)sc->pio_handle;
      break;
    case UNMAP_VME_FOR_MMAP_PIO:
      /***************************************************
       * The UNMAP_VME_FOR_MMAP_PIO ioctl is used to     *
       * return VMEbus mapped resources allocated by     *
       * SETUP_VME_FOR_MMAP_PIO ioctl back to the system.*
       * No data is passed or returned from this         *
       * interface via the data parameter.               *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: UNMAP_VME_FOR_MMAP_PIO\n");

      if (sc->pio_handle) {
        /*
         * Unmap the previously mapped VMEbus address
         */
        vba_unmap_csr(ctlr,sc->pio_handle);
        sc->pio_handle = (unsigned long)0;
        sc->pio_vme_addr    = 0;
        sc->pio_vme_am      = 0;
        sc->pio_vme_size    = 0;
        sc->pio_access_type = 0;
      } else {
        printf("dmaex_ioctl: UNMAP_VME_FOR_MMAP_PIO, no PIO to unmap.\n");
        ret_val = ENXIO;
      }
      break;
    case SET_STRATEGY_INFO_BLK_DMA:
      /***************************************************
       * The SET_STRATEGY_INFO_BLK_DMA ioctl provides    *
       * VMEbus mapping information.  This information   *
       * is used by the dmaex_strategy interface when    *
       * the transfer mode equals BLOCK_DMA_MODE.        *
       * BLOCK_DMA_MODE causes the dmaex_strategy        *
       * interface to perform the data transfer using    *
       * the VME adapter's hardware DMA engine. VMEbus   *
       * mapping information is passed in the ioctl      *
       * data parameter.                                 *
       *   data->data[0] = number of bytes to map        *
       *   data->data[1] = VMEbus address to map         *
       *   data->data[2] = VMEbus address modifiers      *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: SET_STRATEGY_INFO_BLK_DMA\n");

      /*
       * Check that a mapping size and address modifiers specified
       */
      if ( (!(unsigned int)data->data[0]) ||
            (!((unsigned int)data->data[2] & VME_BITS_MASK)) ) {
        printf("dmaex_ioctl: SET_STRATEGY_INFO_BLK_DMA, invalid parameters\n");
        ret_val = EINVAL;
      } else {
        /*
         * Save the DMA mapping information
         */
        sc->dma_is_set       = 1;
        sc->dma_vme_size = (unsigned int)data->data[0];
        sc->dma_vme_addr = (vme_addr_t)data->data[1];
        sc->dma_vme_am  = (vme_atype_t)(data->data[2] & VME_BITS_MASK);
        sc->dma_wrk_vme_addr = 0;
        sc->dma_wrk_vme_size = 0;
      }
      break;
    case GET_STRATEGY_INFO_BLK_DMA:
      /***************************************************
       * The GET_STRATEGY_INFO_BLK_DMA ioctl returns     *
       * BLOCK_DMA_MODE mapping information. It returns  *
       * the initial mapping information specified to    *
       * ioctl SET_STRATEGY_INFO_BLK_DMA interface and   *
       * the results of the DMA transfer. The data is    *
       * returned through the data parameter of the      *
       * ioctl interface.                                *
       *   data->data[0] = number of bytes mapped        *
       *   data->data[1] = VMEbus address mapped         *
       *   data->data[2] = VMEbus address modifiers      *
       *   data->data[3] = VMEbus address after transfer *
       *   data->data[4] = total bytes transferred       *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: GET_STRATEGY_INFO_BLK_DMA\n");

      data->data[0] = (unsigned long)sc->dma_vme_size;
      data->data[1] = (unsigned long)sc->dma_vme_addr;
      data->data[2] = (unsigned long)sc->dma_vme_am;
      data->data[3] = (unsigned long)sc->dma_wrk_vme_addr;
      data->data[4] = (unsigned long)sc->dma_wrk_vme_size;
      break;
    case CLR_STRATEGY_INFO_BLK_DMA:
      /***************************************************
       * The CLR_STRATEGY_INFO_BLK_DMA ioctl clears the  *
       * information previously specified to ioclt       *
       * interface SET_STRATEGY_INFO_BLK_DMA ioctl and   *
       * the infomation that was updated by the          *
       * dmaex_strategy interface when the transfer mode *
       * was BLOCK_DMA_MODE. No parameters are passed to *
       * this ioctl interface.                           *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: CLR_STRATEGY_INFO_BLK_DMA\n");
      sc->dma_is_set   = 0;
      sc->dma_vme_size = 0;
      sc->dma_vme_addr = 0;
      sc->dma_vme_am   = 0;
      sc->dma_wrk_vme_addr = 0;
      sc->dma_wrk_vme_size = 0;
      break;
    case SETUP_VME_FOR_STRATEGY_PIO:
      /***************************************************
       * The SETUP_VME_FOR_STRATEGY_PIO ioctl provides   *
       * VMEbus mapping information.  This ioctl will    *
       * map outbound to the specified VMEbus address,   *
       * address modifiers and for the specified number  *
       * of bytes. The resultant io_handle_t of the      *
       * mapping is used by dmaex_strategy interface     *
       * when the transfer mode equals PIO_XFER_MODE.    *
       * PIO_XFER_MODE causes the dmaex_strategy         *
       * interface to perform the data transfer using    *
       * programmed I/O operations. VMEbus mapping       *
       * information is passed in the ioctl data         *
       * parameter.                                      *
       *   data->data[0] = number of bytes to map        *
       *   data->data[1] = VMEbus address to map         *
       *   data->data[2] = VMEbus address modifiers      *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: SETUP_VME_FOR_STRATEGY_PIO\n");
      if (sc->vme_handle) {
        printf("SETUP_VME_FOR_STRATEGY_PIO failed, a PIO mapping exists.\n");
        ret_val = EBUSY;
        break;
      }

      /*
       * Perform the requested outbound mapping
       */
      sc->vme_handle = vba_map_csr(ctlr,
                                   (vme_addr_t)data->data[1],
                                   (u_int)data->data[0],
                                   (u_int)(data->data[2] & VME_BITS_MASK));

      if (!(sc->vme_handle)) {
        printf("SETUP_VME_FOR_STRATEGY_PIO: vba_map_csr interface failed\n");
        printf("  pio_addr = 0x%x pio_size = 0x%x pio_am = 0x%x\n",
               (vme_addr_t)data->data[1],
               (u_int)data->data[0],
               (vme_atype_t)(data->data[2] & VME_BITS_MASK));
        ret_val = EINVAL;
        break;
      }

      /*
       * Save the PIO mapping information
       */
      sc->vme_size       = (unsigned int)data->data[0];
      sc->vme_addr       = (vme_addr_t)data->data[1];
      sc->vme_am         = (vme_atype_t)(data->data[2] & VME_BITS_MASK);
      sc->vme_wrk_handle = (io_handle_t)0;
      sc->vme_wrk_size   = 0;
      break;
    case GET_VME_INFO_FOR_STRATEGY_PIO:
      /***************************************************
       * The GET_VME_INFO_FOR_STRATEGY_PIO ioctl returns *
       * dmaex_stratgey PIO_XFER_MODE mapping            *
       * information. It returns the initial mapping     *
       * info specified to SETUP_VME_FOR_STRATEGY_PIO    *
       * interface and the results of the PIO transfer.  *
       * The data is returned through the data parameter *
       * of the ioctl interface.                         *
       *   data->data[0] = number of bytes mapped        *
       *   data->data[1] = VMEbus address mapped         *
       *   data->data[2] = VMEbus address modifiers      *
       *   data->data[3] = io_handle_t of mapped VMEbus  *
       *                   address.                      *
       *   data->data[4] = io_handle_t after transfer    *
       *   data->data[5] = total bytes transferred       *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: GET_VME_INFO_FOR_STRATEGY_PIO\n");
      data->data[0] = (unsigned long)sc->vme_size;
      data->data[1] = (unsigned long)sc->vme_addr;
      data->data[2] = (unsigned long)sc->vme_am;
      data->data[3] = (unsigned long)sc->vme_handle;
      data->data[4] = (unsigned long)sc->vme_wrk_handle;
      data->data[5] = (unsigned long)sc->vme_wrk_size;
      break;
    case UNMAP_VME_FOR_STRATEGY_PIO:
      /***************************************************
       * The UNMAP_VME_FOR_STRATEGY_PIO ioctl is used to *
       * return VMEbus mapped resources allocated by     *
       * SETUP_VME_FOR_STRATEGY_PIO ioctl back to the    *
       * system. No data is passed or returned from this *
       * interface via the data parameter.               *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: UNMAP_VME_FOR_STRATEGY_PIO\n");
      if (sc->vme_handle) {
        /*
         * Unmap the previously mapped VMEbus address
         */
        vba_unmap_csr(ctlr,sc->vme_handle);
        sc->vme_handle     = (unsigned long)0;
        sc->vme_addr       = 0;
        sc->vme_am         = 0;
        sc->vme_size       = 0;
        sc->vme_wrk_handle = (io_handle_t)0;
        sc->vme_wrk_size   = 0;
      } else {
        printf("dmaex_ioctl: UNMAP_VME_FOR_STRATEGY_PIO, no VME to unmap.\n");
        ret_val = ENXIO;
      }
      break;
    case MAP_SYS_MEM_TO_VME:
      /***************************************************
       * The MAP_SYS_MEM_TO_VME ioctl maps the user's    *
       * memory to the VMEbus for the number of bytes    *
       * specified. The user's memory is also wired down *
       * so that it will not be swapped. Mapping infor-  *
       * mation is passed in the ioctl data parameter.   *
       *   data->data[0] = number of bytes to be mapped  *
       *   data->data[1] = VMEbus address - not used     *
       *   data->data[2] = VMEbus address modifiers      *
       *   data->data[3] = User virtual buffer address   *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: MAP_SYS_MEM_TO_VME\n");
      if (sc->sys_mem_dma_handle) {
        printf("MAP_SYS_MEM_TO_VME failed, a system memory mapping exists.\n");
        ret_val = EBUSY;
        break;
      }
      if (!data->data[0] || !data->data[2] || !data->data[3]) {
        printf("MAP_SYS_MEM_TO_VME failed, no bc, VME am or vir mem buf\n");
        ret_val = EINVAL;
        break;
      }
      /*
       * Obtain the mapping information from the caller
       */
      sc->sys_mem_dma_handle = (dma_handle_t)NULL;
      sc->sys_mem_vme_size   = (unsigned int)data->data[0];
      sc->sys_mem_vme_am     = (vme_atype_t)data->data[2];
      sc->sys_mem_vir_addr   = (caddr_t)data->data[3];
      proc_p = task_to_proc(current_task());

      /*
       * Map system memory to the VMEbus
       */
      if (dma_map_load ((unsigned long) sc->sys_mem_vme_size,
                        (vm_offset_t) sc->sys_mem_vir_addr,
                        proc_p,
                        ctlr,
                        &sc->sys_mem_dma_handle,
                        0,
                        sc->sys_mem_vme_am | DMA_GUARD_UPPER | DMA_ALL) == 0) {
        printf("MAP_SYS_MEM_TO_VME failed, dma_map_load returned zero\n");
        ret_val = EINVAL;
        break;
      }

      /*
       * Obtain the VMEbus address associated with the mapped system memory
       */
      dmaex_sg = dma_get_curr_sgentry (sc->sys_mem_dma_handle);
      sc->sys_mem_vme_addr = (vme_addr_t)dmaex_sg->ba;
      sc->sys_mem_vme_size = (unsigned int)dmaex_sg->bc;

      /*
       * Wire the system memory down so that it will not be swapped out
       */
      if (vm_map_pageable(current_task()->map,
                          trunc_page(sc->sys_mem_vir_addr),
                          round_page(sc->sys_mem_vir_addr +
                                     sc->sys_mem_vme_size),
                          (VM_PROT_READ | VM_PROT_WRITE))) {
        printf("MAP_SYS_MEM_TO_VME: vm_map_pageable failed\n");
        dma_map_unload (DMA_DEALLOC, sc->sys_mem_dma_handle);
        sc->sys_mem_dma_handle = (dma_handle_t)NULL;
        sc->sys_mem_vme_addr   = 0;
        sc->sys_mem_vme_am     = 0;
        sc->sys_mem_vme_size   = 0;
        sc->sys_mem_vir_addr   = (caddr_t)NULL;
        ret_val = EIO;
        break;
      } 
    case GET_SYS_MEM_INFO:
      /***************************************************
       * The GET_SYS_MEM_INFO ioctl returns the results  *
       * of the MAP_SYS_MEM_TO_VME ioctl. In addition    *
       * to the parameters specified in the              *
       * MAP_SYS_MEM_TO_VME ioctl, the VMEbus address at *
       * which the users memory is mapped is returned.   *
       * The data is returned in the ioctl data parameter*
       *   data->data[0] = number of bytes mapped        *
       *   data->data[1] = VMEbus address of user memory *
       *   data->data[1] = VMEbus address modifiers      *
       *   data->data[2] = User virtual buffer address   *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: GET_SYS_MEM_INFO\n");
      data->data[0] = (unsigned long)sc->sys_mem_vme_size;
      data->data[1] = (unsigned long)sc->sys_mem_vme_addr;
      data->data[2] = (unsigned long)sc->sys_mem_vme_am;
      data->data[3] = (unsigned long)sc->sys_mem_vir_addr;
      break;
    case UNMAP_SYS_MEM_TO_VME:
      /***************************************************
       * The UNMAP_SYS_MEM_TO_VME ioctl releases VME     *
       * resources previously mapped by ioctl interface  *
       * MAP_SYS_MEM_TO_VME and unwires the users memory.*
       * On return from this call the users memroy is no *
       * longer on the VMEbus and the memory is alowed   *
       * to be swapped by the system. No information is  *
       * needed in the ioctl data parameter.             *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: UNMAP_SYS_MEM_TO_VME\n");
      if (sc->sys_mem_dma_handle) {
        /*
         * Unmap the previously mapped system memory to VMEbus mapping
         */
        dma_map_unload (DMA_DEALLOC, sc->sys_mem_dma_handle);
        if (vm_map_pageable(current_task()->map,
                            trunc_page(sc->sys_mem_vir_addr),
                            round_page(sc->sys_mem_vir_addr +
                                       sc->sys_mem_vme_size),
                            (VM_PROT_NONE))) {
          printf("UNMAP_SYS_MEM_TO_VME: vm_map_pageable failed\n");
          ret_val = EIO;
        }
        sc->sys_mem_dma_handle = (dma_handle_t)NULL;
        sc->sys_mem_vme_addr   = 0;
        sc->sys_mem_vme_am     = 0;
        sc->sys_mem_vme_size   = 0;
        sc->sys_mem_vir_addr   = (caddr_t)NULL;
      } else {
        printf("dmaex_ioctl: UNMAP_SYS_MEM_TO_VME, no VME to unmap.\n");
        ret_val = ENXIO;
      }
      break;
    case SETUP_DMA_BLK_WRT:
      /***************************************************
       * The SETUP_DMA_BLK_WRT ioctl sets up the VME     *
       * adapters DMA engine for a DMA Block Mode Write  *
       * to the specified VMEbus address, address        *
       * modifiers and for the number of bytes specified.*
       * If caller's memory buffer is not specified,     *
       * half of the physically contiguous memory buffer *
       * allocated with cma_dd option and contig_malloc  *
       * will be used by this ioctl interface. This      *
       * kernel buffer can be mapped into user space by  *
       * setting the drivers mmap mapping mode to        *
       * MMAP_K_TO_U_MEM_WRT and the user calling the    *
       * mmap interface to obtain a users pointer to     *
       * this address. Mapping information is passed in  *
       * the ioctl data parameter.                       *
       *   data->data[0] = number of bytes to be mapped  *
       *   data->data[1] = VMEbus adddres to be mapped   *
       *   data->data[2] = VMEbus address modifiers      *
       *   data->data[3] = User virtual buffer address   *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: SETUP_DMA_BLK_WRT\n");
      /*
       * Check that a mapping size and address modifiers are specified and
       * that a previous mapping request is not active.
       */
      if ( (!(unsigned int)data->data[0]) ||
            (!((unsigned int)data->data[2] & VME_BITS_MASK)) ) {
        printf("dmaex_ioctl: SETUP_DMA_BLK_WRT, invalid parameters\n");
        ret_val = EINVAL;
        break;
      }
      if (sc->blk_wrt_dma_handle) {
        printf("SETUP_DMA_BLK_WRT failed, a DMA mapping exists.\n");
        ret_val = EBUSY;
        break;
      }
      /*
       * go perform the actual DMA block mode write setup
       */
      ret_val = dmaex_setup_blk_mode(ctlr,sc,data,DMA_OUT);
      break;
    case GET_DMA_BLK_WRT:
      DMAEX_DBG2("dmaex_ioctl: GET_DMA_BLK_WRT\n");
      data->data[0] = (unsigned long)sc->blk_wrt_vme_size;
      data->data[1] = (unsigned long)sc->blk_wrt_vme_addr;
      data->data[2] = (unsigned long)sc->blk_wrt_vme_am;
      data->data[3] = (unsigned long)sc->blk_wrt_dma_handle;
      data->data[4] = (unsigned long)sc->blk_wrt_buf_ptr;
      break;
    case DO_DMA_BLK_WRT:
      DMAEX_DBG2("dmaex_ioctl: DO_DMA_BLK_WRT\n");

      if (!sc->blk_wrt_dma_handle) {
        printf("DO_DMA_BLK_WRT failed, no DMA mapping exists.\n");
        ret_val = EINVAL;
        break;
      }

      /*
       * The adapters DMA engine mapping has been setup to the VMEbus.
       * If the caller has specified a user system address to map, then
       * wire the users memory down so that it will not be swapped out
       * during hardware DMA. If two different DMA buffers are used
       * from user space, then wiring and unwiring the system memory 
       * needs to be done prior to and after the hardware DMA.
       */
      if (sc->blk_wrt_proc_p) {
        if (vm_map_pageable(current_task()->map,
                            trunc_page(sc->blk_wrt_buf_ptr),
                            round_page(sc->blk_wrt_buf_ptr +
                                       sc->blk_wrt_vme_size),
                            (VM_PROT_READ | VM_PROT_WRITE))) {
          printf("DO_DMA_BLK_WRT - vm_map_pageable failed wiring\n");
          data->data[0] = (unsigned long)0;
          ret_val = EIO;
          break;
        }
      }

      /*
       * perform the actual hardware DMA
       */
      data->data[0] = (unsigned long)vba_dma(ctlr,sc->blk_wrt_dma_handle);

      /*
       * If user memory was specified to SETUP_DMA_BLK_WRT ioctl,
       * unwire the user's memory.
       */
      if (sc->blk_wrt_proc_p) {
        if (vm_map_pageable(current_task()->map,
                            trunc_page(sc->blk_wrt_buf_ptr),
                            round_page(sc->blk_wrt_buf_ptr +
                                       sc->blk_wrt_vme_size),
                            (VM_PROT_NONE))) {
          printf("DO_DMA_BLK_WRT: vm_map_pageable failed unwiring\n");
          ret_val = EIO;
        }
      }
      break;
    case CLR_DMA_BLK_WRT:
      /***************************************************
       * The CLR_DMA_BLK_WRT ioctl releases Block Mode   *
       * resources previously mapped by ioctl interface  *
       * SETUP_DMA_BLK_WRT. No information is needed in  *
       * the ioctl data parameter.                       *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: CLR_DMA_BLK_WRT\n");
      if (sc->blk_wrt_dma_handle) {
        /*
         * Unmap the previously mapped system memory to VMEbus mapping
         */
        dma_map_unload (DMA_DEALLOC, sc->blk_wrt_dma_handle);

        sc->blk_wrt_vme_addr   = 0;
        sc->blk_wrt_vme_am     = 0;
        sc->blk_wrt_vme_size   = 0;
        sc->blk_wrt_dma_handle = (dma_handle_t)NULL;
        sc->blk_wrt_buf_ptr    = (caddr_t)NULL;
        sc->blk_wrt_proc_p     = (struct proc *)NULL;
      } else {
        printf("dmaex_ioctl: CLR_DMA_BLK_WRT, no VME to unmap.\n");
        ret_val = ENXIO;
      }
      break;
    case SETUP_DMA_BLK_RD:
      /***************************************************
       * The SETUP_DMA_BLK_RD ioctl sets up the VME      *
       * adapters DMA engine for a DMA Block Mode Read   *
       * to the specified VMEbus address, address        *
       * modifiers and for the number of bytes specified.*
       * If s caller's memory buffer is not specified,   *
       * half of the physically contiguous memory buffer *
       * allocated with cma_dd option and contig_malloc  *
       * will be used by this ioctl interface. This      *
       * kernel buffer can be mapped into user space by  *
       * setting the drivers mmap mapping mode to        *
       * MMAP_K_TO_U_MEM_RD and the user calling the     *
       * mmap interface to obtain a users pointer to     *
       * this address. Mapping information is passed in  *
       * the ioctl data parameter.                       *
       *   data->data[0] = number of bytes to be mapped  *
       *   data->data[1] = VMEbus adddres to be mapped   *
       *   data->data[2] = VMEbus address modifiers      *
       *   data->data[3] = User virtual buffer address   *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: SETUP_DMA_BLK_RD\n");
      /*
       * Check that a mapping size and address modifiers are specified and
       * that a previous mapping request is not active.
       */
      if ( (!(unsigned int)data->data[0]) ||
            (!((unsigned int)data->data[2] & VME_BITS_MASK)) ) {
        printf("dmaex_ioctl: SETUP_DMA_BLK_RD, invalid parameters\n");
        ret_val = EINVAL;
        break;
      }
      if (sc->blk_rd_dma_handle) {
        printf("SETUP_DMA_BLK_RD failed, a DMA mapping exists.\n");
        ret_val = EBUSY;
        break;
      }
      /*
       * go perform the actual DMA block mode read setup
       */
      ret_val = dmaex_setup_blk_mode(ctlr,sc,data,DMA_IN);
      break;
    case GET_DMA_BLK_RD:
      DMAEX_DBG2("dmaex_ioctl: GET_DMA_BLK_RD\n");
      data->data[0] = (unsigned long)sc->blk_rd_vme_size;
      data->data[1] = (unsigned long)sc->blk_rd_vme_addr;
      data->data[2] = (unsigned long)sc->blk_rd_vme_am;
      data->data[3] = (unsigned long)sc->blk_rd_dma_handle;
      data->data[4] = (unsigned long)sc->blk_rd_buf_ptr;
      break;
    case DO_DMA_BLK_RD:
      DMAEX_DBG2("dmaex_ioctl: DO_DMA_BLK_RD\n");

      if (!sc->blk_rd_dma_handle) {
        printf("DO_DMA_BLK_RD failed, no DMA mapping exists.\n");
        ret_val = EINVAL;
        break;
      }

      /*
       * The adapters DMA engine mapping has been setup to the VMEbus.
       * If the caller has specified a user system address to map, then
       * wire the users memory down so that it will not be swapped out
       * during hardware DMA. If two different DMA buffers are used
       * from user space, then wiring and unwiring the system memory
       * needs to be done prior to and after the hardware DMA.
       */
      if (sc->blk_rd_proc_p) {
        if (vm_map_pageable(current_task()->map,
                            trunc_page(sc->blk_rd_buf_ptr),
                            round_page(sc->blk_rd_buf_ptr +
                                       sc->blk_rd_vme_size),
                            (VM_PROT_READ | VM_PROT_WRITE))) {
          printf("DO_DMA_BLK_RD - vm_map_pageable failed wiring\n");
          data->data[0]         = (unsigned long)0;
          return(EIO);
        }
      }

      /*
       * perform the actual hardware DMA
       */
      data->data[0] = (unsigned long)vba_dma(ctlr,sc->blk_rd_dma_handle);

      /*
       * If user memory was specified to SETUP_DMA_BLK_RD ioctl,
       * unwire the memory.
       */
      if (sc->blk_rd_proc_p) {
        if (vm_map_pageable(current_task()->map,
                            trunc_page(sc->blk_rd_buf_ptr),
                            round_page(sc->blk_rd_buf_ptr +
                                       sc->blk_rd_vme_size),
                            (VM_PROT_NONE))) {
          printf("DO_DMA_BLK_RD: vm_map_pageable failed unwiring\n");
          ret_val = EIO;
        }
      }

      break;
    case CLR_DMA_BLK_RD:
      /***************************************************
       * The CLR_DMA_BLK_RD ioctl releases Block Mode    *
       * resources previously mapped by ioctl interface  *
       * SETUP_DMA_BLK_RD. No information is needed in   *
       * the ioctl data parameter.                       *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: CLR_DMA_BLK_RD\n");
      if (sc->blk_rd_dma_handle) {
        /*
         * Unmap the previously mapped system memory to VMEbus mapping
         */
        dma_map_unload (DMA_DEALLOC, sc->blk_rd_dma_handle);

        sc->blk_rd_vme_addr   = 0;
        sc->blk_rd_vme_am     = 0;
        sc->blk_rd_vme_size   = 0;
        sc->blk_rd_dma_handle = (dma_handle_t)NULL;
        sc->blk_rd_buf_ptr    = (caddr_t)NULL;
        sc->blk_rd_proc_p     = (struct proc *)NULL;
      } else {
        printf("dmaex_ioctl: CLR_DMA_BLK_RD, no VME to unmap.\n");
        ret_val = ENXIO;
      }
      break;
    case SET_INT_HANDLER:
      /***************************************************
       * The SET_INT_HANDLER ioctl installs an interrupt *
       * service interface to receive VMEbus interrupts  *
       * at the specified VMEbus IRQ and VECTOR.  The    *
       * interrupt service interface installed will use  *
       * Digital UNIX's psignal interface to wakeup the  *
       * user level application when a VMEbus interrupt  *
       * has been received on the specified vector and   *
       * VME interrupt request line. The VMEbus IRQ and  *
       * vector are passed in the ioctl data parameter.  *
       *   data->data[0] = VMEbus IRQ                    *
       *   data->data[1] = VMEbus interrupt vector       *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: SET_INT_HANDLER\n");

      if (sc->rcvisr_id_t) {
        printf("dmaex_ioctl: SET_INT_HANDLER handler previously installed\n");
        ret_val = EBUSY;
      } else {
        /*
         * check that a legal VMEbus IRQ was specified
         */
        irq    = (unsigned int)data->data[0];
        vector = (unsigned int)data->data[1];
        if ((irq < 1) || (irq > 7)) {
          printf("dmaex_ioctl: SET_INT_HANDLER, invalid IRQ specified\n");
          ret_val = EINVAL;
        } else {
          /*
           * setup handler_add information for VME
           */
          info.gen_intr_info.configuration_st = (caddr_t) ctlr;
          info.gen_intr_info.intr = dmaex_rcv_int_srv;
          info.gen_intr_info.param = (caddr_t) unit;
          info.gen_intr_info.config_type = CONTROLLER_CONFIG_TYPE;
          info.vec = vector;
          info.irq = (int)irq;
          handler.ih_bus = ctlr->bus_hd;
          handler.ih_bus_info = (char *) &info;

          /*
           * install the interrupt handler
           */
          sc->rcvisr_id_t = handler_add(&handler);
          if (sc->rcvisr_id_t == (ihandler_id_t *)NULL) {
            printf("dmaex_ioctl: SET_INT_HANDLER - error in handler_add\n");
            ret_val = EIO;
          } else {
            if (handler_enable(sc->rcvisr_id_t) != 0) {
              handler_del(sc->rcvisr_id_t);
              sc->rcvisr_irq    = 0;
              sc->rcvisr_id_t   = (ihandler_id_t *)NULL;
              sc->rcvisr_proc_p = (struct proc *)NULL;
              ret_val = EIO;
            } else 
              /*
               * dmaex_rcv_isr_srv interface needs the pointer to
               * the proc structure in order to use interface
               * psignal.
               */
              sc->rcvisr_irq    = info.irq;
              sc->rcvisr_proc_p = (struct proc *)task_to_proc(current_task());
          }
        }
      }
      break;
    case CLR_INT_HANDLER:
      /***************************************************
       * The CLR_INT_HANDLER ioctl will remove the       *
       * interrupt service interface installed with      *
       * ioctl SET_INT_HANDER.                           *
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: CLR_INT_HANDLER\n");
      if (sc->rcvisr_id_t) {
        if (handler_disable(sc->rcvisr_id_t) != 0) {
          printf("dmaex_ioctl: CLR_INT_HANDLER handler_disable failed\n");
          ret_val = EIO;
        } else {
          if (handler_del(sc->rcvisr_id_t) != 0) {
            printf("dmaex_ioctl: CLR_INT_HANDLER handler_del failed\n");
            ret_val = EIO;
          } else {
            sc->rcvisr_irq    = 0;
            sc->rcvisr_id_t   = (ihandler_id_t *)NULL;
            sc->rcvisr_proc_p = (struct proc *)NULL;
          }
        }
      }
      break;
    case VME_POST_IRQ:
      /***************************************************
       * The VME_POST_IRQ ioctl call will post a VME     *
       * interrupt request at the specified IRQ and      *
       * vector. An IACK service interface is provided   *
       * to the vba_post_irq interface. The VME bus      *
       * adapter code, upon detection of the VME IACK    *
       * interrupt, will dispatch to the appropriate     *
       * IACK service interface. The IACK service        *
       * interface provided in this example will signal  *
       * an EVENT indicating that the interrupt request  *
       * has been acknowledged. This ioctl interface     *
       * will wait on the EVENT with a 1 second timeout. *
       * If the IACK interrupt was not recieved within   *
       * the timeout period, the VME interrupt request   *
       * will be dismissed with a call to the            *
       * vba_clear_irq interface. If the IACK EVENT was  *
       * signaled, then the ioctl interface will return  *
       * success.  The VME interrupt request IRQ and     *
       * vector is provided to this interface in the     *
       * data parameter.                                 *
       *   data->data[0] = VME IRQ level                 *
       *   data->data[1] = VME interrupt vector to       *
       *                   present upon acknowledgement  *
       *                   of the interrupt request      * 
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: VME_POST_IRQ\n");

      /*
       * Optain the VME IRQ and vector from the ioctl interface
       * data parameter. Check for IRQ validity before posting
       * the requested IRQ and vector.
       */
      irq    = (unsigned int)data->data[0];
      vector = (unsigned int)data->data[1];
      if ((irq < 1) || (irq > 7)) {
        printf("dmaex_ioctl: VME_POST_IRQ, invalid IRQ specified\n");
        ret_val = EINVAL;
        break;
      }
      /*
       * Clear the event and post VMEbus Interrupt Request at
       * the specified VMEbus IRQ and vector.
       */
      event_clear((event_t *)&sc->post_iack_event[irq]);
      ret_val = vba_post_irq(ctlr,irq,vector,dmaex_iack_isr);
      switch (ret_val) {
        case 0:
          printf("dmaex_ioctl: Interrupt could not be posted\n");
          ret_val = EBUSY;
          break;
        case 1:
          /*
           * The interrupt has been posted, wait up to one second
           * for the IACK interrupt in response to the IRQ being
           * acknowledged.
           */
          ret_val = event_wait((event_t *)&sc->post_iack_event[irq],
                               TRUE,
                               1 * hz);
          if (ret_val) {
            printf("dmaex_ioctl: Failed to receive IACK interrupt\n");
            if (vba_clear_irq(ctlr,irq,vector) == FALSE)
              printf("Failed to clear posted IRQ\n");
            ret_val = EIO;
          }
          event_clear((event_t *)&sc->post_iack_event[irq]);
          break;
        case -1:
          printf("dmaex_ioctl: Interface not supported\n");
          ret_val = ENOTSUP;
          break;
        default:
          printf("dmaex_ioctl: Unexpected status returned\n");
          ret_val = EIO;
          break;
      }
      break;
    case VME_CLR_IRQ:
      /***************************************************
       * The VME_CLR_IRQ ioctl will clear an interrupt   *
       * request previously specified by VME_POST_IRQ.   *
       * The VME IRQ and Vector is passed in the data    *
       * parameter of the ioctl interface.               *
       *    data->data[0] = VME IRQ level to clear       *
       *    data->data[1] = VME vector                   * 
       ***************************************************/
      DMAEX_DBG2("dmaex_ioctl: VME_CLR_IRQ\n");

      /*
       * Optain the VME IRQ and vector from the ioctl interface
       * data parameter. Check for IRQ validity before clearing
       * the requested IRQ and vector. If vector specification
       * equals zero, unconditionally clear interrupt request at
       * specified IRQ. If vector is non-zero, it must match the
       * vector previously specified to VME_POST_IRQ at the
       * specified IRQ.
       */
      irq    = (unsigned int)data->data[0];
      vector = (unsigned int)data->data[1];
      if ((irq < 1) || (irq > 7)) {
        printf("dmaex_ioctl: VME_CLR_IRQ, invalid IRQ specified\n");
        ret_val = EINVAL;
        break;
      }
      if (vba_clear_irq(ctlr,irq,vector) == FALSE) {
        printf("IRQ not cleared or invalid vector/irq specified\n");
        ret_val = EIO;
      }
      break;
    default:
      ret_val = EINVAL;
      break;
  }
  return(ret_val);
}

/***************************************************
 *     Read and Write Device Section               *
 *                                                 *
 *--------------- dmaex_read ----------------------*
 *                                                 *
 * The dmaex_read interface calls the physio kernel* 
 * interface to perform the buffer lock, check the *
 * buffer, and set up the I/O packet.  The physio  *
 * interface calls the dmaex_strategy interface to *
 * access the device.                              *
 *                                                 *
 * Depending on the drivers strategy transfer mode *
 * (sc->strategy_xfer_mode), the read can be       *
 * performed by programmed I/O, block mode DMA     *
 * using the VME adapters DMA engine, or device    *
 * DMA using the VME device's DMA engine.          *
 ***************************************************/

dmaex_read(dev_t      dev,  /* Major/minor device number */
           struct uio *uio, /* Pointer to uio structure */ 
           int        flag) /* Access mode of device */
{

  /***************************************************
   * The following code:                             *
   *                                                 *
   *   o Initializes a unit variable to the minor    *
   *     device number                               * 
   *                                                 *
   *   o Determines if the minor device number is    *
   *     greater than the maximum.  If so, return    *
   *     EIO to indicate an I/O error.               *
   *                                                 *
   *   o Otherwise, call physio to perform the       *
   *     buffer lock, check the buffer, and set up   *
   *     the I/O packet.                             *
   ***************************************************/

  register int                unit = minor(dev); 
  register struct dmaex_softc *sc  = &dmaex_softc[unit];
  register struct buf         *bp  = &sc->io_buf;

  DMAEX_DBG1("dmaex_read: dmaex%d\n",unit);

  if ((unit >= NDMAEX ) || (unit < 0))
    return(EIO); 

  if (sc->strategy_xfer_mode == BLOCK_DMA_MODE) {
    if (!(sc->dma_is_set)) {
      printf("dmaex_read: ioctl:SET_STRATEGY_INFO_BLK_DMA not invoked\n");
      return(EINVAL);
    } else {
      /*
       * Copy starting VME address - this is needed
       * to progress the VMEbus address on multiple
       * iterations of the strategy interface
       */
      sc->dma_wrk_vme_addr = sc->dma_vme_addr;
      sc->dma_wrk_vme_size = 0;
    }
  } else {
    if (sc->strategy_xfer_mode == PIO_XFER_MODE) {
      if (!(sc->vme_handle)) {
        printf("dmaex_read: ioctl:SETUP_VME_FOR_STRATEGY_PIO not invoked\n");
        return(EINVAL);
      } else {
        /*
         * Copy starting VME io_handle - this is needed
         * to progress the VMEbus address on multiple
         * iterations of the dmaex_strategy interface
         */
        sc->vme_wrk_handle = sc->vme_handle;
        sc->vme_wrk_size   = 0;
      }
    }
  }

  bzero(bp,sizeof(struct buf));
  return (physio(dmaex_strategy, bp, dev, B_READ, dmaex_minphys, uio)); 
}

/***************************************************
 *                                                 *
 *--------------- dmaex_write ---------------------*
 *                                                 *
 * The dmaex_write interface calls the physio      * 
 * kernel interface to perform the buffer lock,    *
 * check the buffer, and set up the I/O packet.    *
 * The physio interface calls the dmaex_strategy   *
 * interface to access the device.                 *
 *                                                 *
 * Depending on the drivers strategy transfer mode *
 * (sc->strategy_xfer_mode), the write can be      *
 * performed by programmed I/O, block mode DMA     *
 * using the VME adapters DMA engine, or device    *
 * DMA using the VME device's DMA engine.          *
 ***************************************************/

dmaex_write(dev_t      dev,      /* Major/minor device number */
            struct uio *uio,     /* Pointer to uio structure  */ 
            int        flag)     /* Access mode of device     */
{

  /***************************************************
   * The following code:                             *
   *                                                 *
   *   o Initializes a unit variable to the minor    *
   *     device number                               * 
   *                                                 *
   *   o Determines if the minor device number is    *
   *     greater than the maximum.  If so, return    *
   *     EIO to indicate an I/O error.               *
   *                                                 *
   *   o Otherwise, call physio to perform the       *
   *     buffer lock, check the buffer, and set up   *
   *     the I/O packet.                             *
   ***************************************************/

  register int                unit = minor(dev); 
  register struct dmaex_softc *sc  = &dmaex_softc[unit];
  register struct buf         *bp  = &sc->io_buf;

  DMAEX_DBG1("dmaex_write: dmaex%d\n",unit);

  if ((unit >= NDMAEX) || (unit < 0))
    return (EIO);

  if (sc->strategy_xfer_mode == BLOCK_DMA_MODE) {
    if (!(sc->dma_is_set)) {
      printf("dmaex_write: ioctl:SET_STRATEGY_INFO_BLK_DMA not invoked\n");
      return(EINVAL);
    } else {
      /*
       * Copy starting VME address - this is needed
       * to progress the VMEbus address on multiple
       * iterations of the strategy interface
       */
      sc->dma_wrk_vme_addr = sc->dma_vme_addr;
      sc->dma_wrk_vme_size = 0;
    }
  } else {
    if (sc->strategy_xfer_mode == PIO_XFER_MODE) {
      if (!(sc->vme_handle)) {
        printf("dmaex_write: ioctl:SETUP_VME_FOR_STRATEGY_PIO not invoked\n");
        return(EINVAL);
      } else {
        /*
         * Copy starting VME io_handle - this is needed
         * to progress the VMEbus address on multiple
         * iterations of the dmaex_strategy interface
         */
        sc->vme_wrk_handle = sc->vme_handle;
        sc->vme_wrk_size   = 0;
      }
    }
  }

  bzero(bp,sizeof(struct buf));
  return (physio(dmaex_strategy, bp, dev, B_WRITE, dmaex_minphys, uio)); 
}

/***************************************************
 *                                                 *
 *--------------- dmaex_minphys -------------------*
 *                                                 *
 * This interface is specified as a parameter to   *
 * the physio interface invoked from dmaex_read or *
 * dmaex_write. Physio, will call this interface   *
 * once or several times depending on the          *
 * requested transfer size. It is called prior to  *
 * each invokation of dmaex_strategy from physio.  *
 *                                                 *
 * This interface is used to limit the size of the *
 * transfer. This may be a software limit to       *
 * throttle the transfer or a hardware limit       *
 * imposed by hardware. If the requested byte      *
 * count from physio exceeds the maximum byte      *
 * count specified (dmaex_max_dma), dmaex_max_dma  *
 * will be returned to the physio interface. The   *
 * physio interface will then call the strategy    *
 * interface with this byte count to perform the   *
 * transfer.                                       *
 ***************************************************/

dmaex_minphys (register struct buf *bp)
{

  DMAEX_DBG1("dmaex_minphys: entered\n");

  if (bp->b_bcount > dmaex_max_dma)  
    bp->b_bcount = dmaex_max_dma;
  return; 
}

/***************************************************
 *     Strategy Section                            *
 *                                                 *
 *--------------- dmaex_strategy ------------------*
 *                                                 *
 * The dmaex_strategy interface is called by the   *
 * phsyio kernel interface.  (The physio interface *
 * is called by the dmaex_read and dmaex_write     *
 * driver interfaces.)                             *
 *                                                 *
 * Depending on the strategy transfer mode         *
 * (sc->strategy_xfer_mode), the transfer can be   *
 * performed by programmed I/O, by the VME         *
 * adapters DMA engine for block mode ransfers, or *
 * by the VME devices DMA engine.                  *
 *                                                 *
 * The maximum size of the transfer is controlled  *
 * the dmaex_minphys interface. Dmaex_minphys is   *
 * called by physio prior to the invokation of     *
 * this interface by physio.                       *
 ***************************************************/

dmaex_strategy(struct buf  *bp) /* Pointer to buf structure */
{

  /***************************************************
   * The following code initializes:                 *
   *                                                 *
   *   o A unit variable to the minor device number  *
   *                                                 *
   *   o A controller structure to its associated    *
   *     DMAEX device                                *
   *                                                 *
   *   o A dmaex_softc structure to the address of   *
   *     the dmaex_softc associated with this        *
   *     DMAEX device                                *  
   *                                                 *
   *   o A variable to store read, write, and enable *
   *     interrupts status information.              *
   *                                                 *
   *   o A sg_entry structure pointer that the code  *
   *     uses in calls to dma_get_curr_sqentry and   *
   *     write_io_port.                              * 
   ***************************************************/

  register int                unit = minor(bp->b_dev);  
  register struct controller  *ctlr = dmaex_ctlr[unit]; 
  register struct dmaex_softc *sc = &dmaex_softc[unit];  
  sg_entry_t                  dmaex_sg;
  unsigned long               b_count;
  register int                *mem_ptr;
  int                         flags,i,j;
  char                        csr;  

  DMAEX_DBG1("dmaex_strategy: dmaex%d\n",unit);

  /*************************************************** 
   * Check the validity of the unit number and the   *
   * pointers.                                       *
   ***************************************************/

  if ( (!ctlr) || (!sc) )
    return (EINVAL);

  sc->dma_handle = (dma_handle_t) NULL;

  /*************************************************** 
   * Dispatch to the appropriate I/O transfer        *
   *   PIO_XFER_MODE:   perform PIO operations       *
   *   DEVICE_DMA_MODE: perform device DMA           *
   *   BLOCK_MODE_DMA:  perform Master BLOCK DMA     *
   ***************************************************/
  switch (sc->strategy_xfer_mode) {

    case PIO_XFER_MODE:
      if (bp->b_flags & B_READ) {
        if (sc->vme_am & VME_DENSE) {
          /*
           * Mapped to VMEbus through dense space. Use read_io_port
           * interface to read the data.
           */
          mem_ptr = (int *)bp->b_un.b_addr;
          for (i=0,j=0; i < bp->b_bcount/sizeof(int); i++, j+=sizeof(int))
            *mem_ptr++ = (int)read_io_port(sc->vme_wrk_handle + j,4,0);
        } else {
          /*
           * Use the system io copy function to read data from
           * the VMEbus into users mapped memory
           */
          if (io_copyin(sc->vme_wrk_handle, (vm_offset_t)bp->b_un.b_addr,
                                          (unsigned long)bp->b_bcount) == -1) {
            /*
             * io_copyin indicates an error condition
             */
            bp->b_error = EIO;
            bp->b_flags |= B_ERROR;
            bp->b_resid = bp->b_bcount;
            iodone(bp);
            break;
          }
        }
      } else {
        if (sc->vme_am & VME_DENSE) {
          /*
           * Mapped to VMEbus through dense space. Use write_io_port
           * interface to write the data.
           */
          mem_ptr = (int *)bp->b_un.b_addr;
          for (i=0,j=0; i < bp->b_bcount/sizeof(int); i++, j+=sizeof(int))
            write_io_port(sc->vme_wrk_handle + j,4,0,(long)(*mem_ptr++));
          mb();
        } else {
          /*
           * Use the system io copy function to write data from
           * users mapped memory to the VMEbus
           */
          if (io_copyout((vm_offset_t)bp->b_un.b_addr, sc->vme_wrk_handle,
                                       (unsigned long)bp->b_bcount) == -1) {
            /*
             * io_copyout indicated an error condition
             */
            bp->b_error = EIO;
            bp->b_flags |= B_ERROR;
            bp->b_resid = bp->b_bcount;
            iodone(bp);
            break;
          }
        }
      }

      /***************************************************
       * The transfer has completed successfully. Update *
       * the buffers residual count. Update the          *
       * io_handle and total number of bytes transferred.*
       * It is possible for physio to segment the        *
       * transfer if the transfer size exceeds the value *
       * specified in the dmaex_minphys interface.       *
       ***************************************************/
      bp->b_resid = 0;
      sc->vme_wrk_handle += (unsigned long)bp->b_bcount;
      sc->vme_wrk_size   += (unsigned int)bp->b_bcount;

      /***************************************************
       * return to physio indicating I/O is complete     *
       ***************************************************/
      iodone(bp);
      break;

    case DEVICE_DMA_MODE:

      /***************************************************
       * The following code sets up the DMA mapping      *
       * registers by calling dma_map_load interface.    *
       * Dma_map_load invokes dma_map_alloc interface    *
       * as a result of sc->dma_handle being a NULL.     *
       *                                                 *
       * If the return from dma_map_alloc or             *
       * dma_map_load is zero (0), it could not perform  *
       * the requested operation.  The dmaex_strategy    *
       * interface:                                      * 
       *                                                 *
       *   o Sets the error bit to EOI                   *
       *                                                 *
       *   o Sets b_flags to the bitwise inclusive OR of *
       *     the read and error bits                     *
       *                                                 *
       *   o Completes the I/O operation by calling      *
       *     iodone                                      *
       *                                                 *
       * Return to calling routine (either dmaex_read or *
       * dmaex_write.                                    *
       ***************************************************/
      if (dma_map_load ((unsigned long) bp->b_bcount,
                        (vm_offset_t) bp->b_un.b_addr,
                        bp->b_proc,
                        ctlr,
                        &sc->dma_handle,
                        0,
                        VME_A24_UDATA_D32 | VME_BS_NOSWAP | 
                                            DMA_GUARD_UPPER | DMA_ALL) == 0) {
        bp->b_error = EIO;
        bp->b_flags |= B_ERROR;
        bp->b_resid = bp->b_bcount;
        iodone(bp);
        break; 
      }

      /***************************************************
       * If the requested mapping was performed, set up  *
       * the device for transfer. Obtain the VMEbus      *
       * address and byte count of user memory mapped    *
       * to the VMEbus.                                  *
       ***************************************************/
      dmaex_sg = dma_get_curr_sgentry (sc->dma_handle);

      /***************************************************
       * If the dmaex_no_dev flag indicates that the     *
       * 'dmaex' device is present, start the device DMA *
       ***************************************************/
      if (!dmaex_no_dev) {
        write_io_port (sc->csr_handle + DMAEX_COUNT_OFF,
                       DMAEX_COUNT_SIZE, 0, dmaex_sg->bc);
        mb();

        write_io_port (sc->csr_handle + DMAEX_ADDR_OFF,
                       DMAEX_ADDR_SIZE, 0, (long)dmaex_sg->ba);
        mb();

        csr = (bp->b_flags & B_READ) ? (READ | IE) : IE;

        write_io_port (sc->csr_handle + DMAEX_CSR_OFF, 
                       DMAEX_CSR_SIZE, 0, (csr | DMA_GO));
        mb();
      }

      /***************************************************
       * Wait for a timeout or a dma complete interrupt. *
       * dmaex_int_wait specifies how many seconds to    *
       * wait for an interrupt to occur.                 *
       ***************************************************/
      if (event_wait((event_t *)&sc->isi_event,TRUE,dmaex_int_wait * hz)) {
        DMAEX_DBG1("dmaex_strategy: dmaex%d timed out after %d seconds\n",
                    unit,dmaex_int_wait);
        bp->b_error  = ETIMEDOUT;
        bp->b_flags |= B_ERROR;
        bp->b_resid  = bp->b_bcount;
      }
      event_clear((event_t *)&sc->isi_event);

      /***************************************************
       * Resources freed here versus in interrupt        *
       * service interface for performance reasons       *
       ***************************************************/
      dma_map_unload (DMA_DEALLOC, sc->dma_handle);
      sc->dma_handle = (dma_handle_t) NULL;

      /***************************************************
       * return to physio indicating I/O is complete     *
       ***************************************************/
      iodone(bp);

      break;

    case BLOCK_DMA_MODE:

      /***************************************************
       * Add DMA specific flags to the VME address       *
       * modifier that was specified in ioctl:           *
       * SET_STRATEGY_INFO_BLK_DMA interface.  Also set  *
       * the DMA_IN or DMA_OUT flag depending on whether *
       * the I/O operation is a read or write. The flags *
       * DMA_IN or DMA_OUT, indicate to the VME adapter  *
       * code that the VME adapter's DMA engine is to be *
       * used to perform the DMA transfer.               *
       ***************************************************/
      flags = sc->dma_vme_am | DMA_SLEEP | DMA_GUARD_UPPER | DMA_ALL;
      if (bp->b_flags & B_READ)
        flags |= DMA_IN;
      else
        flags |= DMA_OUT;

      /***************************************************
       * When using the DMA engine, the VMEbus address   *
       * and flags MUST be specified via the interface   *
       * vba_set_dma_addr.                               *
       ***************************************************/
      flags = vba_set_dma_addr(ctlr, flags, sc->dma_wrk_vme_addr);

      /***************************************************
       * Allocate, load and set system resources for the *
       * DMA data transfers.  Upon success, dma_map_load *
       * returns the number of bytes mapped and a        *
       * dma_handle to sc->dma_handle.                   *
       ***************************************************/
      b_count = dma_map_load ((unsigned long) bp->b_bcount,
                              (vm_offset_t) bp->b_un.b_addr,
                              bp->b_proc,
                              ctlr,
                              &sc->dma_handle,
                              0,
                              flags);
      if ( (!b_count) || (!sc->dma_handle) ||
                               (b_count != (unsigned long)bp->b_bcount)) {
        printf("dmaex_strategy: dma_map_load error\n");
        bp->b_error = EIO;
        bp->b_flags |= B_ERROR;
        bp->b_resid = bp->b_bcount;
        iodone(bp);
        break; 
      }

      /***************************************************
       * Obtain VMEbus address and byte count of this    *
       * mapped transfer. The VMEbus address should      *
       * match that of sc->dma_wrk_vme_addr              *
       ***************************************************/
      dmaex_sg = dma_get_curr_sgentry (sc->dma_handle);
      DMAEX_DBG2("dmaex_strategy: sg.ba = 0x%lx sg.bc 0x%lx\n",
                  dmaex_sg->ba, dmaex_sg->bc);

      /***************************************************
       * Perform the actual block mode DMA by invoking   *
       * the vba_dma interface. This interface will      *
       * block until the DMA has completed or an error   *
       * occured. The return value from the interface    *
       * is the actual number of bytes transferred. This *
       * value should be the same as value bp->b_bcount. *
       * Specify the dma_handle returned from interface  *
       * dma_map_load in this interface call.            *
       ***************************************************/

      b_count = vba_dma(ctlr, sc->dma_handle);
      if ((!b_count) || (b_count == -1) || 
                                 (b_count != (unsigned long)bp->b_bcount)) {
        printf("dmaex_strategy: error during DMA transfer\n");
        bp->b_error = EIO;
        bp->b_flags |= B_ERROR;
        bp->b_resid = bp->b_bcount;

        /***************************************************
         * Release the DMA system resources                *
         ***************************************************/
        dma_map_unload(DMA_DEALLOC,sc->dma_handle);
        sc->dma_handle = (dma_handle_t) NULL;

        iodone(bp);
        break; 
      }

      /***************************************************
       * The transfer has completed successfully. Update *
       * the buffers residual count. Update the VMEbus   *
       * address and total number of bytes transferred.  *
       * It is possible for physio to segment the DMA    *
       * transfer if the transfer size exceeds the value *
       * specified in the dmaex_minphys interface.       *
       ***************************************************/
      bp->b_resid = 0;
      sc->dma_wrk_vme_addr += (vme_addr_t)b_count;
      sc->dma_wrk_vme_size += (unsigned int)b_count;

      /***************************************************
       * Release the DMA system resources                *
       ***************************************************/
      dma_map_unload(DMA_DEALLOC,sc->dma_handle);
      sc->dma_handle = (dma_handle_t) NULL;

      iodone(bp);
      break;

  } /* end of strategy_xfer_mode */

  return;
}

/***************************************************
 *     Interrupt Section                           *
 *                                                 *
 *--------------- dmaex_intr1 ---------------------*
 *                                                 *
 * The dmaex_intr1 checks the results of the DMA   *
 * transfer and then post an event to wakeup the   *
 * dmaex_strategy interface to complete the I/O    *
 * operation. 
 *                                                 *
 ***************************************************/

dmaex_intr1(int unit) /* Logical unit number for device */
{
  /***************************************************
   * The following code initializes:                 *
   *                                                 *
   *   o A controller structure to its associated    *
   *     DMAEX device                                *
   *                                                 *
   *   o A dmaex_softc structure to the address of   *
   *     the dmaex_softc structure associated with   *
   *     this DMAEX device                           *  
   *                                                 *
   * The code also declares a variable to store the  *
   * return from vme_read_byte and a pointer to a    *
   * buf structure.                                  *
   *                                                 *
   ***************************************************/

  register struct controller  *ctlr = dmaex_ctlr[unit]; 
  register struct dmaex_softc *sc   = &dmaex_softc[unit]; 
  register struct buf         *bp   = &sc->io_buf; 
  char                        csr; 

  DMAEX_DBG2("dmaex_intr1: dmaex%d\n",unit);

  /***************************************************
   *                                                 *
   * Call read_io_port to read a byte from the       *
   * VMEbus.  If the error bit is set, an error      *
   * occurred.  The code does the following:         *
   *                                                 *
   *   o Sets the error bit in b_error               *
   *                                                 *
   *   o Sets b_flags to the bitwise inclusive OR    *
   *     of the read and error bits to indicate      *
   *     an error occurred on this buffer            *
   *                                                 *
   ***************************************************/

  if (!dmaex_no_dev) {
    /***************************************************
     * The software flag indicates that a 'dmaex'      *
     * device is present, read the device csr register *
     ***************************************************/
    csr = read_io_port (sc->csr_handle + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0);
    if (csr & ERROR) { 
      bp->b_error = EIO;
      bp->b_flags |= B_ERROR;
    }
  }

  /***************************************************
   *                                                 *
   * The following code:                             *
   *                                                 *
   *   o Records the number of bytes remaining in    *
   *     b_resid                                     *
   *                                                 *
   *   o Call wakeup returning to strategy()         *
   *                                                 *
   ***************************************************/

  if (dmaex_no_dev)
    /***************************************************
     * The software flag indicates that the 'dmaex'    *
     * device is not present. Indicate success by      *
     * clearing the number of bytes remaining          *
     ***************************************************/
    bp->b_resid = 0;
  else
    bp->b_resid = read_io_port(sc->csr_handle + DMAEX_COUNT_OFF, 
                               DMAEX_COUNT_SIZE, 0); 

  /***************************************************
   * Interrupt service interfaces executing at       *
   * SPLDEVRT spl(6) must not call kernel interfaces *
   * directly. The rt_post_callout function allows   *
   * the calling process to defer execution  of a    *
   * function until a time when kernel interface     *
   * routines can be invoked. The function invoked   *
   * by rt_post_callout runs at an elevated spl and  *
   * is subject to the same restrictions as an       *
   * interrupt service interface.                    *
   ***************************************************/

  rt_post_callout(event_post, (long)&sc->isi_event, (long)NULL);

  /***************************************************
   * If the driver writer knows that VME interrupts  *
   * are NOT dispatched at SPLDEVRT. Then the        *
   * following line of code can be substituted for   *
   * the previous line of code.                      *
   *                                                 *
   *   event_post( (event_t *)&sc->isi_event);       *
   *                                                 *
   ***************************************************/
}

/***************************************************
 *     Interrupt Section                           *
 *                                                 *
 *--------------- dmaex_intr2 ---------------------*
 *                                                 *
 * Handle other interrupts from dmaex controller.  *
 * This, and associated code in dmaex_probe() are  *
 * included only for the purpose of illustrating   *
 * how multiple interrupts and there handlers are  *
 * registered.  This has no functional meaning in  *
 * this example.                                   *
 *                                                 *
 ***************************************************/

dmaex_intr2(int unit) /* Logical unit number for device */
{
  register struct dmaex_softc *sc = &dmaex_softc[unit]; 
  register struct buf         *bp = &sc->io_buf; 

  DMAEX_DBG2("dmaex_intr2: dmaex%d\n",unit);

  bp->b_error = EIO;
  bp->b_flags |= B_ERROR;
  bp->b_resid = bp->b_bcount;

  /***************************************************
   * Interrupt service interfaces  executing  at     *
   * SPLDEVRT spl(6) must not call kernel interfaces *
   * directly. The rt_post_callout function allows   *
   * the calling process to defer execution  of a    *
   * function until a time when kernel interface     *
   * routines can be invoked. The function invoked   *
   * by rt_post_callout runs at an elevated spl and  *
   * is subject to the same restrictions as an       *
   * interrupt service interface.                    *
   ***************************************************/

  rt_post_callout(event_post, (long)&sc->isi_event, (long)NULL);

  /***************************************************
   * If the driver writer knows that VME interrupts  *
   * are not dispatched at SPLDEVRT. Then the        *
   * following line of code can be substituted for   *
   * the previous line of code.                      *
   *                                                 *
   *   event_post( (event_t *)&sc->isi_event);       *
   *                                                 *
   ***************************************************/
}

/***************************************************
 *     Interrupt Section                           *
 *                                                 *
 *------------ dmaex_iack_isr ---------------------*
 *                                                 *
 * This interrupt service interface is dispatched  *
 * to by the VME adapter's IACK interrupt service  *
 * interface as a result of a VME IACK interrupt.  *
 * The VME IACK interrupt occurs in repsonse to a  *
 * VME posted interrupt request from ioctl         *
 * VME_POST_IRQ. The interface will post an event  *
 * to the ioctl interface to indicate that the     *
 * posted interrupt has been acknowledged.         *
 ***************************************************/

int
dmaex_iack_isr(struct controller *ctlr, unsigned int irq)
{
  register struct dmaex_softc *sc = &dmaex_softc[ctlr->ctlr_num]; 

  DMAEX_DBG1("dmaex_iack_isr: dmaex%d\n",ctlr->ctlr_num);
  event_post( (event_t *)&sc->post_iack_event[irq]);
}

/***************************************************
 *     Interrupt Section                           *
 *                                                 *
 *---------- dmaex_post_psignal -------------------*
 *                                                 *
 * This interface is invoked by rt_post_callout    *
 * interface if the VME interrupt was received at  *
 * SPLDEVRT. The interface issues a psignal to     *
 * SIGUSR1 to wakeup a user task.                  *
 *                                                 *
 ***************************************************/

int
dmaex_post_psignal(struct proc *proc_p,long signal)
{
  psignal(proc_p,signal);
}

/***************************************************
 *     Interrupt Section                           *
 *                                                 *
 *------------ dmaex_rcv_int_isr ------------------*
 *                                                 *
 * This interface is dispatched to when a VMEbus   *
 * interrupt has been received at the VMEbus       *
 * vector and VMEbus interrupt request specified   *
 * by SET_INT_HANDLER ioctl. This interface will   *
 * either wakeup a user task by issuing a          *
 * psignal to SIGUSR1 or invoke a function to      *
 * issue the psignal. This is done with interface  *
 * rt_post_callout when the interrupt service      *
 * interface is at system SPL of SPLDEVRT.         *
 *                                                 *
 ***************************************************/

int
dmaex_rcv_int_srv(int unit)
{
  register struct dmaex_softc *sc = &dmaex_softc[unit]; 

  /*
   * On several VME systems, interrupts that are received
   * on VMEbus interrupt requesl level 7 are dispatched to
   * the interrupt service interface at SPLDEVRT spl(6).
   * In these cases, signaling a kernel or user thread must
   * be accomplished by using rt_post_callout interface.
   */
  if (sc->rcvisr_irq == 7)
    rt_post_callout(dmaex_post_psignal,(long)sc->rcvisr_proc_p,(long)SIGUSR1);
  else
    psignal(sc->rcvisr_proc_p,SIGUSR1);
}