The CMA_Option entry provides information that the
Digital-provided cma_dd subsystem needs. The
cma_dd subsystem allocates physically contiguous memory
on behalf of a device driver that is dynamically configured into the system.
Refer to Writing Device Drivers: Tutorial and
Writing Device Drivers: Reference for information
about CMA_Option and the related kernel interfaces
contig_malloc and contig_free.
The VBA_Option entry describes the device driver and its
VMEbus configuration. See Section 4.3 for a
detailed description of the VBA_Option entry.
B.1 VMEbus /dev/dmaex DM100.list Registration File
The following NAME.list
registration file specifies the location of the
/dev/dmaex driver product files. (Typically
the device driver kit does not include this file.)
# HOSTNAME.list
# Registration file which contains a one line entry for kernel layered products
# The format of each line is as follows. Empty lines are ignored. Any line
# with a '#' as the first character on the line is a comment. Other
# entries have the following fields:
#
# Module Path:Subset ID:Date:Company:Product:Version
#
# This file specifies the pathname to the area where your files are.
# .........................................................................
/usr/examples/devdriver/dm100:
B.2 VMEbus /dev/dmaex files File Fragment
The following files file fragment assumes the
/dev/dmaex third-party device driver kit has been
installed in the /usr/examples/devdriver/dm100 directory:
MODULE/STATIC/dmaex standard Binary
/usr/examples/devdriver/dm100/dmaex.c module dmaex
B.3 VMEbus /dev/dmaex sysconfigtab File Fragment
The following sysconfigtab file fragment includes
the CMA_Option and VBA_Option entries
for the /dev/dmaex driver. These entries must be supplied on
contiguous lines, each on a single line with no newline characters inserted.
(Line breaks have been added to the example listing for clarity.)
dmaex:
Subsystem_Description =DMAEX device driver
Module_Config_Name = dmaex
Device_Char_Major = Any
Device_Char_Minor = 0
Device_Char_Files = dmaex0
Device_Major_Req = Same
CMA_Option = Size - 0x20000, Alignment - 0x10000, Addrlimit - 0,
Type - 0x1D, Flag - 2
VBA_Option = Manufact_Name - 'Digital', Product_Name - 'DMAEX Driver',
Bus_Instance - 0, Driver_Name - dmaex, Driver_Instance - 0,
Csr1 - 0x00800000, Csr2 - 0x0, Vector - 0xF0,
Bus_Priority - 1, Type - C, Adpt_Config - N
B.4 VMEbus /dev/dmaex dmaexreg.h Device Register Header File
The following dmaexreg.h header file example is
also available on the Digital UNIX Version 4.0B operating system CD-ROM:
/*
* @DEC_COPYRIGHT@
*/
/*
* HISTORY
* $Log: dmaexreg.h,v $
* Revision 1.1.2.3 1996/10/03 17:41:49 Julie_McGray
* Added additional definitions for device driver
* ioctls, modes, and contiguous physical memory
* constants.
* [1996/10/01 18:57:35 Julie_McGray]
*
* Revision 1.1.2.2 1996/09/06 16:28:46 Julie_McGray
* .h file for dmaex.c and dmaex_test.c example
* VME device driver.
* [1996/09/03 21:56:20 Julie_McGray]
*
* $EndLog$
*/
/*
* @(#)$RCSfile: dmaexreg.h,v $ $Revision: 1.1.2.3 $ (DEC)
$Date: 1996/10/03 17:41:49 $
*/
#include <sys/ioctl.h>
/***************************************************
* dmaexreg.h Device Register Header File for *
* dmaex.c 09-July-1996 *
* *
* Device register offsets for DMAEX device *
***************************************************/
#define DMAEX_CSR_OFF 0 /* One byte control/status register */
#define DMAEX_COUNT_OFF 1 /* Short byte count */
#define DMAEX_ADDR_OFF 4 /* 32-bit VMEbus transfer address */
/****************************************************
* Convenience defines for calls to sizeof operator *
****************************************************/
#define DMAEX_CSR_SIZE sizeof(char)
#define DMAEX_COUNT_SIZE sizeof(short)
#define DMAEX_ADDR_SIZE sizeof(vme_addr_t)
/***************************************************
* Bits for csr device offset register *
***************************************************/
#define IE 0001 /* Interrupt Enable */
#define DMA_GO 0002 /* Start DMA */
#define RESET 0010 /* Ready for data transfer */
#define ERROR 0020 /* Indicate error */
#define READ 0040 /* Indicate data transfer is read */
/***************************************************
* Define the ioctl macros *
* *
* This instructs the kernel on how much data will *
* be moved between the device driver and the user *
* program as a result of a call to the ioctl *
* interface. Note they are all read/write in *
* this case, meaning that the dmaex_ioctl *
* structure parameter can be written into by the *
* user code and by the driver itself. *
***************************************************/
struct dmaex_ioctl_data {
unsigned long data[6];
};
/*
* PHYS_CONTIG_BUF_SIZE defines the number of bytes
* of physically contiguous memory that will be allocated
* by the the driver. This number MUST match the value
* specified in the CMA_Option of the sysconfigtab file
* fragment.
*/
#define PHYS_CONTIG_BUF_SIZE (128 * 1024)
#define CONTIG_RD_WRT_BUF_SIZE (PHYS_CONTIG_BUF_SIZE / 2)
/*
* The following definitions define the data transfer
* mode to be used by dmaex_read and dmaex_write. These
* interfaces calls the physio interface which then invokes
* dmaex_minphys and dmaex_strategy interfaces. The
* dmaex_strategy interface then performs the data transfer
* using the specified transfer mode.
*/
#define PIO_XFER_MODE 0
#define DEVICE_DMA_MODE 1
#define BLOCK_DMA_MODE 2
/*
* The following definitions define how dmaex_mmap will
* interpert the requested mmap call.
* MMAP_VME_TO_U_MEM - This mode causes the dmaex_mmap interface to
* return a kernel page frame number corresponding
* to an outbound mapped VMEbus address.
* MMAP_K_TO_U_MEM_WRT - This mode causes the dmaex_mmap interface to
* return a kernel page frame number corresponding
* to the physically contiguous write buffer
* allocated by contig_malloc and the cma_dd interface.
* MMAP_K_TO_U_MEM_RD - This mode causes the dmaex_mmap interface to
* return a kernel page frame number corresponding
* to the physically contiguous read buffer
* allocated by contig_malloc and the cma_dd interface.
*/
#define MMAP_VME_TO_U_MEM 0
#define MMAP_K_TO_U_MEM_WRT 1
#define MMAP_K_TO_U_MEM_RD 2
enum dmaex_commands { C1, C2, C3, C4, C5, C6, C7,
C8, C9, C10, C11, C12, C13,
C14, C15, C16, C17, C18, C19,
C20, C21, C22, C23, C24, C25,
C26, C27, C28 };
#define SET_MMAP_MODE _IOWR('t', C1, struct dmaex_ioctl_data)
#define GET_MMAP_MODE _IOWR('t', C2, struct dmaex_ioctl_data)
#define SET_STRATEGY_XFER_MODE _IOWR('t', C3, struct dmaex_ioctl_data)
#define GET_STRATEGY_XFER_MODE _IOWR('t', C4, struct dmaex_ioctl_data)
#define SETUP_VME_FOR_MMAP_PIO _IOWR('t', C5, struct dmaex_ioctl_data)
#define GET_VME_INFO_FOR_MMAP_PIO _IOWR('t', C6, struct dmaex_ioctl_data)
#define UNMAP_VME_FOR_MMAP_PIO _IOWR('t', C7, struct dmaex_ioctl_data)
#define SET_STRATEGY_INFO_BLK_DMA _IOWR('t', C8, struct dmaex_ioctl_data)
#define GET_STRATEGY_INFO_BLK_DMA _IOWR('t', C9, struct dmaex_ioctl_data)
#define CLR_STRATEGY_INFO_BLK_DMA _IOWR('t', C10, struct dmaex_ioctl_data)
#define SETUP_VME_FOR_STRATEGY_PIO _IOWR('t', C11, struct dmaex_ioctl_data)
#define GET_VME_INFO_FOR_STRATEGY_PIO _IOWR('t', C12, struct dmaex_ioctl_data)
#define UNMAP_VME_FOR_STRATEGY_PIO _IOWR('t', C13, struct dmaex_ioctl_data)
#define MAP_SYS_MEM_TO_VME _IOWR('t', C14, struct dmaex_ioctl_data)
#define GET_SYS_MEM_INFO _IOWR('t', C15, struct dmaex_ioctl_data)
#define UNMAP_SYS_MEM_TO_VME _IOWR('t', C16, struct dmaex_ioctl_data)
#define SETUP_DMA_BLK_WRT _IOWR('t', C17, struct dmaex_ioctl_data)
#define GET_DMA_BLK_WRT _IOWR('t', C18, struct dmaex_ioctl_data)
#define DO_DMA_BLK_WRT _IOWR('t', C19, struct dmaex_ioctl_data)
#define CLR_DMA_BLK_WRT _IOWR('t', C20, struct dmaex_ioctl_data)
#define SETUP_DMA_BLK_RD _IOWR('t', C21, struct dmaex_ioctl_data)
#define GET_DMA_BLK_RD _IOWR('t', C22, struct dmaex_ioctl_data)
#define DO_DMA_BLK_RD _IOWR('t', C23, struct dmaex_ioctl_data)
#define CLR_DMA_BLK_RD _IOWR('t', C24, struct dmaex_ioctl_data)
#define SET_INT_HANDLER _IOWR('t', C25, struct dmaex_ioctl_data)
#define CLR_INT_HANDLER _IOWR('t', C26, struct dmaex_ioctl_data)
#define VME_POST_IRQ _IOWR('t', C27, struct dmaex_ioctl_data)
#define VME_CLR_IRQ _IOWR('t', C28, struct dmaex_ioctl_data)
B.5 VMEbus /dev/dmaex dmaex.c Source File
The following dmaex.c source file example is also
available on the Digital UNIX Version 4.0B operating system
CD-ROM, along with a program named dmaex_test.c that
exercises the driver:
/*
* @DEC_COPYRIGHT@
*/
/*
* HISTORY
* $Log: dmaex.c,v $
* Revision 1.1.2.3 1996/10/03 17:41:45 Julie_McGray
* The dmaex_mmap interface caused a system panic on Digital
* Alpha VME 2100 system. The mmap driver interface was
* dropping bits of the page frame number being returned.
*
* Cleaned up documentation and provided support for additional
* VME functionality.
* [1996/10/01 19:19:43 Julie_McGray]
*
* Revision 1.1.2.2 1996/09/06 16:28:42 Julie_McGray
* Changed dmaex_mmap to return int instead of caddr_t.
*
* Added more documentation comments.
* [1996/09/04 17:42:58 Julie_McGray]
*
* Example VME device driver.
* [1996/09/03 22:07:25 Julie_McGray]
*
* $EndLog$
*/
#pragma ident "@(#)$RCSfile: dmaex.c,v $ $Revision: 1.1.2.3 $ (DEC)
$Date: 1996/10/03 17:41:45 $"
/****************************************************
* dmaex.c Driver for dmaex device *
* *
* The /dev/dmaex device driver is an example *
* driver that supports a fictitious VME `dmaex' *
* device. *
* *
* The /dev/dmaex driver shows you how to write a *
* a single binary model device driver that can be *
* statically or dynamically configured into the *
* kernel. The dev/dmaex driver is implemented to *
* operate on the VMEbus. *
* *
* The /dev/dmaex device driver assumes that a *
* device is present at the VMEbus address *
* specified in the VBA_Option's Csr1 entry of the *
* sysconfigtab file fragment. The Csr1 VMEbus *
* address and addr1_size and addr1_atype in the *
* driver structure provides information to the *
* auto configuration software. This information *
* is used by the auto configuration software to *
* map system I/O space to VMEbus address space. *
* The result of the mapping is an io_handle_t *
* that is saved in the controller structure's addr *
* element and passed to the /dev/dmaex device *
* driver's probe interface. *
* *
********************** NOTE ************************
* *
* In the absence of 'dmaex' hardware and to allow *
* the driver to be statically or dynamically *
* loaded for testing between two single board *
* computers, a Dmaex_No_Dev attribute has been *
* initialized to indicate that no 'dmaex' device *
* hardware is present. The dmaex_no_dev flag will *
* be used to skip over BADADDR, read_io_port and *
* write_io_port accesses to the fictitious 'dmaex' *
* device hardware. The flag will be checked in *
* dmaex_probe, dmaex_close, dmaex_strategy and *
* dmaex_intr1 driver interfaces. *
* *
****************************************************/
/****************************************************
* The following list highlights some features of *
* the /dev/dmaex driver: *
* *
********************** NOTE ************************
* *
* Refer to Digital UNIX V4.0 Writing Device *
* Driver:Tutorial for discussions on writing *
* device drivers and the single binary module *
* technology. *
* *
* Refer to Writing VMEbus device drivers for *
* VME specific information. *
* *
****************************************************
* *
* o Single Binary module *
* *
* The /dev/dmaex is written to produce a single *
* driver image. This single driver image has a *
* a file extension of .mod. You compile a *
* device driver to produce a single binary *
* module that can be statically or dynamically *
* configured into a Digital UNIX kernel at any *
* point in time. *
* *
* o Static Configuration *
* *
* The /dev/dmaex driver's single binary module *
* (the dmaex.mod) can be: *
* *
* - Statically configured into Digital UNIX *
* kernel (/vmunix). *
* *
* - Statically configured into a sysconfigtab *
* database image when a user boots a *
* Digital UNIX kernel (/vmunix) or when the *
* OSFboot links the sysconfigtab image. *
* *
* o Dynamic configuration *
* *
* The /dev/dmaex driver's single binary module *
* (the dmaex.mod file) can be dynamically *
* configured into a Digital UNIX kernel (/vmunix)*
* when the user makes a request (by using the *
* sysconfig utility) at single user or multiuser *
* time. *
* *
****************************************************/
/****************************************************
* Interfaces the /dev/dmaex implement *
* *
* o dmaex_configure *
* o register_configuration *
* o register_major_number *
* o callback_register_configuration *
* o callback_register_major_number *
* o dmaex_ctlr_unattach *
* *
* o dmaex_probe *
* o dmaex_cattach *
* o dmaex_open *
* o dmaex_close *
* o dmaex_mmap *
* o dmaex_ioctl *
* o dmaex_read *
* o dmaex_write *
* o dmaex_minphys *
* o dmaex_strategy *
* o dmaex_intr1 *
* o dmaex_intr2 *
* o dmaex_iack_isr *
* o dmaex_post_signal *
* o dmaex_rcv_int_srv *
****************************************************/
/****************************************************
* Include Files Section *
****************************************************/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/map.h>
#include <sys/buf.h>
#include <sys/vm.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/devio.h>
#include <hal/cpuconf.h>
#include <sys/exec.h>
#include <sys/malloc.h>
#include <kern/event.h>
#include <io/common/devdriver.h>
#include <io/common/handler.h>
#include <sys/sysconfig.h>
/****************************************************
* If you are writing a device driver to operate on *
* the VMEbus, you must include the following *
* bus-specific header file. *
* *
* #include <io/dec/vme/vbareg.h> *
* *
* Because the fictitious DMAEX device controller *
* can be connected to a TURBOchannel or PCI bus, *
* the tc.h and pci.h files are also included. *
* Typically, you will not need these files for *
* VMEbus device driver development. *
* *
* #include <io/dec/tc/tc.h> *
* #include <io/dec/pci/pci.h> *
****************************************************/
#include <io/dec/tc/tc.h>
#include <io/dec/pci/pci.h>
#include <io/dec/vme/vbareg.h> /* VMEbus definitions */
#include <machine/cpu.h>
#include "dmaexreg.h" /* Device register header file */
/****************************************************
* The /dev/dmaex driver's register_major_number *
* interface uses the following #defines when *
* obtaining a major number *
****************************************************/
#define NO_DEV -1
#define MAJOR_INSTANCE 1
/****************************************************
* Data structure sizing approach *
****************************************************
* *
* The following #define will be used to allocate *
* data structures needed to the /dev/dmaex driver. *
* There can be at most 8 instances of the DMAEX *
* device controller on the system. This means that *
* MAX_NDMAEX is the maximum number of controllers *
* that the /dev/dmaex can support. This is a small *
* number of instances of the driver and the data *
* structures themselves are not large, so it is *
* acceptable to allocate for the maximum con- *
* figuration. *
* *
* Note that MAX_NDMAEX is used in the *
* dmaex_attribute table, specifically as the *
* maximum value for the numunit attribute field. *
* The NDMAEX variable is the data value address *
* for the numunit attribute field. It is the *
* number of controllers to create for this driver. *
* *
****************************************************/
#define MAX_NDMAEX 8
/****************************************************
* Declarations Section *
****************************************************/
/****************************************************
* Defines for softc structure members *
****************************************************/
#define DMAEXOPEN 1
#define DMAEXCLOSE 0
/****************************************************
* Forward declarations of driver interfaces *
****************************************************/
static int register_configuration();
static int register_major_number();
static void callback_register_configuration();
static void callback_register_major_number();
int dmaex_configure(), dmaex_ctlr_unattach(),
dmaex_probe(), dmaex_cattach(),
dmaex_open(), dmaex_close(), dmaex_mmap(),
dmaex_read(), dmaex_write(), dmaex_ioctl(),
dmaex_minphys(), dmaex_strategy(),
dmaex_intr1(), dmaex_intr2(),
dmaex_iack_isr(), dmaex_post_signal(), dmaex_rcv_int_srv();
/****************************************************
* *
* Declare an array of controller structure pointers*
* MAX_NDMAEX is used as the maximum number of *
* controllers that this driver can support. *
****************************************************/
struct controller *dmaex_ctlr[MAX_NDMAEX];
/****************************************************
* Initialize the driver structure. If the driver *
* writer requires the VME auto configuration *
* software to map VMEbus address space into kernel *
* space for the purpose of device register access, *
* the driver writer must provide a VMEbus address *
* in Csr1 of the VBA_Option sysconfigtab file *
* fragment and specify a size and VME address *
* modifiers in the addr1_size and addr1_atype *
* fields of the driver structure. The VME auto *
* configuration software will use these combined *
* entries to map VMEbus adddress space to kernel *
* address space. The result of this mapping will *
* be an io_handle_t that the driver will use with *
* read_io_port and write_io_port kernel interfaces *
* to access the device. The io_handle_t for Csr1 *
* will be passed to the driver's probe interface *
* and also saved in the controller structure's *
* addr element (ctlr->addr). *
* *
* The driver writer can also request the auto *
* configuration software to map a second VMEbus *
* address space, by specifying a value in Csr2 of *
* the VBA_Option sysconfigtab entry and values in *
* addr2_size and addr2_atype. The VME auto *
* configuration software will perform the *
* appropriate mapping and store the io_handle_t in *
* the controller structure's addr2 element. *
****************************************************/
struct driver dmaexdriver = {
dmaex_probe, /* probe */
0, /* slave */
dmaex_cattach, /* cattach */
0, /* dattach */
0, /* go */
0, /* addr_list */
0, /* dev_name */
0, /* dev_list */
"dmaex", /* ctlr_name */
dmaex_ctlr, /* ctlr_list */
0, /* xclu */
0x8, /* addr1_size */
VME_A24_UDATA_D32, /* addr1_atype */
0, /* addr2_size */
0, /* addr2_atype */
dmaex_ctlr_unattach, /* ctlr_unattach */
0 /* dev_unattach */
};
/****************************************************
* Declare softc structure to allow the /dec/dmaex *
* device driver's interface to share data. *
****************************************************/
struct dmaex_softc {
int sc_open; /* DMAEXOPEN, DMAEXCLOSE */
io_handle_t csr_handle; /* Device CSR I/O handle */
event_t isi_event; /* Used by device ISI interrupts */
event_t post_iack_event[8]; /* Used to acknowledge posted IRQs*/
dma_handle_t dma_handle; /* handle used by dmaex_strategy */
/* Entries for mmap support */
io_handle_t pio_handle; /* I/O handle for VMEbus mapping */
vme_addr_t pio_vme_addr; /* VMEbus address */
vme_atype_t pio_vme_am; /* VMEbus address modifiers */
unsigned int pio_vme_size; /* VMEbus byte count */
unsigned int pio_access_type; /* Access type */
/* Strategy block mode DMA entries*/
int dma_is_set; /* Flag for DMA transfers */
vme_addr_t dma_vme_addr; /* DMA VMEbus address */
vme_atype_t dma_vme_am; /* DMA VMEbus address modifiers */
unsigned int dma_vme_size; /* DMA VMEbus byte count */
vme_addr_t dma_wrk_vme_addr; /* Updated DMA bus address */
unsigned int dma_wrk_vme_size; /* Updated transfer byte count */
int strategy_xfer_mode; /* Strategy transfer mode */
int mmap_mode; /* mmap mapping mode */
/* Strategy PIO entries */
io_handle_t vme_handle; /* I/O handle for VMEbus mapping */
vme_addr_t vme_addr; /* VMEbus address */
vme_atype_t vme_am; /* VMEbus address modifiers */
unsigned int vme_size; /* VMEbus byte count */
io_handle_t vme_wrk_handle; /* Updated VME io_handle_t */
unsigned int vme_wrk_size; /* Updated transfer byte count */
dma_handle_t sys_mem_dma_handle; /* Mapped system memory DMA handle*/
vme_addr_t sys_mem_vme_addr; /* VMEbus address for sys memory */
vme_atype_t sys_mem_vme_am; /* VMEbus address modifiers */
unsigned int sys_mem_vme_size; /* VMEbus/system memory byte count*/
caddr_t sys_mem_vir_addr; /* System memory virtual address */
vme_addr_t blk_wrt_vme_addr; /* MBLT write VMEbus address */
vme_atype_t blk_wrt_vme_am; /* MBLT write VMEbus addr mod */
unsigned int blk_wrt_vme_size; /* MBLT write VMEbus byte count */
dma_handle_t blk_wrt_dma_handle; /* MBLT write DMA handle */
caddr_t blk_wrt_buf_ptr; /* MBLT write memory buffer */
struct proc *blk_wrt_proc_p; /* process pointer for write */
vme_addr_t blk_rd_vme_addr; /* MBLT read VMEbus address */
vme_atype_t blk_rd_vme_am; /* MBLT read VMEbus addr mod */
unsigned int blk_rd_vme_size; /* MBLT read VMEbus byte count */
dma_handle_t blk_rd_dma_handle; /* MBLT read DMA handle */
caddr_t blk_rd_buf_ptr; /* MBLT read memory buffer */
struct proc *blk_rd_proc_p; /* process pointer for read */
ihandler_id_t *rcvisr_id_t; /* handler ID for VME receive ISI */
struct proc *rcvisr_proc_p; /* isr user task process pointer */
int rcvisr_irq; /* VMEbus irq for recive ISI */
struct buf io_buf; /* read/write i/o request buffer */
} dmaex_softc[MAX_NDMAEX];
int dmaex_config = FALSE; /* state flag indicating driver configured */
int dmaex_devno = NO_DEV; /* No major number assigned yet */
/****************************************************
* The handler_add interface is used to register *
* the interrupt handler for the dev/dmaex driver. *
* the dmaex_id_t array contains IDs that are used *
* to enable, disable and deregister the interrupt *
* handlers. *
* *
* This fictitious device driver needs to install *
* two interrupts per controller. The device can *
* present two unique interrupts to the system. *
****************************************************/
#define NINTS_PER_DMAEX 2
ihandler_id_t *dmaex_id_t[MAX_NDMAEX][NINTS_PER_DMAEX]; /* handler ID's */
/****************************************************
* The following list describes the use of the *
* dmaex_is_dynamic, dmaexcallback_return_status, *
* and num_dmaex variables. *
* *
* o dmaex_is_dynamic *
* *
* The dmaex_is_dynamic variable will be used to *
* control any differences in tasks performed by *
* statically or dynamically configured *
* /dev/dmaex driver. Implementing a device *
* driver to handle a static or dynamic *
* configuration request gives customers maximum *
* flexibility in how they configure the driver *
* into the kernel. *
* *
* o dmaexcallback_return_status *
* *
* The callback return status variable is used by *
* the callback_register_major_number interface *
* to determine if a previous failure has occurred*
* in statically configuring the /dev/dmaex *
* driver. *
* *
* o num_dmaex *
* *
* The num_dmaex variable is used to count the *
* number of controllers probed. The dmaex_probe *
* interface increments this variable each time *
* it probes a DMAEX controller. *
****************************************************/
int num_dmaex = 0;
int dmaex_is_dynamic = 0;
int dmaexcallback_return_status = ESUCCESS;
/****************************************************
* External function references. These are needed *
* for the devsw declaration. *
****************************************************/
extern int nodev();
/****************************************************
* External reference needed for event_wait timeout *
****************************************************/
extern int hz; /* The value for hz is defined in param.c */
/****************************************************
* Typically, third-party device drivers specify *
* the bus-specific option data in a sysconfigtab *
* file fragment. See Writing Device Drivers: *
* Tutorial and the bus specific books for informa- *
* tion on the syntax associated with VMEbus option *
* data attribute fields (VBA_Option). *
****************************************************/
/****************************************************
* The following code sets the members of the *
* dsent structure to appropriate values for the *
* dev/dmaex driver. The address of this structure *
* is passed to the devsw_add interface, which *
* registers the I/O services interfaces in the *
* dsent table and reserves a major number for the *
* /dev/dmaex driver.
****************************************************/
struct dsent dmaex_devsw_entry = {
dmaex_open, /* d_open */
dmaex_close, /* d_close */
dmaex_strategy, /* d_strategy */
dmaex_read, /* d_read */
dmaex_write, /* d_write */
dmaex_ioctl, /* d_ioctl */
nodev, /* d_dump */
nodev, /* d_psize */
nodev, /* d_stop */
nodev, /* d_reset */
nodev, /* d_select */
dmaex_mmap, /* d_mmap */
0, /* d_segmap */
NULL, /* d_ttys */
DEV_FUNNEL_NULL, /* d_funnel */
0, /* d_bflags */
0, /* d_cflags */
};
/****************************************************
* NOTE *
*--------------------------------------------------*
* *
* All VMEbus device drivers MUST specify "vba" as *
* the bus name. This constant is passed to the *
* configure driver interface, which the /dev/dmaex *
* driver's dmaex_configure interface calls. *
* *
****************************************************/
#define DMAEX_BUSNAME1 "vba"
/****************************************************
* The following code declares variables that are *
* required by the cfgmgr framework's driver *
* method. You use these variables as fields in *
* the driver's attribute table. The attribute *
* table for the /dev/dmaex driver is called *
* dmaex_attributes. *
****************************************************/
static int NDMAEX = 1; /* Number of controllers to create for driver */
static int majnum = NO_DEV;
static int begunit = 0;
static int dmaex_version = 1;
static int dmaex_no_dev = 1;
static int dmaex_int_wait = 1;
static u_int dmaex_max_dma = (1024 * 1024);
static int dmaex_developer_debug = 0;
static char *dmaex_contig_buf = NULL;
static char *dmaex_contig_rd_buf = NULL;
static char *dmaex_contig_wrt_buf = NULL;
static unsigned char subsysname[CFG_ATTR_NAME_SZ] = "";
static unsigned char mcfgname[CFG_ATTR_NAME_SZ] = "";
static unsigned char devmajor[CFG_ATTR_NAME_SZ] = "";
static unsigned char unused[300] = "";
static unsigned char cma_dd[120] = "";
static unsigned char vba_option[300] = "";
/****************************************************
* Setup dmaex device driver debugging macros. *
* *
* dmaex_developer_debug = 0 - no debug messages *
* dmaex_developer_debug = 1 - level 1 messages *
* dmaex_developer_debug = 2 - level 2 messages *
* dmaex_developer_debug = 3 - level 1 & 2 messages *
****************************************************/
#define DMAEX_DEBUG
#ifdef DMAEX_DEBUG
#define DMAEX_DBG1 if (dmaex_developer_debug & 0x01) printf
#define DMAEX_DBG2 if (dmaex_developer_debug & 0x02) printf
#else
#define DMAEX_DBG1 ;
#define DMAEX_DBG2 ;
#endif
/*****************************************************
* The cfgmgr framework uses the driver's attribute *
* table (a structure of type cfg_subsys_attr_t) *
* during static and dynamic configuration of a *
* device driver into the kernel. The cfgmgr *
* framework initializes the attributes table early *
* in the kernel boot process and whenever it *
* receives a sysconfig command request from *
* either the kernel or the user command line. *
* *
* NOTE *
* *
* There are a number of fields (attributes) that *
* appear in the sysconfigtab file fragment that *
* that do not appear in the device driver's *
* attribute table. The cfgmgr framework's device *
* driver method consumes these fields (attributes) *
* to perform kernel configuration work and to make *
* device special files. To avoid warning messages *
* from the cfgmgr framework, you should add these *
* attributes to the driver's attribute table *
*****************************************************/
/*****************************************************
* Declares an array of cfg_subsys_attr_t structures *
* and calls it dmaex_attributes. The device driver *
* method of cfgmgr fills in the elements in *
* dmaex_attributes because the operation types *
* designated by these are CFG_OP_CONFIGURE. The *
* device driver method of cfgmgr reads the entries *
* for the /dev/dmaex driver from the sysconfigtab *
* database. (A sysconfigtab file fragment was *
* created for the /dev/dmaex driver. *
* The sysconfigdb utility appends this sysconfigtab *
* file fragment to the /etc/sysconfigtab database.) *
* *
* To determine if any of these element reads *
* failed, the /dev/dmaex driver verifies each from *
* the cfg_attr_t structure passed into the *
* dmaex_configure interface. *
* *
* Several of the following fields are used in the *
* /dev/dmaex device driver while other fields exist *
* to illustrate what can be loaded by cfgmgr into *
* the driver attributes table. *
* *
* These fields can represent tunable parameters to *
* indicate whether the driver is statically or *
* dynamically configured into the kernel. *
*****************************************************/
cfg_subsys_attr_t dmaex_attributes[] = {
/* Fields used in this driver */
{"Subsystem_Description",CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)subsysname,0,CFG_ATTR_NAME_SZ,0},
{"Module_Config_Name", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)mcfgname,2,CFG_ATTR_NAME_SZ,0},
{"Device_Char_Major", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)devmajor,0,CFG_ATTR_NAME_SZ,0},
{"Device_Char_Minor", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)unused,0,300,0},
{"Device_Char_Files", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)unused,0,300,0},
{"Device_Major_Req", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)unused,0,300,0},
{"Device_User", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)unused,0,300,0},
{"Device_Group", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)unused,0,300,0},
{"Device_Mode", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)unused,0,300,0},
{"CMA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)cma_dd,0,120,0},
{"numunit", CFG_ATTR_INTTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)&NDMAEX,0,MAX_NDMAEX,0},
{"VBA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
(caddr_t)vba_option,0,300,0},
/*****************************************************
* The /dev/dmaex device driver modifies the *
* following attributes during a configure or *
* unconfigure operation. The cfgmgr framework's *
* device driver method uses these attributes to *
* provide the device special files that device *
* drivers need. *
*****************************************************/
{"majnum", CFG_ATTR_INTTYPE, CFG_OP_QUERY,
(caddr_t)&majnum,0,512,0},
{"begunit", CFG_ATTR_INTTYPE, CFG_OP_QUERY,
(caddr_t)&begunit,0,8,0},
{"Dmaex_version", CFG_ATTR_INTTYPE, CFG_OP_QUERY,
(caddr_t)&dmaex_version,0,9999999,0},
{"Dmaex_No_Dev", CFG_ATTR_INTTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)&dmaex_no_dev,0,1,0},
{"Dmaex_Int_Wait", CFG_ATTR_INTTYPE, CFG_OP_QUERY |
CFG_OP_RECONFIGURE | CFG_OP_CONFIGURE,
(caddr_t)&dmaex_int_wait,1,9999999,0},
{"Dmaex_Max_DMA", CFG_ATTR_UINTTYPE, CFG_OP_QUERY |
CFG_OP_RECONFIGURE | CFG_OP_CONFIGURE,
(caddr_t)&dmaex_max_dma,8,0xFFFFFFFF,0},
{"Dmaex_Contig_Mem", CFG_ATTR_ULONGTYPE,CFG_OP_CONFIGURE | CFG_OP_QUERY,
(caddr_t)&dmaex_contig_buf,0,0xFFFFFFFFFFFFFFFF,0},
{"Dmaex_Developer_Debug", CFG_ATTR_INTTYPE, CFG_OP_QUERY |
CFG_OP_RECONFIGURE | CFG_OP_CONFIGURE,
(caddr_t)&dmaex_developer_debug,0,7,0},
{"",0,0,0,0,0,0}
};
/*****************************************************
* dmaex_configure() *
*****************************************************
* Name: dmaex_configure *
* *
* Arguments: *
* *
* op Configure operation *
* indata Input data structure, cfg_attr_t *
* indatalen Size of input data structure *
* outdata Formal parameter not used *
* outdatalen Formal parameter not used *
* *
* Called by: *
* *
* o cfgmgr framework *
* *
* Return codes: *
* *
* Success ESUCCESS *
* *
* Failure EBUSY *
* EINVAL *
* ESRCH *
* ENOTSUP *
* Description: *
* *
* The dmaex_configure interface is called *
* indirectly by the cfgmgr framework. The cfgmgr *
* framework is responsible for calling all single *
* binary modules for registration and integration *
* into the kernel. The cfgmgr framework requires *
* that a single binary module has both an *
* attributes table and a configure interface before *
* it (the single binary module) can be registered *
* as part of the cfgmgr framework and the Digital *
* UNIX kernel. *
* *
* The dmaex_configure cooperates with the cfgmgr *
* framework to complete configure, unconfigure, *
* query, and other requests. These request are *
* differentiated by the "op" arguments. *
* *
* Specifically, the dmaex_configure interface shows *
* the code the device driver must supply to produce *
* a single binary module. This code allows the *
* /dev/dmaex driver to be statically configured *
* into the kernel. *
*****************************************************/
int
dmaex_configure(cfg_op_t op,
cfg_attr_t *indata,
size_t indatalen,
cfg_attr_t *outdata,
size_t outdatalen)
{
/*****************************************************
* The dmaex_configure interface declares the *
* following variables: *
* *
* i Used for loop control *
* driver_cfg_state Stores the configuration state *
* (either static or dynamic) *
*****************************************************/
int retval, i;
int driver_cfg_state;
/***************************************************
* MAX_DEVICE_CFG_ENTRIES represents maximum *
* number of attributes that the cfgmgr frameworks *
* configures on behalf of the devie driver. *
***************************************************/
#define MAX_DEVICE_CFG_ENTRIES 19
/***************************************************
* The cfg_attr_t list passed into the *
* dmaex_configure interface's indata argument *
* contains the strings that are stored in the *
* sysconfigtab database for this subsystem (the *
* /dev/dmaex driver). *
***************************************************/
cfg_attr_t cfg_buf[MAX_DEVICE_CFG_ENTRIES];
switch (op) {
/***************************************************
* Configure (load) the driver. *
***************************************************/
case CFG_OP_CONFIGURE:
DMAEX_DBG1("dmaex_configure: CFG_OP_CONFIGURE.\n");
/***************************************************
* Pass Attributes Verification *
* *
* Attributes passed through the cfg_attr_t *
* structure are not known to be valid until the *
* /dev/dmaex driver can check their status in *
* the indata argument. A status other than *
* CFG_FRAME_SUCCESS is an error condition. You *
* can use this code to: *
* *
* o Check cfgmgr loading problems with the *
* dmaex_attributes (struct type *
* cfg_subsys_attr_t). *
* *
* o Display the contents and status of the *
* attributes in the sysconfigtab database for *
* the /dev/dmaex driver *
* *
* o Report cfgmgr's status that indicates a *
* failure to load any of the attribute fields *
* into the dmaex_attributes structure. *
***************************************************/
bcopy(indata, cfg_buf[0].name,
indatalen * (sizeof(cfg_attr_t)));
for( i = 0; i < indatalen; i++)
switch(cfg_buf[i].type) {
case CFG_ATTR_STRTYPE:
break;
default:
switch(cfg_buf[i].status) {
case CFG_FRAME_SUCCESS:
break;
default:
printf("%s:",cfg_buf[i].name);
switch(cfg_buf[i].status) {
case CFG_ATTR_EEXISTS:
printf("**Attribute does not exist\n");
break;
case CFG_ATTR_EOP:
printf("**Attribute does not support operation\n");
break;
case CFG_ATTR_ESUBSYS:
printf("**Subsystem Failure\n");
break;
case CFG_ATTR_ESMALL:
printf("**Attribute size/value too small\n");
break;
case CFG_ATTR_ELARGE:
printf("**Attribute size/value too large\n");
break;
case CFG_ATTR_ETYPE:
printf("**Attribute invalid type\n");
break;
case CFG_ATTR_EINDEX:
printf("**Attribute invalid index\n");
break;
case CFG_ATTR_EMEM:
printf("**Attribute memory allocation error\n");
break;
default:
printf("**Unknown attribute: ");
printf("%x\n", cfg_buf[i].status);
}
/*
* An error status has been detected
* return an error of invalid
*/
return(EINVAL);
}
break;
}
/*****************************************************
* If this device driver has already been configured *
* either statically or dynamically then return this *
* dmaex_configure call indicating an error when the *
* cfgmgr framework calls the CFG_OP_CONFIGURE entry *
* point of the driver's configure interface. *
*****************************************************/
if(dmaex_config == TRUE) {
printf("dmaex_configure: already configured\n");
return(EINVAL);
}
/***************************************************
* The following code performs a check on the *
* device driver name that the cfgmgr framework *
* uses to statically or dynamically configure the *
* driver into the kernel. If the device driver *
* name is NULL, subsequent code uses the *
* controller name stored in driver structure's *
* ctlr_name member. The driver structure for the *
* /dev/dmaex driver is called dmaexdriver. This *
* structure was declared and initialized to *
* appropriate values in the Declarations Section *
* of the /dev/dmaex driver. *
* *
* The name specified in the ctlr_name member will *
* be replaced if the mcfgname variable is not *
* NULL. The value stored in mcfgname supersedes *
* the controller name stored in ctlr_name during *
* configuration of the driver. *
***************************************************/
if ( strcmp(mcfgname,"") == 0 ) {
/*
* If the operator is interested in knowing
* the configuration name of this driver,
* you can set this attribute to CFG_OP_QUERY in
* the driver's cfg_subsys_attr_t structure.
*
* The mcfgname variable is important because
* this name is later matched to the driver
* name in the Driver_Name field of the option
* data attribute of a specific bus. This
* mechanism is how a hardware device is matched
* to its associated driver.
*/
strcpy(mcfgname,"dmaex");
} else {
/*
* mcfgname from sysconfigtab is used to configure
* the device driver in the following calls to the
* configure_driver interface.
*/
}
if (cfgmgr_get_state(mcfgname, &driver_cfg_state) != ESUCCESS) {
/*
* CFGMGR fatal error determining the state of the "dmaex" subsystem
*/
printf("dmaex_configure: cfgmgr_get_state failed\n");
return(EINVAL);
}
if (driver_cfg_state == SUBSYSTEM_STATICALLY_CONFIGURED) {
/*
* During Static cfgmgr configuration subsystem callbacks
* scheduled to run in the boot path have no way to determine
* if the previous callbacks worked. This global flag is looked
* at in each of the callback jackets to determine if this
* subsystem should still be configured in the boot path.
*/
dmaexcallback_return_status = ESUCCESS;
/*
* During Static configuration the cfgmgr schedules work
* to be done at pre and post autoconfiguration but it
* does does not actualy know at this time whether a subsystem
* will successfull configure into a kernel. The callbacks
* are responsible for determining the status of a subsystems
* configuration in the boot path and reporting failure to
* framework via the cfgmgr_set_state() call.
*/
dmaex_is_dynamic = SUBSYSTEM_STATICALLY_CONFIGURED;
register_callback( callback_register_configuration,
CFG_PT_PRECONFIG, CFG_ORD_NOMINAL, (long) 0 );
register_callback( callback_register_major_number,
CFG_PT_POSTCONFIG, CFG_ORD_NOMINAL, (long) 0 );
} else {
/*
* Dynamic Loadable configuration
*/
DMAEX_DBG1("dmaex_configure: CFG_OP_CONFIGURE - Dynamic loadable\n");
dmaex_is_dynamic = SUBSYSTEM_DYNAMICALLY_CONFIGURED;
retval = register_configuration();
if (retval != ESUCCESS) {
printf("dmaex_configure: register_configuration error status = 0x%x\n",
retval);
return(retval);
}
/*
* Configure this driver into the system
*/
retval = configure_driver(mcfgname, DRIVER_WILDNUM, DMAEX_BUSNAME1,
&dmaexdriver);
if (retval != ESUCCESS) {
printf("dmaex_configure: configure_driver error status = 0x%x\n",
retval);
return(retval);
}
/*
* Register the major number with the system
*/
retval = register_major_number();
if (retval != ESUCCESS) {
printf("dmaex_configure: register_major_number error status = 0x%x\n",
retval);
return(retval);
}
}
break;
/***************************************************
* Unconfigure (unload) the driver. *
***************************************************/
case CFG_OP_UNCONFIGURE:
DMAEX_DBG1("dmaex_configure: CFG_OP_UNCONFIGURE.\n");
/***************************************************
* Return ENOTSUP if the driver is not currently *
* dynamically configured. A statically configured *
* driver CANNOT be unconfigured/unloaded. *
* *
* Static drivers will all physically remain in *
* kernel whether or not they are configured. *
* Dynamic drivers when they are unconfigured are *
* also unloaded from the kernel. *
***************************************************/
if(dmaex_is_dynamic == SUBSYSTEM_STATICALLY_CONFIGURED) {
return(ENOTSUP);
}
/***************************************************
* Do not allow the driver to be unloaded *
* if it is currently active. To see if *
* the driver is active look to see if *
* any users have the device open. *
***************************************************/
for (i = 0; i < num_dmaex; i++) {
if (dmaex_softc[i].sc_open != 0) {
return(EBUSY);
}
}
/***************************************************
* Call devsw_del to remove the driver *
* entry points from the in-memory resident *
* cdevsw table. This is done prior to *
* deleting the loadable configuration *
* and handlers to prevent users from *
* accessing the device in the middle of *
* deconfigure operation. *
***************************************************/
retval = devsw_del(mcfgname, MAJOR_INSTANCE);
if (retval == NO_DEV) {
printf("dmaex_configure: devsw_del failed - error status 0x%x\n",
retval);
return(ESRCH);
}
DMAEX_DBG1("dmaex_configure: CFG_OP_UNCONFIGURE: devsw_del = %d\n",retval);
/***************************************************
* Deregister the driver's configuration *
* data structures from the hardware *
* topology and cause the interrupt handlers *
* to be deleted. *
***************************************************/
/***************************************************
* The bus number is wildcarded to *
* deregister on all instances of the vba *
* bus. The controller name and number are *
* wildcarded. This causes all instances *
* that match the specified driver structure *
* to be deregistered. Through the *
* bus-specific code, this interface results *
* in a call to the dmaex_ctlr_unattach *
* interface for each instance of the *
* controller. *
***************************************************/
retval = unconfigure_driver(DMAEX_BUSNAME1,DRIVER_WILDNUM,&dmaexdriver,
mcfgname,DRIVER_WILDNUM);
if (retval != 0) {
printf("dmaex_configure: unconfigure driver failed - status 0x%x\n",
retval);
return(ESRCH);
}
num_dmaex = 0;
dmaex_is_dynamic = 0;
dmaex_config = FALSE;
break;
/**************************************************
* Requests to query a loadable subsystem will *
* only succeed if the CFG_OP_QUERY: entry *
* point returns success. *
**************************************************/
case CFG_OP_QUERY:
DMAEX_DBG1("dmaex_configure: CFG_OP_QUERY.\n");
break;
/**************************************************
* Code to perform the tasks associated with a *
* system manager request to reconfigure the *
* currently loaded device driver. *
**************************************************/
case CFG_OP_RECONFIGURE:
DMAEX_DBG1("dmaex_configure: CFG_OP_RECONFIGURE.\n");
break;
default:
printf("**Unknown operation type\n");
return(ENOTSUP);
}
/***************************************************
* The driver's configure interface has *
* completed successfully. Return a success *
* status. *
***************************************************/
return(ESUCCESS);
}
/***************************************************
* register_configuration() *
***************************************************
* Name: register_configuration *
* *
* Arguments: None *
* *
* Kernel support interface calls: *
* *
* o create_controller_struct *
* o create_device_struct *
* *
* Called by: *
* *
* o During dynamic configuration *
* o dmaex_configure *
* *
* o During static configuration *
* o cfgmgr framework callback *
* This interface registered with the *
* cfgmgr to be called at a specific *
* point of the configuration process. *
* *
* Return Codes: *
* *
* Failure: ENOMEM *
* *
* Description: *
* *
* The register_configuration interface is *
* responsible for registering the controller and *
* and device information for the /dev/dmaex *
* driver. The dynamically configured /dev/dmaex *
* driver directly calls the register_configuration*
* interface. The statically configured /dev/dmaex*
* driver calls register_configuration through a *
* register callback called callback_register_ *
* configuration. (The dmaex_configure interface *
* calls the register_callback interface to *
* register the register_configuration interface *
* so that it can be called at some later time. *
* *
* Writing an interface similar to *
* register_configuration is the method for *
* specifying the controller and device information*
* associated with a device driver. *
* *
* The register_configuration interface causes *
* the appropriate controller and device structures*
* to be created and integrated into the system *
* (hardware) configuration tree for statically and*
* dynamically configured drivers. *
* *
* This method requires device driver writers to *
* determine how much operator control should be *
* allowed over the system's topology creation *
* support code. If appropriate, driver writers *
* can define sysconfigtab database fields that *
* will allow the operator to control what *
* hardware-related information that this *
* interface passes to the appropriate structures. *
***************************************************/
static int
register_configuration()
{
/***************************************************
* Declare controller_config and device_config *
* structures and structure pointers. These *
* structures are the storage mechanism for *
* populating the controller and device structures *
* associated with a specific device driver. *
***************************************************/
struct controller_config ctlr_register;
struct controller_config *ctlr_register_ptr = &ctlr_register;
struct device_config device_register;
struct device_config *device_register_ptr = &device_register;
int i,status;
DMAEX_DBG1("dmaex: register_configuration:\n");
/***************************************************
* Register a controller structure for the *
* /dev/dmaex driver. The system manager can *
* specify attribute fields for the dmaex: entry *
* in /etc/sysconfigtab database that determine *
* the number of controller structures to be *
* created. It is here that the user-configurable *
* attribute NDMAEX would be used to regulate the *
* number of controller structures to be created *
* up to MAX_NDMAEX value defined by the driver. *
***************************************************/
for (i = 0; i < NDMAEX; i++) {
ctlr_register_ptr->revision = CTLR_CONFIG_REVISION;
strcpy(ctlr_register_ptr->subsystem_name, mcfgname);
strcpy(ctlr_register_ptr->bus_name,DMAEX_BUSNAME1);
ctlr_register_ptr->devdriver = &dmaexdriver;
/***************************************************
* Call the create_controller_struct to create the *
* controller structure associated with the *
* /dev/dmaex driver. *
***************************************************/
status = create_controller_struct(ctlr_register_ptr);
if(status != ESUCCESS) {
printf("register_configuration: create_controller_struct failed\n");
return (status);
}
/***************************************************
* Register a device structure for the /dev/dmaex *
* driver. This driver does not require a device *
* structure but the code is included here for *
* reference and is commented out *
***************************************************/
/*
device_register_ptr->revision = DEVICE_CONFIG_REVISION;
strcpy(device_register_ptr->device_type,"disk");
strcpy(device_register_ptr->device_name,"dmaexdev");
strcpy(device_register_ptr->controller_name,mcfgname);
device_register_ptr->phys_unit_num = 0;
device_register_ptr->logical_unit_number = 0;
device_register_ptr->controller_num = i;
status = create_device_struct(device_register_ptr);
if (status != ESUCCESS) {
printf("register_configuration: create_device_struct failed - 0x%x\n",
status);
return(status);
}
*/
}
/***************************************************
* Request a 128kb physically contiguous buffer *
* aligned to a 64kb boundry. This buffer will be *
* divided into half. The first half will be used *
* to perform DMA block writes with the VME *
* adapters DMA engine. The second half will be *
* used to perform DMA block mode reads with the *
* VME adapters DMA engine. Dynamically loaded *
* drivers specify the size and alignment of the *
* physically contiguous buffer using the *
* sysconfigtab file fragment's CMA_Option entry. *
* The kernel interface, contig_malloc, will *
* return a pointer to the physically contiguous *
* memory buffer. Statically loaded drivers will *
* also be able to obtain a pointer to a physically*
* contiguous buffer using the contig_malloc *
* interface. *
***************************************************/
dmaex_contig_buf = (char *)contig_malloc(PHYS_CONTIG_BUF_SIZE, 0x10000, 0,
M_DEVBUF, M_ZERO | M_WAITOK);
if (!(dmaex_contig_buf))
printf("register_configuration: No physically contig memory allocated\n");
else {
dmaex_contig_wrt_buf = dmaex_contig_buf;
dmaex_contig_rd_buf = dmaex_contig_buf + CONTIG_RD_WRT_BUF_SIZE;
}
return(ESUCCESS);
}
/***************************************************
* register_major_number() *
***************************************************
* Name: register_major_number *
* *
* Arguments: *
* *
* Kernel support interface calls: *
* *
* o devsw_add *
* *
* Called by: *
* *
* o cfgmgr framework *
* *
* Return Codes: *
* *
* Success: ESUCCESS *
* *
* Failure: ENODEV *
* *
* Description: *
* *
* The register_major_number interface registers *
* a drivers I/O services interface (and other *
* information) and receives a major number. It *
* accomplishes these tasks by calling devsw_add *
* interface. *
***************************************************/
static int
register_major_number()
{
/***************************************************
* If there are no devices present in the system *
* after driver configuration completes, set the *
* cfgmgr framework status to unconfigured and *
* exit the callback *
***************************************************/
DMAEX_DBG1("dmaex: register_major_num:\n");
if (num_dmaex == 0) {
printf("register_major_number: num_dmaex not updated by probe\n");
return (ENODEV);
}
/***************************************************
* Call devsw_add interface to register the *
* driver's I/O services interfaces (and other *
* information) and to reserve a major number in *
* the device switch table. Each allocation *
* provides a block and character entry. *
***************************************************/
majnum = devsw_add(mcfgname, MAJOR_INSTANCE,
majnum, &dmaex_devsw_entry);
/***************************************************
* call to devsw_add could fail if the driver *
* requests a specific major number and that *
* major number is currently in use. The call *
* could also fail if the device switch table is *
* currently full. *
***************************************************/
if (majnum == NO_DEV) {
printf("register_major_num: no major number assigned\n");
return (ENODEV);
}
/***************************************************
* Store the major number returned by the *
* devsw_add interface so that is can be used *
* later to unconfigure the device.
***************************************************/
dmaex_devno = majnum;
/***************************************************
* This member indicates that the beginning minor *
* number will be zero (0). *
***************************************************/
begunit = 0;
/***************************************************
* Set this state field to indicate that the *
* driver has successfully configured. *
***************************************************/
dmaex_config = TRUE;
return(ESUCCESS);
}
/***************************************************
* callback_register_configuration() *
***************************************************
* Name: callback_register_configuration *
* *
* Arguments: *
* *
* int point - driver callback point *
* int order - Priority at callback *
* ulong args - User argument *
* *
* Kernel support interface calls: *
* *
* o cfgmgr_set_status *
* o register_configuration *
* *
* Called by: *
* *
* o cfgmgr framework called through a call *
* to the register_callback interface *
* *
* Return Codes: *
* *
* Success: ESUCCESS *
* *
* Failure: EBUSY *
* EINVAL *
* ESRCH *
* *
* Description: *
* *
* This is the Static cfgmgr jacket used to do *
* configuration registration in the boot path. *
* This jacket routine handles the static callback *
* specific error handling but utilizes the same *
* configuration routine used by the driver when *
* dynamically loading. *
* *
***************************************************/
static void
callback_register_configuration(int point,
int order,
ulong args,
ulong event_arg)
{
int status;
DMAEX_DBG1("dmaex: callback_register_configuration:\n");
/************************************************
* Detect if a previous failure has occurred in *
* statically configuring this driver. If so, *
* exit the callback without doing any *
* configuration work. *
************************************************/
if(dmaexcallback_return_status != ESUCCESS) {
printf
("callback_register_configuration: dmaexcallback_return_status error\n");
return;
}
/************************************************
* Register this drivers configuration with the *
* kernel's topology. *
************************************************/
status = register_configuration();
/************************************************
* If configuration registration is successful, *
* then just return from this callback. Other- *
* wise signal the cfgmgr framework that this *
* driver has failed to configure and set the *
* variable callback_return_status to the *
* failure condition. *
* *
***************** NOTE *************************
* *
* A side effect of calling cfgmgr_set_status *
* interface is that the configure interface or *
* this driver is called with the unconfigure *
* op passed. The unconfigure interface must be *
* be prepared for this event. *
************************************************/
if(status != ESUCCESS) {
printf
("callback_register_configiguration: register_configuration error\n");
cfgmgr_set_status(mcfgname);
dmaexcallback_return_status = status;
return;
}
}
/***************************************************
* callback_register_major_number() *
***************************************************
* Name: callback_register_major_number *
* *
* Arguments: *
* *
* int point - driver callback point *
* int order - Priority at callback *
* ulong args - User argument *
* *
* Kernel support interface calls: *
* *
* o cfgmgr_set_status *
* o register_major_number *
* *
* Called by: *
* *
* o cfgmgr framework as static callback *
* *
* Return Codes: *
* *
* Success: ESUCCESS *
* *
* Failure: EBUSY *
* EINVAL *
* ESRCH *
* *
* Description: *
* *
* This is the Static cfgmgr jacket used to *
* do major number registration in the boot path. *
* This jacket routine handles the static callback *
* specific error handling but utilizes the same *
* configuration routine used by the driver when *
* dynamically loading. *
* *
***************************************************/
static void
callback_register_major_number(int point,
int order,
ulong args,
ulong event_arg)
{
int status;
DMAEX_DBG1("dmaex: callback_register_major_number:\n");
/*************************************************
* Detect if a previous failure has occurred in *
* statically configuring this driver. If so, *
* exit the callback without doing any *
* configuration work. *
*************************************************/
if(dmaexcallback_return_status != ESUCCESS) {
printf
("callback_register_major_number: dmaexcallback_return_status error\n");
return;
}
/*************************************************
* Call driver-specific register_major_number *
* interface to register the driver's I/O *
* services interfaces (and other information) *
* and reserve a major number *
*************************************************/
status = register_major_number();
/*************************************************
* If configuration registration is successful, *
* return from this callback. Otherwise, signal *
* the cfgmgr framework that this driver has *
* failed to configure and set the variable *
* callback_return_status to the failure *
* condition. *
* *
*************** Note ***************************
* *
* A side effect of calling the cfgmgr_set_status*
* interface is that the configure interface or *
* this driver is called with the unconfigure op *
* passed. The unconfigure routine must be *
* prepared for this event. *
*************************************************/
if(status != ESUCCESS) {
printf("callback_register_major_number: register_major_number error \n");
cfgmgr_set_status(mcfgname);
dmaexcallback_return_status = status;
return;
}
}
/*****************************************************
* Autoconfiguration Support Section *
* *
*--------------- dmaex_probe -----------------------*
* *
* The bus configuration interface calls the *
* driver's dmaex_probe interface during dynamic *
* and static configuration. *
* *
* The dmaex_probe interface calls the BADADDR *
* interface during static configration to determine *
* if the device is present. If the device is not *
* present then the interface returns with error. *
* Dynamic configuration assumes the device to be *
* present. *
* *
* Dmaex_probe registers the interrupt service *
* interfaces(s) and returns a one (1) for success *
* or zero (0) for failure. *
*****************************************************/
dmaex_probe(io_handle_t addr, /* Base I/O handle for DMAEX device */
struct controller *ctlr) /* Pointer to controller structure */
{
/***************************************************
* Declarations: *
* *
* o An ihandler_t structure called handler to *
* contain information associated with device *
* driver interrupt handling. *
* *
* o A handler_intr_info structure called info to *
* contain interrupt handler information. *
* *
* o A unit variable to contain the unit number *
* associated with this DMAEX device. The *
* unit number is obtained from ctlr->ctlr_num. *
* *
* o A csr variable to obtain the return value *
* from read_io_port. *
***************************************************/
ihandler_t handler;
struct vme_handler_info info;
int unit,i;
char csr;
u_long phys_addr;
caddr_t sva;
register struct dmaex_softc *sc = &dmaex_softc[ctlr->ctlr_num];
unit = ctlr->ctlr_num;
DMAEX_DBG1("dmaex_probe: dmaex%d\n",unit);
sc->csr_handle = (io_handle_t)addr;
if(dmaex_is_dynamic == SUBSYSTEM_STATICALLY_CONFIGURED) {
/****************************************************
* Calls the iohandle_to_phys interface to convert *
* an I/O handle to a valid system physical address *
* present. *
****************************************************/
phys_addr = iohandle_to_phys(sc->csr_handle + DMAEX_CSR_OFF,
HANDLE_BYTE | HANDLE_SPARSE_SPACE);
sva = (caddr_t)PHYS_TO_KSEG((vm_offset_t)phys_addr);
/***************************************************
* Call BADADDR to determine if device is present. *
* BADADDR returns the value zero (0) if the device*
* is present. BADADDR returns a nonzero value if *
* the device is not present. *
* *
* It is recommended that BADADDR only be called *
* for drivers that are statically configured into *
* kernel. During static configurations, driver *
* probe interfaces are single threaded by the *
* kernel. Therfore, BADADDR failures are *
* guaranteed to be that of the driver issuing the *
* BADADDR. This is not true for dynamically *
* loaded drivers. It is not guaranteed that a *
* BADADDR failure in this case is the result of *
* this driver's BADADDR access. It could be an *
* access caused by another driver in the system *
* to an invalid address or failing hardware. *
***************************************************/
if (!dmaex_no_dev) {
/***************************************************
* The software flag indicates that the 'dmaex' *
* device hardware is present. Perform the BADADDR *
***************************************************/
if (BADADDR(sva, DMAEX_CSR_SIZE, ctlr))
return(0);
}
}
/***************************************************
* If the dmaex_no_dev flag is set, bypass all *
* accesses to the device's control and status *
* hardware registers. There is no hardware device *
***************************************************/
if (!dmaex_no_dev) {
/***************************************************
* Reset the device. The write_io_port interface *
* is called to write the data, and it will *
* perform a memory barrier to insure sequential *
* writes to I/O space. *
***************************************************/
write_io_port (sc->csr_handle + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0, RESET);
mb(); /* perform a memory barrier */
/***************************************************
* Read the byte from the VMEbus by calling the *
* read_io_port interface. If the result of the *
* bitwise AND is nonzero, dmaex_probe returns the *
* value zero (0) to the configuration code to *
* indicate that the device is not responding. *
***************************************************/
csr = (char) read_io_port (sc->csr_handle + DMAEX_CSR_OFF,
DMAEX_CSR_SIZE, 0);
if (csr & ERROR)
return(0);
/***************************************************
* Otherwise, the result of the bitwise AND is *
* zero indicating that the error bit is not set. *
* The dmaex_probe interface calls write_io_port *
* to write a byte to the VMEbus. Clear csr reg. *
***************************************************/
write_io_port (sc->csr_handle + DMAEX_CSR_OFF,
DMAEX_CSR_SIZE, 0, 0);
mb(); /* perform a memory barrier */
}
/***************************************************
* Register the driver's interrupt service *
* interface by calling handler_add. Then enable *
* it by calling handler_enable. *
***************************************************/
/* initialize information for handler_add() */
info.gen_intr_info.configuration_st = (caddr_t) ctlr;
info.gen_intr_info.intr = dmaex_intr1;
info.gen_intr_info.param = (caddr_t) unit;
info.gen_intr_info.config_type = CONTROLLER_CONFIG_TYPE;
info.vec = ctlr->ivnum; /* VME vector */
info.irq = ctlr->bus_priority; /* VME IPL */
handler.ih_bus = ctlr->bus_hd;
handler.ih_bus_info = (char *) &info;
/* note: handler.ih_id is initialized in handler_add() */
dmaex_id_t[unit][0] = handler_add (&handler);
if (dmaex_id_t[unit][0] == (ihandler_id_t *) NULL) {
return (0);
}
if (handler_enable (dmaex_id_t[unit][0]) != 0) {
handler_del (dmaex_id_t[unit][0]);
return (0);
}
/***************************************************
* Adding another interrupt service routine: *
* Generally the next interrupt vector is *
* sequentially incremented from what is provided *
* in ctlr->ivnum *
***************************************************/
info.gen_intr_info.intr = dmaex_intr2;
info.vec = ctlr->ivnum + 1; /* VME vector */
dmaex_id_t[unit][1] = handler_add (&handler);
if (dmaex_id_t[unit][1] == (ihandler_id_t *) NULL) {
handler_disable(dmaex_id_t[unit][0]);
handler_del(dmaex_id_t[unit][0]);
return (0);
}
if (handler_enable (dmaex_id_t[unit][1]) != 0) {
handler_del (dmaex_id_t[unit][1]);
handler_disable(dmaex_id_t[unit][0]);
handler_del(dmaex_id_t[unit][0]);
return (0);
}
/***************************************************
* Create an event for the device to signal when *
* an interrupt has been received *
***************************************************/
event_init( (event_t *)&sc->isi_event);
event_clear( (event_t *)&sc->isi_event);
/***************************************************
* Create events to be used by the ioctl interface *
* and the iack isr when posting VMEbus interrupts.*
* *
* Interrupts can be posted on VME IRQs 1 to 7, *
* therefore, create an event for each IRQ. *
***************************************************/
for (i = 1; i < 8; i++) {
event_init( (event_t *)&sc->post_iack_event[i]);
event_clear( (event_t *)&sc->post_iack_event[i]);
}
/***************************************************
* update the number of /dev/dmaex devices probed *
***************************************************/
num_dmaex++;
/***************************************************
* Return a value of 1 to indicate that the *
* driver's probe interface succeeded. *
***************************************************/
return (1); /* return success status */
}
/***************************************************
* Controller Initialization Section *
* *
*--------------- dmaex_cattach -------------------*
* *
* The bus configuration interface calls the *
* driver's attach interface during dynamic and *
* static configuration. This interface is usually *
* used to perform tasks necessary to establish *
* communication with the actual device. *
* *
* The dmaex_cattach interface does not currently *
* perform any tasks (other then to return to the *
* bus configuration code with success). It is *
* provided here as a stub for future development. *
***************************************************/
int
dmaex_cattach(struct controller *ctlr)
{
struct dmaex_softc *sc = &dmaex_softc[ctlr->ctlr_num];
DMAEX_DBG1("dmaex_cattach: dmaex%d\n",ctlr->ctlr_num);
return;
}
/***************************************************
* Controller Unattach Section *
* *
*----------- dmaex_ctlr_unattach -----------------*
* *
* The device driver's controller unattach *
* interface is called directly from the bus *
* configuration code's controller unconfigure *
* interface when a driver is being unloaded. *
* *
* Statically configured device drivers can not be *
* unloaded. The interface returns ENODEV for this *
* unconfigure request. *
* *
* Dynamically loaded drivers need to deregister *
* interrupt handlers, return physical memory *
* allocated with contig_malloc back to the *
* system, unwire memory that may have been wired *
* down by vm_map_pageable, unmap VME program I/O *
* space, unmap VME DMA resources, and return any *
* other system resources back to the system. *
***************************************************/
int
dmaex_ctlr_unattach(struct bus *bus,
struct controller *ctlr)
{
struct dmaex_softc *sc = &dmaex_softc[ctlr->ctlr_num];
register int unit = ctlr->ctlr_num;
int i;
DMAEX_DBG1("dmaex_ctlr_unattach: dmaex%d\n",unit);
/***************************************************
* Validate the unit number. *
***************************************************/
if ((unit > num_dmaex) || (unit < 0)) {
printf("dmaex_ctlr_unattach: unit %d not valid\n",unit);
return(ENODEV);
}
/***************************************************
* A statically configured device driver can not *
* be unloaded. Therefore return ENODEV as an *
* error condition *
***************************************************/
if (dmaex_is_dynamic == SUBSYSTEM_STATICALLY_CONFIGURED) {
printf("dmaex_ctlr_unattach: statically configured driver\n");
return(ENODEV);
}
/***************************************************
* The deregistration of interrupt handlers *
* consists of a call to handler_disable to *
* disable any further interrupts. Then, call *
* handler_del to remove the interrupt handler. *
***************************************************/
for (i = 0; i < NINTS_PER_DMAEX; i++) {
if (dmaex_id_t[unit][i]) {
if (handler_disable(dmaex_id_t[unit][i]) != 0) {
printf("dmaex_ctlr_unattach: dmaex%d handler_disable failure\n",unit);
return(ENODEV);
}
if (handler_del(dmaex_id_t[unit][i]) != 0) {
printf("dmaex_ctlr_unattach: dmaex%d handler_del failure\n",unit);
return(ENODEV);
}
} else {
printf("dmaex_ctlr_unattach: dmaex[%d][%d] no interrupt handler\n",
unit,i);
}
}
/***************************************************
* Remove interrupt handler installed by ioctl *
* SET_INT_HANDLER *
***************************************************/
if (sc->rcvisr_id_t) {
if (handler_disable(sc->rcvisr_id_t) != 0) {
printf("dmaex_ctlr_unattach: dmaex%d handler_disable failed\n",unit);
return(ENODEV);
} else {
if (handler_del(sc->rcvisr_id_t) != 0) {
printf("dmaex_ctlr_unatttach: dmaex%d handler_del failed\n",unit);
return(ENODEV);
} else {
sc->rcvisr_irq = 0;
sc->rcvisr_id_t = (ihandler_id_t *)NULL;
sc->rcvisr_proc_p = (struct proc *)NULL;
}
}
}
/***************************************************
* If the 128kb physically contiguous 64kb aligned *
* memory was allocated, return the memory to the *
* systems physical memory resource pool. *
***************************************************/
if (dmaex_contig_buf) {
contig_free(dmaex_contig_buf,PHYS_CONTIG_BUF_SIZE,M_DEVBUF);
dmaex_contig_buf = NULL;
}
/***************************************************
* If VMEbus to processor cpu I/O space mapping *
* was performed for the use of mmaping from user *
* space, return VME system resources. *
***************************************************/
if (sc->pio_handle) {
vba_unmap_csr(ctlr,sc->pio_handle);
sc->pio_handle = (io_handle_t)0;
sc->pio_vme_addr = 0;
sc->pio_vme_am = 0;
sc->pio_vme_size = 0;
sc->pio_access_type = 0;
}
/***************************************************
* If VMEbus to processor cpu I/O space mapping *
* was performed for the use by UNIX's read or *
* write file system interfaces, return VME system *
* resources. *
***************************************************/
if (sc->vme_handle) {
vba_unmap_csr(ctlr,sc->vme_handle);
sc->vme_handle = (io_handle_t)0;
sc->vme_addr = 0;
sc->vme_am = 0;
sc->vme_size = 0;
sc->vme_wrk_handle = (io_handle_t)0;
sc->vme_wrk_size = 0;
}
/***************************************************
* Free VME DMA resources that still may be mapped *
* from the VMEbus to the system's memory. *
***************************************************/
if (sc->dma_handle) {
dma_map_unload (DMA_DEALLOC, sc->dma_handle);
sc->dma_handle = (dma_handle_t) NULL;
}
if (sc->sys_mem_dma_handle) {
dma_map_unload (DMA_DEALLOC, sc->sys_mem_dma_handle);
sc->sys_mem_dma_handle = (dma_handle_t) NULL;
/*
* Unwire the memory associated with the mapping
*/
vm_map_pageable(current_task()->map,
trunc_page(sc->sys_mem_vir_addr),
round_page(sc->sys_mem_vir_addr +
sc->sys_mem_vme_size),
VM_PROT_NONE);
}
/***************************************************
* Free DMA block mode write resources that still *
* may be mapped for the hardware DMA engine *
***************************************************/
if (sc->blk_wrt_dma_handle) {
dma_map_unload(DMA_DEALLOC, sc->blk_wrt_dma_handle);
}
if (sc->blk_rd_dma_handle) {
dma_map_unload(DMA_DEALLOC, sc->blk_rd_dma_handle);
}
/***************************************************
* Release system resource used by devices *
* interrupt service interface. *
***************************************************/
event_terminate( (event_t *)&sc->isi_event);
/***************************************************
* Release system resources reserved for posting *
* VMEbus interrupt request. *
***************************************************/
for (i = 1; i < 8; i++)
event_terminate( (event_t *)&sc->post_iack_event[i]);
return(ESUCCESS);
}
/***************************************************
* Open and Close Device Section *
* *
*--------------- dmaex_open ----------------------*
* *
* The dmaex_open interface checks that the device *
* is open uniquely. *
***************************************************/
int
dmaex_open(dev_t dev, /* Major/minor device number */
int flag, /* Flags from file.h */
int format) /* Format of special device */
{
/***************************************************
* The following code initializes a unit variable *
* to the minor device number and declares: *
* *
* o A pointer to a controller structure *
* associated with this DMAEX device *
* *
* o A pointer to a dmaex_softc structure *
* associated with this DMAEX device *
***************************************************/
register int unit = minor(dev);
register struct controller *ctlr;
register struct dmaex_softc *sc;
/***************************************************
* The following code makes sure that: *
* *
* o The unit number is no more than the maximum *
* number of devices configured on the system *
* *
* o The open is unique *
* *
* Note: that this sequence of code also initial- *
* izes the pointer sc to the address of the *
* dmaex_softc structure associated with this *
* DMAEX device. *
***************************************************/
DMAEX_DBG1("dmaex_open: dmaex%d\n",unit);
if ((unit >= NDMAEX ) || (unit < 0))
return (EIO);
sc = &dmaex_softc[unit];
if (sc == (struct dmaex_softc *)NULL)
return (ENODEV);
if (sc->sc_open == DMAEXOPEN)
return (EBUSY);
/***************************************************
* This sequence of code initializes the *
* controller structure pointer to its associated *
* DMAEX device. *
* *
* If the DMAEX device is initialized, set the *
* sc_open member to the open bit DMAEXOPEN and *
* returns the value zero (0) to indicate success. *
* *
* Otherwise if the device does not exist, return *
* an error. *
***************************************************/
ctlr = dmaex_ctlr[unit];
if ((ctlr) && (ctlr->alive == ALV_ALIVE)) {
sc->sc_open = DMAEXOPEN;
return(ESUCCESS);
} else
return(ENXIO);
}
/***************************************************
* *
*--------------- dmaex_close ---------------------*
* *
* The dmaex_close interface clears the DMAEXOPEN *
* flag to allow other processes to use the *
* device. *
***************************************************/
dmaex_close(dev_t dev, /* Major/minor device number */
int flag, /* Flags from file.h */
int format) /* Format of special device */
{
/***************************************************
* The following code initializes a unit variable *
* to the minor device number and declares: *
* *
* o A pointer to a controller structure *
* associated with this DMAEX device *
* *
* o A pointer to a dmaex_softc structure *
* associated with this DMAEX device *
***************************************************/
register int unit = minor(dev);
register struct controller *ctlr;
register struct dmaex_softc *sc;
/***************************************************
* The following code: *
* *
* o Initializes the sc pointer to the *
* dmaex_softc structure associated with this *
* DMAEX device *
* *
* o Initializes the ctlr pointer to the *
* controller structure associated with this *
* DMAEX device *
* *
* o Calls write_io_port to write a byte to *
* the VMEbus *
* *
* o Returns the value zero (0) to indicate a *
* successful close of the DMAEX device *
***************************************************/
DMAEX_DBG1("dmaex_close: dmaex%d\n",unit);
sc = &dmaex_softc[unit];
sc->sc_open = DMAEXCLOSE;
ctlr = dmaex_ctlr[unit];
if (!(ctlr))
return (ENODEV);
/***************************************************
* This driver assumes that the driver writer *
* requested the configuration software to map *
* VMEbus address space for device register access *
***************************************************/
if (sc->csr_handle == (io_handle_t)0)
return (ENODEV);
/***************************************************
* If the software flag indicates that the 'dmaex' *
* device is not present, bypass the writing of *
* device's control and status register. *
***************************************************/
if (!dmaex_no_dev) {
write_io_port (sc->csr_handle + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0, 0);
mb();
}
return(ESUCCESS);
}
/***************************************************
* *
*--------------- dmaex_mmap ----------------------*
* *
* The dmaex_mmap interface is invoked by the *
* kernel as a result of the application calling *
* the mmap(2) system call. dmaex_mmap returns *
* the page frame number corresponding to the page *
* at the specified offset. *
* *
* Depending on the device drivers mmap mode *
* sc->mmap_mode, a mapped VMEbus address can be *
* mapped into user space for user PIO operations *
* to the device, or a kernel physically *
* contiguous write or read buffer mapped to *
* VMEbus DMA space can be mapped to user space. *
***************************************************/
int
dmaex_mmap(dev_t dev,
off_t off,
int prot)
{
long kpfnum,flags;
register int unit = minor(dev);
register struct controller *ctlr = dmaex_ctlr[unit];
register struct dmaex_softc *sc = &dmaex_softc[unit];
caddr_t phys_addr = 0;
int error = 0;
DMAEX_DBG1("dmaex_mmap: dmaex%d\n",unit);
switch (sc->mmap_mode) {
case MMAP_VME_TO_U_MEM:
/***************************************************
* Prior to the user level device driver calling *
* the mmap interface, it must invoke the ioctl *
* interface with SETUP_VME_FOR_MMAP_PIO ioctl *
* parameters. This ioctl interface allows the *
* user level driver to specify the VMEbus address,*
* the VMEbus address modifiers, the swap modes, *
* the data size (ignored on some VME adapters) *
* and number of bytes to be mapped onto VMEbus. *
***************************************************/
if (!sc->pio_handle) {
printf("dmaex_mmap: You must do an outbound mapping first. \n");
error++;
} else {
if ((sc->pio_vme_am) & VME_DENSE)
flags = HANDLE_DENSE_SPACE;
else
flags = HANDLE_SPARSE_SPACE;
/*
* OR in the access type : eg. HANDLE_LONGWORD
*/
flags |= (u_int)(sc->pio_access_type);
/***************************************************
* Convert the io_handle that represents the cpu *
* I/O space to VMEbus mapping to a kernel KSEG *
* memory address. *
***************************************************/
phys_addr = ((caddr_t)iohandle_to_phys(sc->pio_handle, flags));
DMAEX_DBG2("*** io_handle = 0x%lx\n",(u_long)sc->pio_handle);
DMAEX_DBG2("*** Bus physical address = 0x%lx\n",phys_addr);
phys_addr = (char *)PHYS_TO_KSEG(phys_addr);
}
break;
case MMAP_K_TO_U_MEM_WRT:
/***************************************************
* Prior to the user level device driver calling *
* the mmap interface, it must invoke the ioctl *
* interface with SETUP_DMA_BLK_WRT ioctl *
* parameters. This ioctl interface allows the *
* the user level driver to specify the VMEbus *
* address, the VMEbus address modifiers, the swap *
* modes, the data size, the number of bytes to be *
* written during the DMA transfer. This interface *
* allows the physically contiguous kernel memory *
* buffer to be mapped into user space. The memory *
* buffer was allocated by this driver with *
* contig_malloc interface. *
***************************************************/
if (!sc->blk_wrt_dma_handle) {
printf("dmaex_mmap: need to call SETUP_DMA_BLK_WRT ioctl\n");
error++;
} else {
if (sc->blk_wrt_proc_p) {
/*
* When SETUP_DMA_BLK_WRT ioctl interface was called, the
* user specified his own buffer and chose not to use the
* physically contiguous memory buffer.
*/
printf("dmaex_mmap: user buffer mapped - SETUP_DMA_BLK_WRT ioctl\n");
error++;
} else {
/*
* retrieve the kernel KSEG address saved in the
* SETUP_DMA_BLK_WRT ioctl
*/
phys_addr = sc->blk_wrt_buf_ptr;
}
}
break;
case MMAP_K_TO_U_MEM_RD:
/***************************************************
* Prior to the user level device driver calling *
* the mmap interface, it must invoke the ioctl *
* interface with SETUP_DMA_BLK_RD ioctl *
* parameters. This ioctl interface allows the *
* the user level driver to specify the VMEbus *
* address, the VMEbus address modifiers, the swap *
* modes, the data size, the number of bytes to be *
* read during the DMA transfer. This interface *
* allows the physically contiguous kernel memory *
* buffer to be mapped into user space. The memory *
* buffer was allocated by this driver with *
* contig_malloc interface. *
***************************************************/
if (!sc->blk_rd_dma_handle) {
printf("dmaex_mmap: need to call SETUP_DMA_BLK_RD ioctl\n");
error++;
} else {
if (sc->blk_wrt_proc_p) {
/*
* When SETUP_DMA_BLK_RD ioctl interface was called, the
* user specified his own buffer and chose not to use the
* physically contiguous memory buffer.
*/
printf("dmaex_mmap: user buffer mapped - SETUP_DMA_BLK_RD ioctl\n");
error++;
} else {
/*
* retrieve the kernel KSEG address saved in the
* SETUP_DMA_BLK_RD ioctl
*/
phys_addr = sc->blk_rd_buf_ptr;
}
}
break;
default:
printf("dmaex_mmap: Invalid mmap selection mode\n");
error++;
}
/*
* return -1 to caller if an error condtion was detected.
*/
if (error)
return(-1);
DMAEX_DBG2("*** Kseg address = 0x%lx\n",phys_addr);
/***************************************************
* Calculate the ALPHA page frame number from the *
* KSEG memory address plus the byte offset *
* provided by the system. Return the frame number *
* to the file system calling interface *
***************************************************/
kpfnum = (long) ((((u_long)phys_addr + off) >> 13) & (0x1fffffffL));
DMAEX_DBG2("*** Page frame number = 0x%lx\n",kpfnum);
return((int)kpfnum);
}
/***************************************************
* *
*--------- dmaex_setup_blk_mode ------------------*
* *
* This interface sets up the VME adapters DMA *
* engine for either a DMA block mode write or *
* read operation. The actual DMA is performed by *
* DO_DMA_BLK_WRT or DO_DMA_BLK_RD ioctl call. *
***************************************************/
int
dmaex_setup_blk_mode(struct controller *ctlr,
struct dmaex_softc *sc,
struct dmaex_ioctl_data *data,
int direction)
{
struct proc *proc_p = (struct proc *)NULL;
unsigned long b_count;
sg_entry_t dmaex_sg;
vme_atype_t flags;
DMAEX_DBG2("dmaex_setup_blk_mode: - direction = 0x%x\n",direction);
/***************************************************
* If the caller specified a system virtual *
* address, then the process pointer is needed for *
* dma_map_load. The process pointer is not needed *
* if a kernel physical buffer is to be used. *
***************************************************/
if (data->data[3])
proc_p = task_to_proc(current_task());
else {
/***************************************************
* The caller did not specify a user buffer, check *
* that the requested byte count does not exceed *
* the kernel's physically contiguous memory *
* buffer allocated with contig_malloc. *
***************************************************/
if ((data->data[0] > (unsigned long)(CONTIG_RD_WRT_BUF_SIZE)) ||
(!dmaex_contig_buf)) {
printf("dmaex_setup_blk_mode: buffer to large or no kernel buffer\n");
return(EINVAL);
}
}
/***************************************************
* Update the VME address modifiers with DMA *
* specific flags *
***************************************************/
flags = (vme_atype_t)
((data->data[2] & VME_BITS_MASK) | DMA_SLEEP | DMA_GUARD_UPPER | DMA_ALL);
/***************************************************
* Obtain the mapping information from the ioctl *
* data parameters, update VME address modifiers *
* with the direction of transfer (DMA_IN or *
* DMA_OUT, and save the buffer address. The *
* buffer address will either be user supplied *
* buffer or a kernel physically contiguous buffer *
***************************************************/
if (direction & DMA_OUT) {
/*
* Setting up for a DMA block mode write
*/
sc->blk_wrt_vme_size = (unsigned int)data->data[0];
sc->blk_wrt_vme_addr = (vme_addr_t)data->data[1];
sc->blk_wrt_vme_am = (vme_atype_t)(data->data[2] & VME_BITS_MASK);
sc->blk_wrt_dma_handle = (dma_handle_t)NULL;
sc->blk_wrt_proc_p = proc_p;
flags |= DMA_OUT;
/*
* If caller specified a user buffer, use the callers buffer,
* otherwise, use the physical contiguous kernel write buffer.
*/
if (proc_p)
sc->blk_wrt_buf_ptr = (caddr_t)data->data[3];
else
sc->blk_wrt_buf_ptr = dmaex_contig_wrt_buf;
} else {
/*
* Setting up for a DMA block mode read
*/
sc->blk_rd_vme_size = (unsigned int)data->data[0];
sc->blk_rd_vme_addr = (vme_addr_t)data->data[1];
sc->blk_rd_vme_am = (vme_atype_t)(data->data[2] & VME_BITS_MASK);
sc->blk_rd_dma_handle = (dma_handle_t)NULL;
sc->blk_rd_proc_p = proc_p;
flags |= DMA_IN;
/*
* If caller specified a user buffer, use the callers buffer,
* otherwise, use the physical contiguous kernel write buffer.
*/
if (proc_p)
sc->blk_rd_buf_ptr = (caddr_t)data->data[3];
else
sc->blk_rd_buf_ptr = dmaex_contig_rd_buf;
}
/***************************************************
* When using the DMA engine, the VMEbus address *
* and flags with DMA_IN or DMA_OUT must be *
* provided to the vba_set_dma_addr interface. *
***************************************************/
flags = vba_set_dma_addr(ctlr, flags, (vme_addr_t)data->data[1]);
/***************************************************
* Allocate, load and set system resources for the *
* DMA block mode transfer. *
***************************************************/
if (direction == DMA_OUT) {
b_count = dma_map_load( (unsigned long)sc->blk_wrt_vme_size,
(vm_offset_t)sc->blk_wrt_buf_ptr,
proc_p,
ctlr,
&sc->blk_wrt_dma_handle,
0,
flags);
if ( (!b_count) || (!sc->blk_wrt_dma_handle) ) {
printf("dmaex_setup_blk_mode: - write - dma_map_load failed.\n");
return(EIO);
}
/***************************************************
* Obtain the VMEbus address and byte count of DMA *
* block write request *
***************************************************/
dmaex_sg = dma_get_curr_sgentry(sc->blk_wrt_dma_handle);
} else {
b_count = dma_map_load( (unsigned long)sc->blk_rd_vme_size,
(vm_offset_t)sc->blk_rd_buf_ptr,
proc_p,
ctlr,
&sc->blk_rd_dma_handle,
0,
flags);
if ( (!b_count) || (!sc->blk_rd_dma_handle) ) {
printf("dmaex_setup_blk_mode: - read - dma_map_load failed.\n");
return(EIO);
}
/***************************************************
* Obtain the VMEbus address and byte count of DMA *
* block read request *
***************************************************/
dmaex_sg = dma_get_curr_sgentry(sc->blk_rd_dma_handle);
}
DMAEX_DBG2("dmaex_setup_blk_mode: sg.ba = 0x%lx sg.bc = 0x%x\n",
dmaex_sg->ba, dmaex_sg->bc);
return(ESUCCESS);
}
/***************************************************
* *
*--------------- dmaex_ioctl ---------------------*
* *
* The dmaex_ioctl interface performs driver *
* specific ioctl functions. *
* *
***************************************************/
int
dmaex_ioctl(dev_t dev, /* fd */
int cmd, /* ioctl command */
struct dmaex_ioctl_data *data, /* user command-specified data */
int flag) /* access mode */
{
int unit = minor(dev);
struct dmaex_softc *sc = &dmaex_softc[unit];
struct controller *ctlr = dmaex_ctlr[unit];
int ret_val = ESUCCESS;
struct proc *proc_p;
sg_entry_t dmaex_sg;
ihandler_t handler;
struct vme_handler_info info;
unsigned int irq;
unsigned int vector;
DMAEX_DBG1("dmaex_ioctl: dmaex%d\n",unit);
DMAEX_DBG2("param0 = 0x%lx param1 = 0x%lx param2 = 0x%lx\n",
data->data[0],data->data[1],data->data[2]);
if ((unit >= NDMAEX) || (unit < 0))
return(ENODEV);
switch(cmd) {
case SET_MMAP_MODE:
/***************************************************
* The SET_MMAP_MODE ioctl defines how dmaex_mmap *
* will interpret a mmap request. The mmap mode is *
* passed in the ioctl data parameter. *
* data->data[0] mmap mode selection *
* MMAP_VME_TO_U_MEM - will return page frame *
* number representing a *
* mapped VMEbus address *
* MMAP_K_TO_U_MEM_WRT - will return page frame *
* number representing a *
* physical contiguous write *
* cma_dd kernel buffer. *
* MMAP_K_TO_U_MEM_RD - will return page frame *
* number representing a *
* physical contiguous read *
* cma_dd kernel buffer. *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: SET_MMAP_MODE\n");
switch ((int)data->data[0]) {
case MMAP_VME_TO_U_MEM:
case MMAP_K_TO_U_MEM_WRT:
case MMAP_K_TO_U_MEM_RD:
sc->mmap_mode = (int)data->data[0];
break;
default:
ret_val = EINVAL;
}
break;
case GET_MMAP_MODE:
/***************************************************
* The GET_MMAP_MODE ioctl returns mmap mode *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: GET_MMAP_MODE\n");
data->data[0] = (unsigned long)sc->mmap_mode;
break;
case SET_STRATEGY_XFER_MODE:
/***************************************************
* The SET_STRATEGY_XFER_MODE ioctl sets the data *
* transfer mode of the dmaex_strategy interface. *
* The dmaex_strategy interface is invoked by *
* physio when dmaex_read or dmaex_write is called *
* by the user application through Digital UNIX's *
* read or write interfaces. The dmaex_strategy *
* interface can perform the transfer using *
* programmed I/O, using the VME adapters DMA *
* engine ot using the VME device's DMA engine. *
* The transfer mode is specified in the ioctl *
* data parameter. *
* data->data[0] = data transfer mode *
* (PIO_XFER_MODE, DEVICE_DMA_MODE, BLOCK_DMA_MODE)*
***************************************************/
DMAEX_DBG2("dmaex_ioctl: SET_STRATEGY_XFER_MODE\n");
switch ((int)data->data[0]) {
case PIO_XFER_MODE:
case DEVICE_DMA_MODE:
case BLOCK_DMA_MODE:
sc->strategy_xfer_mode = (int)data->data[0];
break;
default:
ret_val = EINVAL;
}
break;
case GET_STRATEGY_XFER_MODE:
/***************************************************
* The GET_STRATEGY_XFER_MODE ioctl returns the *
* current data transfer mode that was specified *
* by the SET_STRATEGY_XFER_MODE ioctl. The *
* transfer mode is used by the dmaex_strategy *
* interface to determine how the data is to be *
* transferred. The transfer mode is returned in *
* the ioctl data interface. *
* data->data[0] = data transfer mode *
* (PIO_XFER_MODE, DEVICE_DMA_MODE, BLOCK_DMA_MODE)*
***************************************************/
DMAEX_DBG2("dmaex_ioctl: GET_STRATEGY_XFER_MODE\n");
data->data[0] = (unsigned long)sc->strategy_xfer_mode;
break;
case SETUP_VME_FOR_MMAP_PIO:
/***************************************************
* The SETUP_VME_FOR_MMAP_PIO ioctl maps outbound *
* to a user specified VME address, size and *
* address modifier. The information provided to *
* this ioctl will be used to perform a user mmap *
* request when the kernel invokes the dmaex_mmap *
* interface. The VMEbus configuration data is *
* passed in the data parameter of the ioctl call. *
* data->data[0] = number of bytes to map *
* data->data[1] = VMEbus address to be mapped *
* data->data[2] = VMEbus address modifiers *
* data->data[3] = PIO access (HANDLE_LONGWORD) *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: SETUP_VME_FOR_MMAP_PIO\n");
if (sc->pio_handle) {
printf("SETUP_VME_FOR_MMAP_PIO failed, a PIO mapping exists\n");
ret_val = EBUSY;
break;
}
/*
* Perform the requested outbound mapping
*/
sc->pio_handle = vba_map_csr(ctlr,
(vme_addr_t)data->data[1],
(u_int)data->data[0],
(u_int)(data->data[2] & VME_BITS_MASK));
if (!(sc->pio_handle)) {
printf("SETUP_VME_FOR_MMAP_PIO: vba_map_csr interface failed\n");
printf(" pio_addr = 0x%x pio_size = 0x%x pio_am = 0x%x\n",
(vme_addr_t)data->data[1],
(u_int)data->data[0],
(vme_atype_t)(data->data[2] & VME_BITS_MASK));
ret_val = EINVAL;
break;
}
/*
* Save the PIO mapping information for a mmap request
*/
sc->pio_vme_size = (unsigned int)data->data[0];
sc->pio_vme_addr = (vme_addr_t)data->data[1];
sc->pio_vme_am = (vme_atype_t)(data->data[2] & VME_BITS_MASK);
sc->pio_access_type = (unsigned int)data->data[3];
break;
case GET_VME_INFO_FOR_MMAP_PIO:
/***************************************************
* The GET_VME_INFO_FOR_MMAP_PIO ioctl is used to *
* return mmap specific VMEbus mapping information.*
* data->data[0] = number of bytes mapped *
* data->data[1] = VMEbus address mapped *
* data->data[2] = VMEbus address modifiers *
* data->data[3] = PIO access (HANDLE_LONGWORD) *
* data->data[4] = io_handle_t representing the *
* VMEbus mapped address *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: GET_VME_INFO_FOR_MMAP_PIO\n");
data->data[0] = (unsigned long)sc->pio_vme_size;
data->data[1] = (unsigned long)sc->pio_vme_addr;
data->data[2] = (unsigned long)sc->pio_vme_am;
data->data[3] = (unsigned long)sc->pio_access_type;
data->data[4] = (unsigned long)sc->pio_handle;
break;
case UNMAP_VME_FOR_MMAP_PIO:
/***************************************************
* The UNMAP_VME_FOR_MMAP_PIO ioctl is used to *
* return VMEbus mapped resources allocated by *
* SETUP_VME_FOR_MMAP_PIO ioctl back to the system.*
* No data is passed or returned from this *
* interface via the data parameter. *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: UNMAP_VME_FOR_MMAP_PIO\n");
if (sc->pio_handle) {
/*
* Unmap the previously mapped VMEbus address
*/
vba_unmap_csr(ctlr,sc->pio_handle);
sc->pio_handle = (unsigned long)0;
sc->pio_vme_addr = 0;
sc->pio_vme_am = 0;
sc->pio_vme_size = 0;
sc->pio_access_type = 0;
} else {
printf("dmaex_ioctl: UNMAP_VME_FOR_MMAP_PIO, no PIO to unmap.\n");
ret_val = ENXIO;
}
break;
case SET_STRATEGY_INFO_BLK_DMA:
/***************************************************
* The SET_STRATEGY_INFO_BLK_DMA ioctl provides *
* VMEbus mapping information. This information *
* is used by the dmaex_strategy interface when *
* the transfer mode equals BLOCK_DMA_MODE. *
* BLOCK_DMA_MODE causes the dmaex_strategy *
* interface to perform the data transfer using *
* the VME adapter's hardware DMA engine. VMEbus *
* mapping information is passed in the ioctl *
* data parameter. *
* data->data[0] = number of bytes to map *
* data->data[1] = VMEbus address to map *
* data->data[2] = VMEbus address modifiers *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: SET_STRATEGY_INFO_BLK_DMA\n");
/*
* Check that a mapping size and address modifiers specified
*/
if ( (!(unsigned int)data->data[0]) ||
(!((unsigned int)data->data[2] & VME_BITS_MASK)) ) {
printf("dmaex_ioctl: SET_STRATEGY_INFO_BLK_DMA, invalid parameters\n");
ret_val = EINVAL;
} else {
/*
* Save the DMA mapping information
*/
sc->dma_is_set = 1;
sc->dma_vme_size = (unsigned int)data->data[0];
sc->dma_vme_addr = (vme_addr_t)data->data[1];
sc->dma_vme_am = (vme_atype_t)(data->data[2] & VME_BITS_MASK);
sc->dma_wrk_vme_addr = 0;
sc->dma_wrk_vme_size = 0;
}
break;
case GET_STRATEGY_INFO_BLK_DMA:
/***************************************************
* The GET_STRATEGY_INFO_BLK_DMA ioctl returns *
* BLOCK_DMA_MODE mapping information. It returns *
* the initial mapping information specified to *
* ioctl SET_STRATEGY_INFO_BLK_DMA interface and *
* the results of the DMA transfer. The data is *
* returned through the data parameter of the *
* ioctl interface. *
* data->data[0] = number of bytes mapped *
* data->data[1] = VMEbus address mapped *
* data->data[2] = VMEbus address modifiers *
* data->data[3] = VMEbus address after transfer *
* data->data[4] = total bytes transferred *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: GET_STRATEGY_INFO_BLK_DMA\n");
data->data[0] = (unsigned long)sc->dma_vme_size;
data->data[1] = (unsigned long)sc->dma_vme_addr;
data->data[2] = (unsigned long)sc->dma_vme_am;
data->data[3] = (unsigned long)sc->dma_wrk_vme_addr;
data->data[4] = (unsigned long)sc->dma_wrk_vme_size;
break;
case CLR_STRATEGY_INFO_BLK_DMA:
/***************************************************
* The CLR_STRATEGY_INFO_BLK_DMA ioctl clears the *
* information previously specified to ioclt *
* interface SET_STRATEGY_INFO_BLK_DMA ioctl and *
* the infomation that was updated by the *
* dmaex_strategy interface when the transfer mode *
* was BLOCK_DMA_MODE. No parameters are passed to *
* this ioctl interface. *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: CLR_STRATEGY_INFO_BLK_DMA\n");
sc->dma_is_set = 0;
sc->dma_vme_size = 0;
sc->dma_vme_addr = 0;
sc->dma_vme_am = 0;
sc->dma_wrk_vme_addr = 0;
sc->dma_wrk_vme_size = 0;
break;
case SETUP_VME_FOR_STRATEGY_PIO:
/***************************************************
* The SETUP_VME_FOR_STRATEGY_PIO ioctl provides *
* VMEbus mapping information. This ioctl will *
* map outbound to the specified VMEbus address, *
* address modifiers and for the specified number *
* of bytes. The resultant io_handle_t of the *
* mapping is used by dmaex_strategy interface *
* when the transfer mode equals PIO_XFER_MODE. *
* PIO_XFER_MODE causes the dmaex_strategy *
* interface to perform the data transfer using *
* programmed I/O operations. VMEbus mapping *
* information is passed in the ioctl data *
* parameter. *
* data->data[0] = number of bytes to map *
* data->data[1] = VMEbus address to map *
* data->data[2] = VMEbus address modifiers *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: SETUP_VME_FOR_STRATEGY_PIO\n");
if (sc->vme_handle) {
printf("SETUP_VME_FOR_STRATEGY_PIO failed, a PIO mapping exists.\n");
ret_val = EBUSY;
break;
}
/*
* Perform the requested outbound mapping
*/
sc->vme_handle = vba_map_csr(ctlr,
(vme_addr_t)data->data[1],
(u_int)data->data[0],
(u_int)(data->data[2] & VME_BITS_MASK));
if (!(sc->vme_handle)) {
printf("SETUP_VME_FOR_STRATEGY_PIO: vba_map_csr interface failed\n");
printf(" pio_addr = 0x%x pio_size = 0x%x pio_am = 0x%x\n",
(vme_addr_t)data->data[1],
(u_int)data->data[0],
(vme_atype_t)(data->data[2] & VME_BITS_MASK));
ret_val = EINVAL;
break;
}
/*
* Save the PIO mapping information
*/
sc->vme_size = (unsigned int)data->data[0];
sc->vme_addr = (vme_addr_t)data->data[1];
sc->vme_am = (vme_atype_t)(data->data[2] & VME_BITS_MASK);
sc->vme_wrk_handle = (io_handle_t)0;
sc->vme_wrk_size = 0;
break;
case GET_VME_INFO_FOR_STRATEGY_PIO:
/***************************************************
* The GET_VME_INFO_FOR_STRATEGY_PIO ioctl returns *
* dmaex_stratgey PIO_XFER_MODE mapping *
* information. It returns the initial mapping *
* info specified to SETUP_VME_FOR_STRATEGY_PIO *
* interface and the results of the PIO transfer. *
* The data is returned through the data parameter *
* of the ioctl interface. *
* data->data[0] = number of bytes mapped *
* data->data[1] = VMEbus address mapped *
* data->data[2] = VMEbus address modifiers *
* data->data[3] = io_handle_t of mapped VMEbus *
* address. *
* data->data[4] = io_handle_t after transfer *
* data->data[5] = total bytes transferred *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: GET_VME_INFO_FOR_STRATEGY_PIO\n");
data->data[0] = (unsigned long)sc->vme_size;
data->data[1] = (unsigned long)sc->vme_addr;
data->data[2] = (unsigned long)sc->vme_am;
data->data[3] = (unsigned long)sc->vme_handle;
data->data[4] = (unsigned long)sc->vme_wrk_handle;
data->data[5] = (unsigned long)sc->vme_wrk_size;
break;
case UNMAP_VME_FOR_STRATEGY_PIO:
/***************************************************
* The UNMAP_VME_FOR_STRATEGY_PIO ioctl is used to *
* return VMEbus mapped resources allocated by *
* SETUP_VME_FOR_STRATEGY_PIO ioctl back to the *
* system. No data is passed or returned from this *
* interface via the data parameter. *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: UNMAP_VME_FOR_STRATEGY_PIO\n");
if (sc->vme_handle) {
/*
* Unmap the previously mapped VMEbus address
*/
vba_unmap_csr(ctlr,sc->vme_handle);
sc->vme_handle = (unsigned long)0;
sc->vme_addr = 0;
sc->vme_am = 0;
sc->vme_size = 0;
sc->vme_wrk_handle = (io_handle_t)0;
sc->vme_wrk_size = 0;
} else {
printf("dmaex_ioctl: UNMAP_VME_FOR_STRATEGY_PIO, no VME to unmap.\n");
ret_val = ENXIO;
}
break;
case MAP_SYS_MEM_TO_VME:
/***************************************************
* The MAP_SYS_MEM_TO_VME ioctl maps the user's *
* memory to the VMEbus for the number of bytes *
* specified. The user's memory is also wired down *
* so that it will not be swapped. Mapping infor- *
* mation is passed in the ioctl data parameter. *
* data->data[0] = number of bytes to be mapped *
* data->data[1] = VMEbus address - not used *
* data->data[2] = VMEbus address modifiers *
* data->data[3] = User virtual buffer address *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: MAP_SYS_MEM_TO_VME\n");
if (sc->sys_mem_dma_handle) {
printf("MAP_SYS_MEM_TO_VME failed, a system memory mapping exists.\n");
ret_val = EBUSY;
break;
}
if (!data->data[0] || !data->data[2] || !data->data[3]) {
printf("MAP_SYS_MEM_TO_VME failed, no bc, VME am or vir mem buf\n");
ret_val = EINVAL;
break;
}
/*
* Obtain the mapping information from the caller
*/
sc->sys_mem_dma_handle = (dma_handle_t)NULL;
sc->sys_mem_vme_size = (unsigned int)data->data[0];
sc->sys_mem_vme_am = (vme_atype_t)data->data[2];
sc->sys_mem_vir_addr = (caddr_t)data->data[3];
proc_p = task_to_proc(current_task());
/*
* Map system memory to the VMEbus
*/
if (dma_map_load ((unsigned long) sc->sys_mem_vme_size,
(vm_offset_t) sc->sys_mem_vir_addr,
proc_p,
ctlr,
&sc->sys_mem_dma_handle,
0,
sc->sys_mem_vme_am | DMA_GUARD_UPPER | DMA_ALL) == 0) {
printf("MAP_SYS_MEM_TO_VME failed, dma_map_load returned zero\n");
ret_val = EINVAL;
break;
}
/*
* Obtain the VMEbus address associated with the mapped system memory
*/
dmaex_sg = dma_get_curr_sgentry (sc->sys_mem_dma_handle);
sc->sys_mem_vme_addr = (vme_addr_t)dmaex_sg->ba;
sc->sys_mem_vme_size = (unsigned int)dmaex_sg->bc;
/*
* Wire the system memory down so that it will not be swapped out
*/
if (vm_map_pageable(current_task()->map,
trunc_page(sc->sys_mem_vir_addr),
round_page(sc->sys_mem_vir_addr +
sc->sys_mem_vme_size),
(VM_PROT_READ | VM_PROT_WRITE))) {
printf("MAP_SYS_MEM_TO_VME: vm_map_pageable failed\n");
dma_map_unload (DMA_DEALLOC, sc->sys_mem_dma_handle);
sc->sys_mem_dma_handle = (dma_handle_t)NULL;
sc->sys_mem_vme_addr = 0;
sc->sys_mem_vme_am = 0;
sc->sys_mem_vme_size = 0;
sc->sys_mem_vir_addr = (caddr_t)NULL;
ret_val = EIO;
break;
}
case GET_SYS_MEM_INFO:
/***************************************************
* The GET_SYS_MEM_INFO ioctl returns the results *
* of the MAP_SYS_MEM_TO_VME ioctl. In addition *
* to the parameters specified in the *
* MAP_SYS_MEM_TO_VME ioctl, the VMEbus address at *
* which the users memory is mapped is returned. *
* The data is returned in the ioctl data parameter*
* data->data[0] = number of bytes mapped *
* data->data[1] = VMEbus address of user memory *
* data->data[1] = VMEbus address modifiers *
* data->data[2] = User virtual buffer address *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: GET_SYS_MEM_INFO\n");
data->data[0] = (unsigned long)sc->sys_mem_vme_size;
data->data[1] = (unsigned long)sc->sys_mem_vme_addr;
data->data[2] = (unsigned long)sc->sys_mem_vme_am;
data->data[3] = (unsigned long)sc->sys_mem_vir_addr;
break;
case UNMAP_SYS_MEM_TO_VME:
/***************************************************
* The UNMAP_SYS_MEM_TO_VME ioctl releases VME *
* resources previously mapped by ioctl interface *
* MAP_SYS_MEM_TO_VME and unwires the users memory.*
* On return from this call the users memroy is no *
* longer on the VMEbus and the memory is alowed *
* to be swapped by the system. No information is *
* needed in the ioctl data parameter. *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: UNMAP_SYS_MEM_TO_VME\n");
if (sc->sys_mem_dma_handle) {
/*
* Unmap the previously mapped system memory to VMEbus mapping
*/
dma_map_unload (DMA_DEALLOC, sc->sys_mem_dma_handle);
if (vm_map_pageable(current_task()->map,
trunc_page(sc->sys_mem_vir_addr),
round_page(sc->sys_mem_vir_addr +
sc->sys_mem_vme_size),
(VM_PROT_NONE))) {
printf("UNMAP_SYS_MEM_TO_VME: vm_map_pageable failed\n");
ret_val = EIO;
}
sc->sys_mem_dma_handle = (dma_handle_t)NULL;
sc->sys_mem_vme_addr = 0;
sc->sys_mem_vme_am = 0;
sc->sys_mem_vme_size = 0;
sc->sys_mem_vir_addr = (caddr_t)NULL;
} else {
printf("dmaex_ioctl: UNMAP_SYS_MEM_TO_VME, no VME to unmap.\n");
ret_val = ENXIO;
}
break;
case SETUP_DMA_BLK_WRT:
/***************************************************
* The SETUP_DMA_BLK_WRT ioctl sets up the VME *
* adapters DMA engine for a DMA Block Mode Write *
* to the specified VMEbus address, address *
* modifiers and for the number of bytes specified.*
* If caller's memory buffer is not specified, *
* half of the physically contiguous memory buffer *
* allocated with cma_dd option and contig_malloc *
* will be used by this ioctl interface. This *
* kernel buffer can be mapped into user space by *
* setting the drivers mmap mapping mode to *
* MMAP_K_TO_U_MEM_WRT and the user calling the *
* mmap interface to obtain a users pointer to *
* this address. Mapping information is passed in *
* the ioctl data parameter. *
* data->data[0] = number of bytes to be mapped *
* data->data[1] = VMEbus adddres to be mapped *
* data->data[2] = VMEbus address modifiers *
* data->data[3] = User virtual buffer address *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: SETUP_DMA_BLK_WRT\n");
/*
* Check that a mapping size and address modifiers are specified and
* that a previous mapping request is not active.
*/
if ( (!(unsigned int)data->data[0]) ||
(!((unsigned int)data->data[2] & VME_BITS_MASK)) ) {
printf("dmaex_ioctl: SETUP_DMA_BLK_WRT, invalid parameters\n");
ret_val = EINVAL;
break;
}
if (sc->blk_wrt_dma_handle) {
printf("SETUP_DMA_BLK_WRT failed, a DMA mapping exists.\n");
ret_val = EBUSY;
break;
}
/*
* go perform the actual DMA block mode write setup
*/
ret_val = dmaex_setup_blk_mode(ctlr,sc,data,DMA_OUT);
break;
case GET_DMA_BLK_WRT:
DMAEX_DBG2("dmaex_ioctl: GET_DMA_BLK_WRT\n");
data->data[0] = (unsigned long)sc->blk_wrt_vme_size;
data->data[1] = (unsigned long)sc->blk_wrt_vme_addr;
data->data[2] = (unsigned long)sc->blk_wrt_vme_am;
data->data[3] = (unsigned long)sc->blk_wrt_dma_handle;
data->data[4] = (unsigned long)sc->blk_wrt_buf_ptr;
break;
case DO_DMA_BLK_WRT:
DMAEX_DBG2("dmaex_ioctl: DO_DMA_BLK_WRT\n");
if (!sc->blk_wrt_dma_handle) {
printf("DO_DMA_BLK_WRT failed, no DMA mapping exists.\n");
ret_val = EINVAL;
break;
}
/*
* The adapters DMA engine mapping has been setup to the VMEbus.
* If the caller has specified a user system address to map, then
* wire the users memory down so that it will not be swapped out
* during hardware DMA. If two different DMA buffers are used
* from user space, then wiring and unwiring the system memory
* needs to be done prior to and after the hardware DMA.
*/
if (sc->blk_wrt_proc_p) {
if (vm_map_pageable(current_task()->map,
trunc_page(sc->blk_wrt_buf_ptr),
round_page(sc->blk_wrt_buf_ptr +
sc->blk_wrt_vme_size),
(VM_PROT_READ | VM_PROT_WRITE))) {
printf("DO_DMA_BLK_WRT - vm_map_pageable failed wiring\n");
data->data[0] = (unsigned long)0;
ret_val = EIO;
break;
}
}
/*
* perform the actual hardware DMA
*/
data->data[0] = (unsigned long)vba_dma(ctlr,sc->blk_wrt_dma_handle);
/*
* If user memory was specified to SETUP_DMA_BLK_WRT ioctl,
* unwire the user's memory.
*/
if (sc->blk_wrt_proc_p) {
if (vm_map_pageable(current_task()->map,
trunc_page(sc->blk_wrt_buf_ptr),
round_page(sc->blk_wrt_buf_ptr +
sc->blk_wrt_vme_size),
(VM_PROT_NONE))) {
printf("DO_DMA_BLK_WRT: vm_map_pageable failed unwiring\n");
ret_val = EIO;
}
}
break;
case CLR_DMA_BLK_WRT:
/***************************************************
* The CLR_DMA_BLK_WRT ioctl releases Block Mode *
* resources previously mapped by ioctl interface *
* SETUP_DMA_BLK_WRT. No information is needed in *
* the ioctl data parameter. *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: CLR_DMA_BLK_WRT\n");
if (sc->blk_wrt_dma_handle) {
/*
* Unmap the previously mapped system memory to VMEbus mapping
*/
dma_map_unload (DMA_DEALLOC, sc->blk_wrt_dma_handle);
sc->blk_wrt_vme_addr = 0;
sc->blk_wrt_vme_am = 0;
sc->blk_wrt_vme_size = 0;
sc->blk_wrt_dma_handle = (dma_handle_t)NULL;
sc->blk_wrt_buf_ptr = (caddr_t)NULL;
sc->blk_wrt_proc_p = (struct proc *)NULL;
} else {
printf("dmaex_ioctl: CLR_DMA_BLK_WRT, no VME to unmap.\n");
ret_val = ENXIO;
}
break;
case SETUP_DMA_BLK_RD:
/***************************************************
* The SETUP_DMA_BLK_RD ioctl sets up the VME *
* adapters DMA engine for a DMA Block Mode Read *
* to the specified VMEbus address, address *
* modifiers and for the number of bytes specified.*
* If s caller's memory buffer is not specified, *
* half of the physically contiguous memory buffer *
* allocated with cma_dd option and contig_malloc *
* will be used by this ioctl interface. This *
* kernel buffer can be mapped into user space by *
* setting the drivers mmap mapping mode to *
* MMAP_K_TO_U_MEM_RD and the user calling the *
* mmap interface to obtain a users pointer to *
* this address. Mapping information is passed in *
* the ioctl data parameter. *
* data->data[0] = number of bytes to be mapped *
* data->data[1] = VMEbus adddres to be mapped *
* data->data[2] = VMEbus address modifiers *
* data->data[3] = User virtual buffer address *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: SETUP_DMA_BLK_RD\n");
/*
* Check that a mapping size and address modifiers are specified and
* that a previous mapping request is not active.
*/
if ( (!(unsigned int)data->data[0]) ||
(!((unsigned int)data->data[2] & VME_BITS_MASK)) ) {
printf("dmaex_ioctl: SETUP_DMA_BLK_RD, invalid parameters\n");
ret_val = EINVAL;
break;
}
if (sc->blk_rd_dma_handle) {
printf("SETUP_DMA_BLK_RD failed, a DMA mapping exists.\n");
ret_val = EBUSY;
break;
}
/*
* go perform the actual DMA block mode read setup
*/
ret_val = dmaex_setup_blk_mode(ctlr,sc,data,DMA_IN);
break;
case GET_DMA_BLK_RD:
DMAEX_DBG2("dmaex_ioctl: GET_DMA_BLK_RD\n");
data->data[0] = (unsigned long)sc->blk_rd_vme_size;
data->data[1] = (unsigned long)sc->blk_rd_vme_addr;
data->data[2] = (unsigned long)sc->blk_rd_vme_am;
data->data[3] = (unsigned long)sc->blk_rd_dma_handle;
data->data[4] = (unsigned long)sc->blk_rd_buf_ptr;
break;
case DO_DMA_BLK_RD:
DMAEX_DBG2("dmaex_ioctl: DO_DMA_BLK_RD\n");
if (!sc->blk_rd_dma_handle) {
printf("DO_DMA_BLK_RD failed, no DMA mapping exists.\n");
ret_val = EINVAL;
break;
}
/*
* The adapters DMA engine mapping has been setup to the VMEbus.
* If the caller has specified a user system address to map, then
* wire the users memory down so that it will not be swapped out
* during hardware DMA. If two different DMA buffers are used
* from user space, then wiring and unwiring the system memory
* needs to be done prior to and after the hardware DMA.
*/
if (sc->blk_rd_proc_p) {
if (vm_map_pageable(current_task()->map,
trunc_page(sc->blk_rd_buf_ptr),
round_page(sc->blk_rd_buf_ptr +
sc->blk_rd_vme_size),
(VM_PROT_READ | VM_PROT_WRITE))) {
printf("DO_DMA_BLK_RD - vm_map_pageable failed wiring\n");
data->data[0] = (unsigned long)0;
return(EIO);
}
}
/*
* perform the actual hardware DMA
*/
data->data[0] = (unsigned long)vba_dma(ctlr,sc->blk_rd_dma_handle);
/*
* If user memory was specified to SETUP_DMA_BLK_RD ioctl,
* unwire the memory.
*/
if (sc->blk_rd_proc_p) {
if (vm_map_pageable(current_task()->map,
trunc_page(sc->blk_rd_buf_ptr),
round_page(sc->blk_rd_buf_ptr +
sc->blk_rd_vme_size),
(VM_PROT_NONE))) {
printf("DO_DMA_BLK_RD: vm_map_pageable failed unwiring\n");
ret_val = EIO;
}
}
break;
case CLR_DMA_BLK_RD:
/***************************************************
* The CLR_DMA_BLK_RD ioctl releases Block Mode *
* resources previously mapped by ioctl interface *
* SETUP_DMA_BLK_RD. No information is needed in *
* the ioctl data parameter. *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: CLR_DMA_BLK_RD\n");
if (sc->blk_rd_dma_handle) {
/*
* Unmap the previously mapped system memory to VMEbus mapping
*/
dma_map_unload (DMA_DEALLOC, sc->blk_rd_dma_handle);
sc->blk_rd_vme_addr = 0;
sc->blk_rd_vme_am = 0;
sc->blk_rd_vme_size = 0;
sc->blk_rd_dma_handle = (dma_handle_t)NULL;
sc->blk_rd_buf_ptr = (caddr_t)NULL;
sc->blk_rd_proc_p = (struct proc *)NULL;
} else {
printf("dmaex_ioctl: CLR_DMA_BLK_RD, no VME to unmap.\n");
ret_val = ENXIO;
}
break;
case SET_INT_HANDLER:
/***************************************************
* The SET_INT_HANDLER ioctl installs an interrupt *
* service interface to receive VMEbus interrupts *
* at the specified VMEbus IRQ and VECTOR. The *
* interrupt service interface installed will use *
* Digital UNIX's psignal interface to wakeup the *
* user level application when a VMEbus interrupt *
* has been received on the specified vector and *
* VME interrupt request line. The VMEbus IRQ and *
* vector are passed in the ioctl data parameter. *
* data->data[0] = VMEbus IRQ *
* data->data[1] = VMEbus interrupt vector *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: SET_INT_HANDLER\n");
if (sc->rcvisr_id_t) {
printf("dmaex_ioctl: SET_INT_HANDLER handler previously installed\n");
ret_val = EBUSY;
} else {
/*
* check that a legal VMEbus IRQ was specified
*/
irq = (unsigned int)data->data[0];
vector = (unsigned int)data->data[1];
if ((irq < 1) || (irq > 7)) {
printf("dmaex_ioctl: SET_INT_HANDLER, invalid IRQ specified\n");
ret_val = EINVAL;
} else {
/*
* setup handler_add information for VME
*/
info.gen_intr_info.configuration_st = (caddr_t) ctlr;
info.gen_intr_info.intr = dmaex_rcv_int_srv;
info.gen_intr_info.param = (caddr_t) unit;
info.gen_intr_info.config_type = CONTROLLER_CONFIG_TYPE;
info.vec = vector;
info.irq = (int)irq;
handler.ih_bus = ctlr->bus_hd;
handler.ih_bus_info = (char *) &info;
/*
* install the interrupt handler
*/
sc->rcvisr_id_t = handler_add(&handler);
if (sc->rcvisr_id_t == (ihandler_id_t *)NULL) {
printf("dmaex_ioctl: SET_INT_HANDLER - error in handler_add\n");
ret_val = EIO;
} else {
if (handler_enable(sc->rcvisr_id_t) != 0) {
handler_del(sc->rcvisr_id_t);
sc->rcvisr_irq = 0;
sc->rcvisr_id_t = (ihandler_id_t *)NULL;
sc->rcvisr_proc_p = (struct proc *)NULL;
ret_val = EIO;
} else
/*
* dmaex_rcv_isr_srv interface needs the pointer to
* the proc structure in order to use interface
* psignal.
*/
sc->rcvisr_irq = info.irq;
sc->rcvisr_proc_p = (struct proc *)task_to_proc(current_task());
}
}
}
break;
case CLR_INT_HANDLER:
/***************************************************
* The CLR_INT_HANDLER ioctl will remove the *
* interrupt service interface installed with *
* ioctl SET_INT_HANDER. *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: CLR_INT_HANDLER\n");
if (sc->rcvisr_id_t) {
if (handler_disable(sc->rcvisr_id_t) != 0) {
printf("dmaex_ioctl: CLR_INT_HANDLER handler_disable failed\n");
ret_val = EIO;
} else {
if (handler_del(sc->rcvisr_id_t) != 0) {
printf("dmaex_ioctl: CLR_INT_HANDLER handler_del failed\n");
ret_val = EIO;
} else {
sc->rcvisr_irq = 0;
sc->rcvisr_id_t = (ihandler_id_t *)NULL;
sc->rcvisr_proc_p = (struct proc *)NULL;
}
}
}
break;
case VME_POST_IRQ:
/***************************************************
* The VME_POST_IRQ ioctl call will post a VME *
* interrupt request at the specified IRQ and *
* vector. An IACK service interface is provided *
* to the vba_post_irq interface. The VME bus *
* adapter code, upon detection of the VME IACK *
* interrupt, will dispatch to the appropriate *
* IACK service interface. The IACK service *
* interface provided in this example will signal *
* an EVENT indicating that the interrupt request *
* has been acknowledged. This ioctl interface *
* will wait on the EVENT with a 1 second timeout. *
* If the IACK interrupt was not recieved within *
* the timeout period, the VME interrupt request *
* will be dismissed with a call to the *
* vba_clear_irq interface. If the IACK EVENT was *
* signaled, then the ioctl interface will return *
* success. The VME interrupt request IRQ and *
* vector is provided to this interface in the *
* data parameter. *
* data->data[0] = VME IRQ level *
* data->data[1] = VME interrupt vector to *
* present upon acknowledgement *
* of the interrupt request *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: VME_POST_IRQ\n");
/*
* Optain the VME IRQ and vector from the ioctl interface
* data parameter. Check for IRQ validity before posting
* the requested IRQ and vector.
*/
irq = (unsigned int)data->data[0];
vector = (unsigned int)data->data[1];
if ((irq < 1) || (irq > 7)) {
printf("dmaex_ioctl: VME_POST_IRQ, invalid IRQ specified\n");
ret_val = EINVAL;
break;
}
/*
* Clear the event and post VMEbus Interrupt Request at
* the specified VMEbus IRQ and vector.
*/
event_clear((event_t *)&sc->post_iack_event[irq]);
ret_val = vba_post_irq(ctlr,irq,vector,dmaex_iack_isr);
switch (ret_val) {
case 0:
printf("dmaex_ioctl: Interrupt could not be posted\n");
ret_val = EBUSY;
break;
case 1:
/*
* The interrupt has been posted, wait up to one second
* for the IACK interrupt in response to the IRQ being
* acknowledged.
*/
ret_val = event_wait((event_t *)&sc->post_iack_event[irq],
TRUE,
1 * hz);
if (ret_val) {
printf("dmaex_ioctl: Failed to receive IACK interrupt\n");
if (vba_clear_irq(ctlr,irq,vector) == FALSE)
printf("Failed to clear posted IRQ\n");
ret_val = EIO;
}
event_clear((event_t *)&sc->post_iack_event[irq]);
break;
case -1:
printf("dmaex_ioctl: Interface not supported\n");
ret_val = ENOTSUP;
break;
default:
printf("dmaex_ioctl: Unexpected status returned\n");
ret_val = EIO;
break;
}
break;
case VME_CLR_IRQ:
/***************************************************
* The VME_CLR_IRQ ioctl will clear an interrupt *
* request previously specified by VME_POST_IRQ. *
* The VME IRQ and Vector is passed in the data *
* parameter of the ioctl interface. *
* data->data[0] = VME IRQ level to clear *
* data->data[1] = VME vector *
***************************************************/
DMAEX_DBG2("dmaex_ioctl: VME_CLR_IRQ\n");
/*
* Optain the VME IRQ and vector from the ioctl interface
* data parameter. Check for IRQ validity before clearing
* the requested IRQ and vector. If vector specification
* equals zero, unconditionally clear interrupt request at
* specified IRQ. If vector is non-zero, it must match the
* vector previously specified to VME_POST_IRQ at the
* specified IRQ.
*/
irq = (unsigned int)data->data[0];
vector = (unsigned int)data->data[1];
if ((irq < 1) || (irq > 7)) {
printf("dmaex_ioctl: VME_CLR_IRQ, invalid IRQ specified\n");
ret_val = EINVAL;
break;
}
if (vba_clear_irq(ctlr,irq,vector) == FALSE) {
printf("IRQ not cleared or invalid vector/irq specified\n");
ret_val = EIO;
}
break;
default:
ret_val = EINVAL;
break;
}
return(ret_val);
}
/***************************************************
* Read and Write Device Section *
* *
*--------------- dmaex_read ----------------------*
* *
* The dmaex_read interface calls the physio kernel*
* interface to perform the buffer lock, check the *
* buffer, and set up the I/O packet. The physio *
* interface calls the dmaex_strategy interface to *
* access the device. *
* *
* Depending on the drivers strategy transfer mode *
* (sc->strategy_xfer_mode), the read can be *
* performed by programmed I/O, block mode DMA *
* using the VME adapters DMA engine, or device *
* DMA using the VME device's DMA engine. *
***************************************************/
dmaex_read(dev_t dev, /* Major/minor device number */
struct uio *uio, /* Pointer to uio structure */
int flag) /* Access mode of device */
{
/***************************************************
* The following code: *
* *
* o Initializes a unit variable to the minor *
* device number *
* *
* o Determines if the minor device number is *
* greater than the maximum. If so, return *
* EIO to indicate an I/O error. *
* *
* o Otherwise, call physio to perform the *
* buffer lock, check the buffer, and set up *
* the I/O packet. *
***************************************************/
register int unit = minor(dev);
register struct dmaex_softc *sc = &dmaex_softc[unit];
register struct buf *bp = &sc->io_buf;
DMAEX_DBG1("dmaex_read: dmaex%d\n",unit);
if ((unit >= NDMAEX ) || (unit < 0))
return(EIO);
if (sc->strategy_xfer_mode == BLOCK_DMA_MODE) {
if (!(sc->dma_is_set)) {
printf("dmaex_read: ioctl:SET_STRATEGY_INFO_BLK_DMA not invoked\n");
return(EINVAL);
} else {
/*
* Copy starting VME address - this is needed
* to progress the VMEbus address on multiple
* iterations of the strategy interface
*/
sc->dma_wrk_vme_addr = sc->dma_vme_addr;
sc->dma_wrk_vme_size = 0;
}
} else {
if (sc->strategy_xfer_mode == PIO_XFER_MODE) {
if (!(sc->vme_handle)) {
printf("dmaex_read: ioctl:SETUP_VME_FOR_STRATEGY_PIO not invoked\n");
return(EINVAL);
} else {
/*
* Copy starting VME io_handle - this is needed
* to progress the VMEbus address on multiple
* iterations of the dmaex_strategy interface
*/
sc->vme_wrk_handle = sc->vme_handle;
sc->vme_wrk_size = 0;
}
}
}
bzero(bp,sizeof(struct buf));
return (physio(dmaex_strategy, bp, dev, B_READ, dmaex_minphys, uio));
}
/***************************************************
* *
*--------------- dmaex_write ---------------------*
* *
* The dmaex_write interface calls the physio *
* kernel interface to perform the buffer lock, *
* check the buffer, and set up the I/O packet. *
* The physio interface calls the dmaex_strategy *
* interface to access the device. *
* *
* Depending on the drivers strategy transfer mode *
* (sc->strategy_xfer_mode), the write can be *
* performed by programmed I/O, block mode DMA *
* using the VME adapters DMA engine, or device *
* DMA using the VME device's DMA engine. *
***************************************************/
dmaex_write(dev_t dev, /* Major/minor device number */
struct uio *uio, /* Pointer to uio structure */
int flag) /* Access mode of device */
{
/***************************************************
* The following code: *
* *
* o Initializes a unit variable to the minor *
* device number *
* *
* o Determines if the minor device number is *
* greater than the maximum. If so, return *
* EIO to indicate an I/O error. *
* *
* o Otherwise, call physio to perform the *
* buffer lock, check the buffer, and set up *
* the I/O packet. *
***************************************************/
register int unit = minor(dev);
register struct dmaex_softc *sc = &dmaex_softc[unit];
register struct buf *bp = &sc->io_buf;
DMAEX_DBG1("dmaex_write: dmaex%d\n",unit);
if ((unit >= NDMAEX) || (unit < 0))
return (EIO);
if (sc->strategy_xfer_mode == BLOCK_DMA_MODE) {
if (!(sc->dma_is_set)) {
printf("dmaex_write: ioctl:SET_STRATEGY_INFO_BLK_DMA not invoked\n");
return(EINVAL);
} else {
/*
* Copy starting VME address - this is needed
* to progress the VMEbus address on multiple
* iterations of the strategy interface
*/
sc->dma_wrk_vme_addr = sc->dma_vme_addr;
sc->dma_wrk_vme_size = 0;
}
} else {
if (sc->strategy_xfer_mode == PIO_XFER_MODE) {
if (!(sc->vme_handle)) {
printf("dmaex_write: ioctl:SETUP_VME_FOR_STRATEGY_PIO not invoked\n");
return(EINVAL);
} else {
/*
* Copy starting VME io_handle - this is needed
* to progress the VMEbus address on multiple
* iterations of the dmaex_strategy interface
*/
sc->vme_wrk_handle = sc->vme_handle;
sc->vme_wrk_size = 0;
}
}
}
bzero(bp,sizeof(struct buf));
return (physio(dmaex_strategy, bp, dev, B_WRITE, dmaex_minphys, uio));
}
/***************************************************
* *
*--------------- dmaex_minphys -------------------*
* *
* This interface is specified as a parameter to *
* the physio interface invoked from dmaex_read or *
* dmaex_write. Physio, will call this interface *
* once or several times depending on the *
* requested transfer size. It is called prior to *
* each invokation of dmaex_strategy from physio. *
* *
* This interface is used to limit the size of the *
* transfer. This may be a software limit to *
* throttle the transfer or a hardware limit *
* imposed by hardware. If the requested byte *
* count from physio exceeds the maximum byte *
* count specified (dmaex_max_dma), dmaex_max_dma *
* will be returned to the physio interface. The *
* physio interface will then call the strategy *
* interface with this byte count to perform the *
* transfer. *
***************************************************/
dmaex_minphys (register struct buf *bp)
{
DMAEX_DBG1("dmaex_minphys: entered\n");
if (bp->b_bcount > dmaex_max_dma)
bp->b_bcount = dmaex_max_dma;
return;
}
/***************************************************
* Strategy Section *
* *
*--------------- dmaex_strategy ------------------*
* *
* The dmaex_strategy interface is called by the *
* phsyio kernel interface. (The physio interface *
* is called by the dmaex_read and dmaex_write *
* driver interfaces.) *
* *
* Depending on the strategy transfer mode *
* (sc->strategy_xfer_mode), the transfer can be *
* performed by programmed I/O, by the VME *
* adapters DMA engine for block mode ransfers, or *
* by the VME devices DMA engine. *
* *
* The maximum size of the transfer is controlled *
* the dmaex_minphys interface. Dmaex_minphys is *
* called by physio prior to the invokation of *
* this interface by physio. *
***************************************************/
dmaex_strategy(struct buf *bp) /* Pointer to buf structure */
{
/***************************************************
* The following code initializes: *
* *
* o A unit variable to the minor device number *
* *
* o A controller structure to its associated *
* DMAEX device *
* *
* o A dmaex_softc structure to the address of *
* the dmaex_softc associated with this *
* DMAEX device *
* *
* o A variable to store read, write, and enable *
* interrupts status information. *
* *
* o A sg_entry structure pointer that the code *
* uses in calls to dma_get_curr_sqentry and *
* write_io_port. *
***************************************************/
register int unit = minor(bp->b_dev);
register struct controller *ctlr = dmaex_ctlr[unit];
register struct dmaex_softc *sc = &dmaex_softc[unit];
sg_entry_t dmaex_sg;
unsigned long b_count;
register int *mem_ptr;
int flags,i,j;
char csr;
DMAEX_DBG1("dmaex_strategy: dmaex%d\n",unit);
/***************************************************
* Check the validity of the unit number and the *
* pointers. *
***************************************************/
if ( (!ctlr) || (!sc) )
return (EINVAL);
sc->dma_handle = (dma_handle_t) NULL;
/***************************************************
* Dispatch to the appropriate I/O transfer *
* PIO_XFER_MODE: perform PIO operations *
* DEVICE_DMA_MODE: perform device DMA *
* BLOCK_MODE_DMA: perform Master BLOCK DMA *
***************************************************/
switch (sc->strategy_xfer_mode) {
case PIO_XFER_MODE:
if (bp->b_flags & B_READ) {
if (sc->vme_am & VME_DENSE) {
/*
* Mapped to VMEbus through dense space. Use read_io_port
* interface to read the data.
*/
mem_ptr = (int *)bp->b_un.b_addr;
for (i=0,j=0; i < bp->b_bcount/sizeof(int); i++, j+=sizeof(int))
*mem_ptr++ = (int)read_io_port(sc->vme_wrk_handle + j,4,0);
} else {
/*
* Use the system io copy function to read data from
* the VMEbus into users mapped memory
*/
if (io_copyin(sc->vme_wrk_handle, (vm_offset_t)bp->b_un.b_addr,
(unsigned long)bp->b_bcount) == -1) {
/*
* io_copyin indicates an error condition
*/
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
iodone(bp);
break;
}
}
} else {
if (sc->vme_am & VME_DENSE) {
/*
* Mapped to VMEbus through dense space. Use write_io_port
* interface to write the data.
*/
mem_ptr = (int *)bp->b_un.b_addr;
for (i=0,j=0; i < bp->b_bcount/sizeof(int); i++, j+=sizeof(int))
write_io_port(sc->vme_wrk_handle + j,4,0,(long)(*mem_ptr++));
mb();
} else {
/*
* Use the system io copy function to write data from
* users mapped memory to the VMEbus
*/
if (io_copyout((vm_offset_t)bp->b_un.b_addr, sc->vme_wrk_handle,
(unsigned long)bp->b_bcount) == -1) {
/*
* io_copyout indicated an error condition
*/
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
iodone(bp);
break;
}
}
}
/***************************************************
* The transfer has completed successfully. Update *
* the buffers residual count. Update the *
* io_handle and total number of bytes transferred.*
* It is possible for physio to segment the *
* transfer if the transfer size exceeds the value *
* specified in the dmaex_minphys interface. *
***************************************************/
bp->b_resid = 0;
sc->vme_wrk_handle += (unsigned long)bp->b_bcount;
sc->vme_wrk_size += (unsigned int)bp->b_bcount;
/***************************************************
* return to physio indicating I/O is complete *
***************************************************/
iodone(bp);
break;
case DEVICE_DMA_MODE:
/***************************************************
* The following code sets up the DMA mapping *
* registers by calling dma_map_load interface. *
* Dma_map_load invokes dma_map_alloc interface *
* as a result of sc->dma_handle being a NULL. *
* *
* If the return from dma_map_alloc or *
* dma_map_load is zero (0), it could not perform *
* the requested operation. The dmaex_strategy *
* interface: *
* *
* o Sets the error bit to EOI *
* *
* o Sets b_flags to the bitwise inclusive OR of *
* the read and error bits *
* *
* o Completes the I/O operation by calling *
* iodone *
* *
* Return to calling routine (either dmaex_read or *
* dmaex_write. *
***************************************************/
if (dma_map_load ((unsigned long) bp->b_bcount,
(vm_offset_t) bp->b_un.b_addr,
bp->b_proc,
ctlr,
&sc->dma_handle,
0,
VME_A24_UDATA_D32 | VME_BS_NOSWAP |
DMA_GUARD_UPPER | DMA_ALL) == 0) {
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
iodone(bp);
break;
}
/***************************************************
* If the requested mapping was performed, set up *
* the device for transfer. Obtain the VMEbus *
* address and byte count of user memory mapped *
* to the VMEbus. *
***************************************************/
dmaex_sg = dma_get_curr_sgentry (sc->dma_handle);
/***************************************************
* If the dmaex_no_dev flag indicates that the *
* 'dmaex' device is present, start the device DMA *
***************************************************/
if (!dmaex_no_dev) {
write_io_port (sc->csr_handle + DMAEX_COUNT_OFF,
DMAEX_COUNT_SIZE, 0, dmaex_sg->bc);
mb();
write_io_port (sc->csr_handle + DMAEX_ADDR_OFF,
DMAEX_ADDR_SIZE, 0, (long)dmaex_sg->ba);
mb();
csr = (bp->b_flags & B_READ) ? (READ | IE) : IE;
write_io_port (sc->csr_handle + DMAEX_CSR_OFF,
DMAEX_CSR_SIZE, 0, (csr | DMA_GO));
mb();
}
/***************************************************
* Wait for a timeout or a dma complete interrupt. *
* dmaex_int_wait specifies how many seconds to *
* wait for an interrupt to occur. *
***************************************************/
if (event_wait((event_t *)&sc->isi_event,TRUE,dmaex_int_wait * hz)) {
DMAEX_DBG1("dmaex_strategy: dmaex%d timed out after %d seconds\n",
unit,dmaex_int_wait);
bp->b_error = ETIMEDOUT;
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
}
event_clear((event_t *)&sc->isi_event);
/***************************************************
* Resources freed here versus in interrupt *
* service interface for performance reasons *
***************************************************/
dma_map_unload (DMA_DEALLOC, sc->dma_handle);
sc->dma_handle = (dma_handle_t) NULL;
/***************************************************
* return to physio indicating I/O is complete *
***************************************************/
iodone(bp);
break;
case BLOCK_DMA_MODE:
/***************************************************
* Add DMA specific flags to the VME address *
* modifier that was specified in ioctl: *
* SET_STRATEGY_INFO_BLK_DMA interface. Also set *
* the DMA_IN or DMA_OUT flag depending on whether *
* the I/O operation is a read or write. The flags *
* DMA_IN or DMA_OUT, indicate to the VME adapter *
* code that the VME adapter's DMA engine is to be *
* used to perform the DMA transfer. *
***************************************************/
flags = sc->dma_vme_am | DMA_SLEEP | DMA_GUARD_UPPER | DMA_ALL;
if (bp->b_flags & B_READ)
flags |= DMA_IN;
else
flags |= DMA_OUT;
/***************************************************
* When using the DMA engine, the VMEbus address *
* and flags MUST be specified via the interface *
* vba_set_dma_addr. *
***************************************************/
flags = vba_set_dma_addr(ctlr, flags, sc->dma_wrk_vme_addr);
/***************************************************
* Allocate, load and set system resources for the *
* DMA data transfers. Upon success, dma_map_load *
* returns the number of bytes mapped and a *
* dma_handle to sc->dma_handle. *
***************************************************/
b_count = dma_map_load ((unsigned long) bp->b_bcount,
(vm_offset_t) bp->b_un.b_addr,
bp->b_proc,
ctlr,
&sc->dma_handle,
0,
flags);
if ( (!b_count) || (!sc->dma_handle) ||
(b_count != (unsigned long)bp->b_bcount)) {
printf("dmaex_strategy: dma_map_load error\n");
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
iodone(bp);
break;
}
/***************************************************
* Obtain VMEbus address and byte count of this *
* mapped transfer. The VMEbus address should *
* match that of sc->dma_wrk_vme_addr *
***************************************************/
dmaex_sg = dma_get_curr_sgentry (sc->dma_handle);
DMAEX_DBG2("dmaex_strategy: sg.ba = 0x%lx sg.bc 0x%lx\n",
dmaex_sg->ba, dmaex_sg->bc);
/***************************************************
* Perform the actual block mode DMA by invoking *
* the vba_dma interface. This interface will *
* block until the DMA has completed or an error *
* occured. The return value from the interface *
* is the actual number of bytes transferred. This *
* value should be the same as value bp->b_bcount. *
* Specify the dma_handle returned from interface *
* dma_map_load in this interface call. *
***************************************************/
b_count = vba_dma(ctlr, sc->dma_handle);
if ((!b_count) || (b_count == -1) ||
(b_count != (unsigned long)bp->b_bcount)) {
printf("dmaex_strategy: error during DMA transfer\n");
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
/***************************************************
* Release the DMA system resources *
***************************************************/
dma_map_unload(DMA_DEALLOC,sc->dma_handle);
sc->dma_handle = (dma_handle_t) NULL;
iodone(bp);
break;
}
/***************************************************
* The transfer has completed successfully. Update *
* the buffers residual count. Update the VMEbus *
* address and total number of bytes transferred. *
* It is possible for physio to segment the DMA *
* transfer if the transfer size exceeds the value *
* specified in the dmaex_minphys interface. *
***************************************************/
bp->b_resid = 0;
sc->dma_wrk_vme_addr += (vme_addr_t)b_count;
sc->dma_wrk_vme_size += (unsigned int)b_count;
/***************************************************
* Release the DMA system resources *
***************************************************/
dma_map_unload(DMA_DEALLOC,sc->dma_handle);
sc->dma_handle = (dma_handle_t) NULL;
iodone(bp);
break;
} /* end of strategy_xfer_mode */
return;
}
/***************************************************
* Interrupt Section *
* *
*--------------- dmaex_intr1 ---------------------*
* *
* The dmaex_intr1 checks the results of the DMA *
* transfer and then post an event to wakeup the *
* dmaex_strategy interface to complete the I/O *
* operation.
* *
***************************************************/
dmaex_intr1(int unit) /* Logical unit number for device */
{
/***************************************************
* The following code initializes: *
* *
* o A controller structure to its associated *
* DMAEX device *
* *
* o A dmaex_softc structure to the address of *
* the dmaex_softc structure associated with *
* this DMAEX device *
* *
* The code also declares a variable to store the *
* return from vme_read_byte and a pointer to a *
* buf structure. *
* *
***************************************************/
register struct controller *ctlr = dmaex_ctlr[unit];
register struct dmaex_softc *sc = &dmaex_softc[unit];
register struct buf *bp = &sc->io_buf;
char csr;
DMAEX_DBG2("dmaex_intr1: dmaex%d\n",unit);
/***************************************************
* *
* Call read_io_port to read a byte from the *
* VMEbus. If the error bit is set, an error *
* occurred. The code does the following: *
* *
* o Sets the error bit in b_error *
* *
* o Sets b_flags to the bitwise inclusive OR *
* of the read and error bits to indicate *
* an error occurred on this buffer *
* *
***************************************************/
if (!dmaex_no_dev) {
/***************************************************
* The software flag indicates that a 'dmaex' *
* device is present, read the device csr register *
***************************************************/
csr = read_io_port (sc->csr_handle + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0);
if (csr & ERROR) {
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
}
}
/***************************************************
* *
* The following code: *
* *
* o Records the number of bytes remaining in *
* b_resid *
* *
* o Call wakeup returning to strategy() *
* *
***************************************************/
if (dmaex_no_dev)
/***************************************************
* The software flag indicates that the 'dmaex' *
* device is not present. Indicate success by *
* clearing the number of bytes remaining *
***************************************************/
bp->b_resid = 0;
else
bp->b_resid = read_io_port(sc->csr_handle + DMAEX_COUNT_OFF,
DMAEX_COUNT_SIZE, 0);
/***************************************************
* Interrupt service interfaces executing at *
* SPLDEVRT spl(6) must not call kernel interfaces *
* directly. The rt_post_callout function allows *
* the calling process to defer execution of a *
* function until a time when kernel interface *
* routines can be invoked. The function invoked *
* by rt_post_callout runs at an elevated spl and *
* is subject to the same restrictions as an *
* interrupt service interface. *
***************************************************/
rt_post_callout(event_post, (long)&sc->isi_event, (long)NULL);
/***************************************************
* If the driver writer knows that VME interrupts *
* are NOT dispatched at SPLDEVRT. Then the *
* following line of code can be substituted for *
* the previous line of code. *
* *
* event_post( (event_t *)&sc->isi_event); *
* *
***************************************************/
}
/***************************************************
* Interrupt Section *
* *
*--------------- dmaex_intr2 ---------------------*
* *
* Handle other interrupts from dmaex controller. *
* This, and associated code in dmaex_probe() are *
* included only for the purpose of illustrating *
* how multiple interrupts and there handlers are *
* registered. This has no functional meaning in *
* this example. *
* *
***************************************************/
dmaex_intr2(int unit) /* Logical unit number for device */
{
register struct dmaex_softc *sc = &dmaex_softc[unit];
register struct buf *bp = &sc->io_buf;
DMAEX_DBG2("dmaex_intr2: dmaex%d\n",unit);
bp->b_error = EIO;
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
/***************************************************
* Interrupt service interfaces executing at *
* SPLDEVRT spl(6) must not call kernel interfaces *
* directly. The rt_post_callout function allows *
* the calling process to defer execution of a *
* function until a time when kernel interface *
* routines can be invoked. The function invoked *
* by rt_post_callout runs at an elevated spl and *
* is subject to the same restrictions as an *
* interrupt service interface. *
***************************************************/
rt_post_callout(event_post, (long)&sc->isi_event, (long)NULL);
/***************************************************
* If the driver writer knows that VME interrupts *
* are not dispatched at SPLDEVRT. Then the *
* following line of code can be substituted for *
* the previous line of code. *
* *
* event_post( (event_t *)&sc->isi_event); *
* *
***************************************************/
}
/***************************************************
* Interrupt Section *
* *
*------------ dmaex_iack_isr ---------------------*
* *
* This interrupt service interface is dispatched *
* to by the VME adapter's IACK interrupt service *
* interface as a result of a VME IACK interrupt. *
* The VME IACK interrupt occurs in repsonse to a *
* VME posted interrupt request from ioctl *
* VME_POST_IRQ. The interface will post an event *
* to the ioctl interface to indicate that the *
* posted interrupt has been acknowledged. *
***************************************************/
int
dmaex_iack_isr(struct controller *ctlr, unsigned int irq)
{
register struct dmaex_softc *sc = &dmaex_softc[ctlr->ctlr_num];
DMAEX_DBG1("dmaex_iack_isr: dmaex%d\n",ctlr->ctlr_num);
event_post( (event_t *)&sc->post_iack_event[irq]);
}
/***************************************************
* Interrupt Section *
* *
*---------- dmaex_post_psignal -------------------*
* *
* This interface is invoked by rt_post_callout *
* interface if the VME interrupt was received at *
* SPLDEVRT. The interface issues a psignal to *
* SIGUSR1 to wakeup a user task. *
* *
***************************************************/
int
dmaex_post_psignal(struct proc *proc_p,long signal)
{
psignal(proc_p,signal);
}
/***************************************************
* Interrupt Section *
* *
*------------ dmaex_rcv_int_isr ------------------*
* *
* This interface is dispatched to when a VMEbus *
* interrupt has been received at the VMEbus *
* vector and VMEbus interrupt request specified *
* by SET_INT_HANDLER ioctl. This interface will *
* either wakeup a user task by issuing a *
* psignal to SIGUSR1 or invoke a function to *
* issue the psignal. This is done with interface *
* rt_post_callout when the interrupt service *
* interface is at system SPL of SPLDEVRT. *
* *
***************************************************/
int
dmaex_rcv_int_srv(int unit)
{
register struct dmaex_softc *sc = &dmaex_softc[unit];
/*
* On several VME systems, interrupts that are received
* on VMEbus interrupt requesl level 7 are dispatched to
* the interrupt service interface at SPLDEVRT spl(6).
* In these cases, signaling a kernel or user thread must
* be accomplished by using rt_post_callout interface.
*/
if (sc->rcvisr_irq == 7)
rt_post_callout(dmaex_post_psignal,(long)sc->rcvisr_proc_p,(long)SIGUSR1);
else
psignal(sc->rcvisr_proc_p,SIGUSR1);
}