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.
The source code uses the following convention:
#define IE 0001 [1]
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.
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)
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
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 */
};
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.
| Part | Section |
|---|---|
| Providing Declarations Required for Configuration | Section 6.4.1 |
| Implementing the dmaex_configure Interface | Section 6.4.2 |
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}
};
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);
}
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);
}
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);
}
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;
}
}
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;
}
}
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.
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 */
}
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;
}
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);
}
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.
| Part | Section |
|---|---|
| Implementing the dmaex_open Interface | Section 6.6.1 |
| Implementing the dmaex_close Interface | Section 6.6.2 |
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);
}
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);
}
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);
}
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.
| Part | Section |
|---|---|
| Implementing a dmaex_setup_blk_mode Interface | Section 6.8.1 |
| Implementing the dmaex_ioctl Interface | Section 6.8.2 |
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);
}
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);
}
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
}
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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.
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.
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]
}
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]
}
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;
}
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;
}
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;
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;
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;
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);
}