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

6    VMEbus Device Driver Example

This chapter provides you with an opportunity to study a VMEbus device driver called /dev/dmaex. Although this driver does not operate on a real device, you can use it as the basis for writing your own working VMEbus device drivers. The /dev/dmaex device driver supports three methods of data transfer over the 32-Bit VMEbus: parallel I/O (PIO), block-mode direct memory access (DMA), and device DMA.

The /dev/dmaex driver shows you how to write a single binary model device driver that operates on the VMEbus and can be statically or dynamically configured into the kernel. You can compile this driver into a single binary module, dmaex.mod, that can be:

Table 6-1 lists the parts of the /dev/dmaex device driver and the sections of the chapter where each is listed and described.


Table 6-1: Parts of the /dev/dmaex Device Driver
PartSection
Device Register header file dmaexreg.h  Section 6.1 
Include Files Section  Section 6.2 
Declarations Section  Section 6.3 
Configure Section  Section 6.4 
Autoconfiguration Support Section  Section 6.5 
Open and Close Device Section  Section 6.6 
Memory Map (mmap) Section  Section 6.7 
Driver-Specific ioctl Section  Section 6.8 
Read and Write Device Section  Section 6.9 
Strategy Section  Section 6.10 
Interrupt Sections  Section 6.11 

The source code uses the following convention:

#define IE 0001 [1]
  1. --> Numbers appear after some line or lines of code in the /dev/dmaex device driver example. Following the example, a corresponding number appears that contains an explanation for the associated line or lines. Some inline comments have been removed from the displayed source code. If you prefer to read the /dev/dmaex driver source code in its entirety with the inline comments, see Appendix B.


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

6.1    Device Register Header File dmaexreg.h

The following code shows dmaexreg.h, the device register header file for the /dev/dmaex device driver. It contains public declarations and the device register structure for the device.
#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       [1]
#define DMAEX_COUNT_OFF 1       [2]
#define DMAEX_ADDR_OFF  4       [3]

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

#define DMAEX_CSR_SIZE          sizeof(char) [4]
#define DMAEX_COUNT_SIZE        sizeof(short)
#define DMAEX_ADDR_SIZE         sizeof(vme_addr_t)

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

#define  IE         0001   [5]
#define  DMA_GO     0002   [6]
#define  RESET      0010   [7]
#define  ERROR      0020   [8]
#define  READ       0040   [9]

/***************************************************
 *         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)
  1. --> Defines a device register byte offset that specifies a 1-byte control status register (CSR).

  2. --> Defines a device register byte offset that specifies a 16-bit byte count.

  3. --> Defines a device register byte offset that specifies the 32-bit VMEbus transfer address.

  4. --> Defines a convenience constant for a call to the sizeof operator. The next two lines define similar convenience constants. The /dev/dmaex device driver passes these constants to a variety of kernel interfaces.

  5. --> Defines a constant that represents the interrupt enable bit. Section 6.10 shows how the dmaex_strategy interface uses this bit to set up the device for transfer of data.

  6. --> Defines a constant that represents the start DMA bit. Section 6.5 shows how the dmaex_probe interface uses this bit to reset the device.

  7. --> Defines a constant that represents the reset bit. Section 6.5 shows how the dmaex_probe interface uses this bit to instruct the device to reset itself in preparation for data transfer operations.

  8. --> Defines a constant that represents the error bit. Section 6.5 shows how the dmaex_probe interface uses this bit to indicate that the device is broken.

  9. --> Defines a constant that represents the data-transfer-is-read/write bit. Section 6.10 shows how the dmaex_strategy interface uses this bit to indicate a read operation.


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

6.2    Include Files Section

The following code shows the Include Files Section for the /dev/dmaex device driver:
#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> [1]
#include        <machine/cpu.h>

#include        "dmaexreg.h"         [2]

/****************************************************
 * 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
  1. --> Includes the /usr/sys/include/io/dec/vme/vbareg.h file, which is specific to VMEbus device drivers. This file contains definitions for the different VMEbus adapters. For a summary description of this header file, see Section A.2.

  2. --> Includes the device register header file, which contains the device register offset definitions that describe the DMAEX device. This device register header file is the one created for the DMAEX device.

    Section 6.1 shows the device offset definitions for this device. Writing Device Drivers: Reference provides reference page-style descriptions of the header files most commonly used by Digital UNIX device drivers.


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

6.3    Declarations Section

The following code shows the Declarations Section for the /dev/dmaex device driver:
/****************************************************
 *     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 */
}; 


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

6.4    Configure Section

Table 6-2 lists the interfaces implemented as part of the Configure Section along with the sections in the book where each is listed and described.
Table 6-2: Interfaces Implemented as Part of the Configure Section
PartSection
Providing Declarations Required for Configuration  Section 6.4.1 
Implementing the dmaex_configure Interface  Section 6.4.2 


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

6.4.1    Providing Declarations Required for Configuration

The following code shows the decalaration required for driver configuration:
/****************************************************
 *                                                  *
 * 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}
};


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

6.4.2    Implementing the dmaex_configure Interface

The following code shows the implementation of the dmaex_configure interface:
/*****************************************************
 *           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); 
}


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

6.4.2.1    Implementing register_configuration

The following code shows the implementation of the register_configuration interface, which is called by dmaex_configure:
/***************************************************
 *       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);
}


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

6.4.2.2    Implementing register_major_number

The following code shows the implementation of the register_major_number interface, which is called by dmaex_configure:
/***************************************************
 *        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);
}


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

6.4.2.3    Implementing callback_register_configuration

The following code shows the implementation of the callback_register_configuration interface, which is used by dmaex_configure:
/***************************************************
 *   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;
  }
}


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

6.4.2.4    Implementing callback_register_major_number

The following code shows the implementation of the callback_register_major_number interface, which is used by dmaex_configure:
/***************************************************
 *     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;
  }
}


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

6.5    Autoconfiguration Support Section

Table 6-3 lists the interfaces implemented as part of the Autoconfiguration Support Section along with the sections in the book where each is described.
Table 6-3: Interfaces Implemented as Part of the Autoconfiguration Support Section
PartSection
Implementing the dmaex_probe Interface  Section 6.5.1 
Implementing the dmaex_cattach Interface  Section 6.5.2 
Implementing the dmaex_ctlr_unattach Interface  Section 6.5.3 


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

6.5.1    Implementing the dmaex_probe Interface

The following code shows the implementation of the dmaex_probe interface:
/*****************************************************
 *     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 */
}


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

6.5.2    Implementing the dmaex_cattach Interface

The following code shows the implementation of the dmaex_cattach interface:
/***************************************************
 *     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;
}


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

6.5.3    Implementing the dmaex_ctlr_unattach Interface

The following code shows the implementation of the dmaex_ctlr_unattach interface:
/***************************************************
 *        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);
}


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

6.6    Open and Close Device Section

Table 6-4 lists the two interfaces implemented as part of the Open and Close Device Section along with the sections in the book where each is described.
Table 6-4: Interfaces Implemented as Part of the Open and Close Device Section
PartSection
Implementing the dmaex_open Interface  Section 6.6.1 
Implementing the dmaex_close Interface  Section 6.6.2 


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

6.6.1    Implementing the dmaex_open Interface

The following code shows the implementation of the dmaex_open interface:
/***************************************************
 *     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); 
}


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

6.6.2    Implementing the dmaex_close Interface

The following code shows the implementation of the dmaex_close interface:
/***************************************************
 *                                                 *
 *--------------- 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); 
}


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

6.7    Memory Map (mmap) Section

The following code shows the Memory Map Section for the /dev/dmaex device driver:
/***************************************************
 *                                                 *
 *--------------- 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);

}


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

6.8    Driver-Specific ioctl Section

Table 6-5 lists the two top-level interfaces implemented as part of the ioctl Section along with the sections in the book where each is described.
Table 6-5: Interfaces Implemented as Part of the ioctl Section
PartSection
Implementing a dmaex_setup_blk_mode Interface  Section 6.8.1 
Implementing the dmaex_ioctl Interface  Section 6.8.2 


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

6.8.1    Implementing a dmaex_setup_blk_mode Interface

The following code shows the implementation of the dmaex_setup_blk_mode interface:
/***************************************************
 *                                                 *
 *--------- 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);
}


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

6.8.2    Implementing the dmaex_ioctl Interface

The following code shows the implementation of the dmaex_ioctl interface; the case statements for each of the 28 ioctl operations supported have been broken out from this code into individual subsections:
/***************************************************
 *                                                 *
 *--------------- 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:
      ...
      break;
    .
    .
    .
    case VME_CLR_IRQ:
      ...
      break;
    default:
      ret_val = EINVAL;
      break;
  }
  return(ret_val);
}


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

6.8.2.1    Implementing SET_MMAP_MODE

    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;


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

6.8.2.2    Implementing GET_MMAP_MODE

    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;


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

6.8.2.3    Implementing SET_STRATEGY_XFER_MODE

    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;


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

6.8.2.4    Implementing GET_STRATEGY_XFER_MODE

    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;


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

6.8.2.5    Implementing SETUP_VME_FOR_MMAP_PIO

    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;


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

6.8.2.6    Implementing GET_VME_INFO_FOR_MMAP_PIO

    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;


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

6.8.2.7    Implementing UNMAP_VME_FOR_MMAP_PIO

    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;


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

6.8.2.8    Implementing SET_STRATEGY_INFO_BLK_DMA

    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;


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

6.8.2.9    Implementing GET_STRATEGY_INFO_BLK_DMA

    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;


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

6.8.2.10    Implementing CLR_STRATEGY_INFO_BLK_DMA

    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;


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

6.8.2.11    Implementing SETUP_VME_FOR_STRATEGY_PIO

    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;


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

6.8.2.12    Implementing GET_VME_INFO_FOR_STRATEGY_PIO

    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;


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

6.8.2.13    Implementing UNMAP_VME_FOR_STRATEGY_PIO

    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;


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

6.8.2.14    Implementing MAP_SYS_MEM_TO_VME

    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;
      } 


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

6.8.2.15    Implementing GET_SYS_MEM_INFO

    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;


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

6.8.2.16    Implementing UNMAP_SYS_MEM_TO_VME

    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;


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

6.8.2.17    Implementing SETUP_DMA_BLK_WRT

    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;


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

6.8.2.18    Implementing GET_DMA_BLK_WRT

    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;


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

6.8.2.19    Implementing DO_DMA_BLK_WRT

    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;


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

6.8.2.20    Implementing CLR_DMA_BLK_WRT

    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;


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

6.8.2.21    Implementing SETUP_DMA_BLK_RD

    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;


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

6.8.2.22    Implementing GET_DMA_BLK_RD

    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;


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

6.8.2.23    Implementing DO_DMA_BLK_RD

    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;


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

6.8.2.24    Implementing CLR_DMA_BLK_RD

    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;


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

6.8.2.25    Implementing SET_INT_HANDLER

    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;


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

6.8.2.26    Implementing CLR_INT_HANDLER

    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;


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

6.8.2.27    Implementing VME_POST_IRQ

    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;


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

6.8.2.28    Implementing VME_CLR_IRQ

    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;


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

6.9    Read and Write Device Section

Table 6-6 lists the two interfaces implemented as part of the Read and Write Device Section for the /dev/dmaex device driver along with the sections in the book where each is described.
Table 6-6: Interfaces Implemented as Part of the Read and Write Device Section
PartSection
Implementing the dmaex_read Interface  Section 6.9.1 
Implementing the dmaex_write Interface  Section 6.9.2 
Implementing the dmaex_minphys Interface  Section 6.9.3 


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

6.9.1    Implementing the dmaex_read Interface

The following code shows the implementation of the dmaex_read interface:
/***************************************************
 *     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,  [1]
           struct uio *uio, [2]
           int        flag) [3]
{

  /***************************************************
   * 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); [4]
  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);  [5]

  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)); [6]
}
  1. --> Declares an argument that specifies the major and minor numbers for a specific DMAEX device. The minor device number is used to determine the logical unit number for the device on which the read operation is performed.

  2. --> Declares a pointer to a uio structure. This structure contains the information for transferring data to and from the address space of the user's process. You typically pass this pointer unmodified to the uiomove or physio kernel interface. This driver passes the pointer to physio.

  3. --> Declares an argument that specifies the access mode of the device. The access modes are represented by a bit mask of flags defined in /usr/sys/include/sys/fcntl.h. The dmaexread interface does not use this argument.

  4. --> Declares a unit variable and initializes it to the device minor number. Note the use of the minor interface to obtain the device minor number.

    The minor interface takes one argument: the number of the device for which an associated device minor number will be obtained. The minor number is encoded in the dev argument, which is of type dev_t.

  5. --> If the device minor number is greater than the number of devices on the system, returns the error constant EIO to indicate an I/O error.

  6. --> Calls the physio kernel interface to implement raw I/O. The physio interface takes six arguments:


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

6.9.2    Implementing the dmaex_write Interface

The following code shows the implementation of the dmaex_write interface:
/***************************************************
 *                                                 *
 *--------------- 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)); [1]
}
  1. --> The dmaexwrite interface is almost identical to the dmaexread interface. The only difference is that dmaexwrite uses the B_WRITE bit instead of the B_READ bit for the read/write flag to indicate that this is a write operation.


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

6.9.3    Implementing the dmaex_minphys Interface

The following code shows the implementation of the dmaex_minphys interface:
/***************************************************
 *                                                 *
 *--------------- 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; 
}


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

6.10    Strategy Section

The following code shows the Strategy Section for the /dev/dmaex device driver; the case statements for each of the three data transfer modes supported have been broken out from this code into individual subsections:
/***************************************************
 *     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:
      ...
      break;

    case DEVICE_DMA_MODE:
      ...
      break;

    case BLOCK_DMA_MODE:
      ...
      break;

  } /* end of strategy_xfer_mode */

  return;
}


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

6.10.1    Implementing PIO Transfer Mode

The following code shows the implementation of the PIO transfer 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;


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

6.10.2    Implementing Device DMA Transfer Mode

The following code shows the implementation of the device DMA transfer mode:
    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;


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

6.10.3    Implementing Block DMA Transfer Mode

The following code shows the implementation of the block-mode DMA transfer mode:
    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;


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

6.11    Interrupt Sections

The following code shows the five Interrupt Sections implemented for the /dev/dmaex device driver:
/***************************************************
 *     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);
}