/* * ***************************************************************** * * * * * Copyright Compaq Computer Corporation, 2000 * * * * * * The software contained on this media is proprietary to * * * and embodies the confidential technology of Compaq * * * Computer Corporation. Possession, use, duplication or * * * dissemination of the software and media is authorized only * * * pursuant to a valid written license from Compaq Computer * * * Corporation. * * * * * * RESTRICTED RIGHTS LEGEND Use, duplication, or disclosure * * * by the U.S. Government is subject to restrictions as set * * * forth in Subparagraph (c)(1)(ii) of DFARS 252.227-7013, * * * or in FAR 52.227-19, as applicable. * * * * * ***************************************************************** */ /* * HISTORY * ANSI C and general driver cleanup. * * Added dmaex_sw_byte_swap and dmaex_univ_adapter * variables to the dmaex subsystem attributes. * * Changed CFG_OP_CONFIGURE code in dmaex_configure. * * Changed register_configuration routine to clear * controller_config and device_config structures. * Changed code to initialize the revision levels of * these structrues. * * Added calls to the new vba_badaddr and vba_get_info * routines. * * Changed dmaex_open routine to check ctlr->alive * element for ALV_ALIVE only and not equal to. * * Added code to support HANDLE_LINEAR_SPACE flag in * the dmaex_mmap routine (mmap_mode = MMAP_VME_TO_U_MEM). * Changed code for mmap_modes of MMAP_K_TO_U_MEM_WRT and * MMAP_K_TO_U_MEM_RD to obtain a physical memory address * versus virtual. Changed the calculation for obtaining * the kernel page frame number (kpfnum). * * Added ioctl support for GET_HW_BYTE_SWAP_INFO and * GET_VBA_BUS_TYPE_INFO commands. */ #pragma ident "@(#)$RCSfile: dmaex.c,v $ $Revision: 1.1.2.5 $ (DEC) $Date: 1997/04/16 15:36:02 $" /**************************************************** * Compaq Computer Corporation supplies this * * software example on an "as-is" basis for general * * customer use. Note that COMPAQ does not offer * * any support for it, nor is it covered under any * * of COMPAQ's support contracts. * ****************************************************/ /**************************************************** * 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 provide information to the * * autoconfiguration software. This information * * is used by the autoconfiguration 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 routine. * * * ********************** 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 the * * dmaex_probe, dmaex_close, dmaex_strategy and * * dmaex_intr1 driver routines. * * * ****************************************************/ /**************************************************** * The following list highlights some features of * * the /dev/dmaex driver: * * * ********************** NOTE ************************ * * * Refer to the DDK manual, Writing Device Drivers, * * 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 Tru64 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 a Tru64 UNIX * * kernel (/vmunix) * * * * - Statically configured into a sysconfigtab * * database image when a user boots a Tru64 * * 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 Tru64 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_probe * * o dmaex_cattach * * o dmaex_ctlr_unattach * * o dmaex_open * * o dmaex_close * * o dmaex_mmap * * o dmaex_setup_blk_mode * * 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_psignal * * o dmaex_rcv_int_srv * ****************************************************/ /**************************************************** * Include Files Section * ****************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /**************************************************** * If you are writing a device driver to operate on * * the VMEbus, you must include the following * * bus-specific header file: * * * * #include * * * * 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 * * #include * ****************************************************/ #include #include #include /* VMEbus definitions */ #include #include "dmaexreg.h" /* Device register header file */ /**************************************************** * The /dev/dmaex driver's register_major_number * * routine 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 by 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 * ****************************************************/ int dmaex_configure(cfg_op_t, cfg_attr_t *, size_t, cfg_attr_t *, size_t); static int register_configuration(void); static int register_major_number(void); static void callback_register_configuration(int, int, ulong, ulong); static void callback_register_major_number(int, int, ulong, ulong); static int dmaex_probe(io_handle_t, struct controller *); static int dmaex_cattach(struct controller *); static int dmaex_ctlr_unattach(struct bus *, struct controller *); static int dmaex_open(dev_t, int, int); static int dmaex_close(dev_t, int, int); static int dmaex_mmap(dev_t, off_t, int); static int dmaex_ioctl(dev_t, int, struct dmaex_ioctl_data *, int); static int dmaex_read(dev_t, struct uio *, int); static int dmaex_write(dev_t, struct uio *, int); static int dmaex_minphys(struct buf *); static int dmaex_strategy(struct buf *); static int dmaex_intr1(int); static int dmaex_intr2(int); static int dmaex_iack_isr(struct controller *, unsigned int); static int dmaex_post_psignal(struct proc *, long); static int dmaex_rcv_int_srv(int); /**************************************************** * * * Declare an array of controller structure * * pointers. The value, MAX_NDMAEX, is 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 autoconfiguration * * 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 address 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 routines * * to access the device. The io_handle_t for Csr1 * * will be passed to the driver's probe routine * * 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 a softc structure to allow the /dev/dmaex* * device driver routines to share data. * ****************************************************/ struct dmaex_softc { int sc_open; /* DMAEXOPEN, DMAEXCLOSE */ io_handle_t csr_handle; /* Device CSR I/O handle */ event_t isr_event; /* Used by device ISR 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; /* The 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 ISR */ struct proc *rcvisr_proc_p; /* ISR user task process pointer */ int rcvisr_irq; /* VMEbus irq for recive ISR */ struct buf io_buf; /* Read/write I/O request buffer */ } dmaex_softc[MAX_NDMAEX]; /***************************************************** * Forward declaration of a driver routine. * * Because of a parameter dependency, this routine * * needed to be declared after struct dmaex_softc. * *****************************************************/ static int dmaex_setup_blk_mode(struct controller *, struct dmaex_softc *, struct dmaex_ioctl_data *, int); int dmaex_config = FALSE; /* State flag indicating driver configured */ int dmaex_devno = NO_DEV; /* No major number assigned yet */ /**************************************************** * The handler_add routine 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 * * a 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 routine * * 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 * * routine 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 * * and the bus-specific books for information 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 routine, which * * registers the I/O service routines 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 routine, which the /dev/dmaex * * driver's dmaex_configure routine 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_univ_adapter = 0; static int dmaex_sw_byte_swap = 0; 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] = ""; /**************************************************** * Set up 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 * * 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 routine. * * * * 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_Sw_Byte_Swap", CFG_ATTR_INTTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY | CFG_OP_RECONFIGURE, (caddr_t)&dmaex_sw_byte_swap,0,1,0}, {"Dmaex_Univ_Adapter", CFG_ATTR_INTTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (caddr_t)&dmaex_univ_adapter,0,1,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 routine 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 routine before * * it (the single binary module) can be registered * * as part of the cfgmgr framework and the Tru64 * * UNIX kernel. * * * * The dmaex_configure cooperates with the cfgmgr * * framework to complete configure, unconfigure, * * query, and other requests. These requests are * * differentiated by the "op" arguments. * * * * Specifically, the dmaex_configure routine 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 routine 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; /*************************************************** * The cfg_attr_t list passed into the * * dmaex_configure routine's indata argument * * contains the strings that are stored in the * * sysconfigtab database for this subsystem (the * * /dev/dmaex driver). * ***************************************************/ 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 * ***************************************************/ for( i = 0; i < indatalen; i++) switch(indata[i].type) { case CFG_ATTR_STRTYPE: break; default: switch(indata[i].status) { case CFG_FRAME_SUCCESS: break; default: printf("%s:",indata[i].name); switch(indata[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", indata[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 routine. * *****************************************************/ 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 the 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((caddr_t)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((caddr_t)mcfgname,"dmaex"); } else { /* * mcfgname from sysconfigtab is used to configure * the device driver in the following calls to the * configure_driver routine: */ } if (cfgmgr_get_state((caddr_t)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 successfully configure into a kernel. The callbacks * are responsible for determining the status of a subsystem's * 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 * * the 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 a * * 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 routine results * * in a call to the dmaex_ctlr_unattach * * routine 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 * * succeed only 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 routine has * * completed successfully. Return a success * * status. * ***************************************************/ return(ESUCCESS); } /*************************************************** * register_configuration() * *************************************************** * Name: register_configuration * * * * Arguments: None * * * * Kernel support routine 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 routine registered with the * * cfgmgr to be called at a specific * * point of the configuration process. * * * * Return Codes: * * * * Failure: ENOMEM * * * * Description: * * * * The register_configuration routine is * * responsible for registering the controller and * * device information for the /dev/dmaex driver. * * The dynamically configured /dev/dmaex driver * * directly calls the register_configuration * * routine. The statically configured /dev/dmaex * * driver calls register_configuration through a * * register callback called callback_register_ * * configuration. (The dmaex_configure routine * * calls the register_callback routine to * * register the register_configuration routine * * so that it can be called at some later time. * * * * Writing a routine similar to * * register_configuration is the method for * * specifying the controller and device information* * associated with a device driver. * * * * The register_configuration routine 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 * * routine 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"); /* * Initialize controller_config and device_config structures * created on the stack. The size of these structures have * changed in Tru64 UNIX V5.X releases, thus, uninitialized * data could exist. */ bzero((char *)ctlr_register_ptr, sizeof(struct controller_config)); bzero((char *)device_register_ptr, sizeof(struct device_config)); /*************************************************** * Register a controller structure for the * * /dev/dmaex driver. The system manager can * * specify attribute fields for the dmaex: entry * * in the /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 the MAX_NDMAEX value defined by the * * driver. * ***************************************************/ for (i = 0; i < NDMAEX; i++) { /**************************************************** * The controller structure revision value must * * match the value defined for CTLR_CONFIG_REVISION * * in your system's io/common/devdriver.h; for * * example, CTLR_CONFIG_REV_V1 (V5.0) or * * CTLR_CONFIG_REV_V0 (V4.0G). To avoid a potential * * future version mismatch, do not specifiy * * CTLR_CONFIG_REVISION as the revision value. * ****************************************************/ ctlr_register_ptr->revision = CTLR_CONFIG_REV_V1; strcpy(ctlr_register_ptr->subsystem_name, (caddr_t)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. * ***************************************************/ /**************************************************** * The device structure revision value must match * * the value defined for DEVICE_CONFIG_REVISION in * * your system's io/common/devdriver.h; for * * example, DEVICE_CONFIG_REV_V1 (V5.0) or * * DEVICE_CONFIG_REV_V0 (V4.0G). To avoid a * * potential future version mismatch, do not * * specifiy DEVICE_CONFIG_REVISION as the revision * * value. * ****************************************************/ /* device_register_ptr->revision = DEVICE_CONFIG_REV_V1; 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 boundary. This buffer will be * * divided into half. The first half will be used * * to perform DMA block writes with the VME * * adapter's DMA engine. The second half will be * * used to perform DMA block mode reads with the * * VME adapter's DMA engine. Dynamically loaded * * drivers specify the size and alignment of the * * physically contiguous buffer with the * * sysconfigtab file fragment's CMA_Option entry. * * The kernel routine, 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 by invoking the contig_malloc * * kernel routine. * ***************************************************/ 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 routine calls: * * * * o devsw_add * * * * Called by: * * * * o cfgmgr framework * * * * Return Codes: * * * * Success: ESUCCESS * * * * Failure: ENODEV * * * * Description: * * * * The register_major_number routine registers * * a driver's I/O service routines (and other * * information) and receives a major number. It * * accomplishes these tasks by calling the * * devsw_add routine. * ***************************************************/ 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 from 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 the devsw_add routine to register the * * driver's I/O service routines (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); /*************************************************** * The 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 routine 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 been 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 routine calls: * * * * o cfgmgr_set_status * * o register_configuration * * * * Called by: * * * * o cfgmgr framework called through a call * * to the register_callback routine * * * * 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 from the callback without doing any * * configuration work. * ************************************************/ if(dmaexcallback_return_status != ESUCCESS) { printf ("callback_register_configuration: dmaexcallback_return_status error\n"); return; } /************************************************ * Register this driver's 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 the * * cfgmgr_set_status routine is that the * * configure routine 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_configiguration: register_configuration error\n"); cfgmgr_set_status((caddr_t)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 routine 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 from the callback without doing any * * configuration work. * *************************************************/ if(dmaexcallback_return_status != ESUCCESS) { printf ("callback_register_major_number: dmaexcallback_return_status error\n"); return; } /************************************************* * Call the driver-specific register_major_number* * routine to register the driver's I/O * * service routines (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* * routine is that the configure routine 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((caddr_t)mcfgname); dmaexcallback_return_status = status; return; } } /***************************************************** * Autoconfiguration Support Section * * * *--------------- dmaex_probe -----------------------* * * * The bus configuration routine calls the * * driver's dmaex_probe routine during dynamic * * and static configuration. * * * * The dmaex_probe routine calls the BADADDR * * routine during configuration to determine * * if the device is present. If the device is not * * present then the routine returns with error. * * * * Dmaex_probe registers the interrupt service * * routine(s) and returns a one (1) for success * * or zero (0) for failure. * *****************************************************/ static int 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; #ifdef VBA_TYPE_MSK /***************************************************** * Call the kernel routine vba_get_info. Check the * * return value for hardware byte swap capabiliites. * *****************************************************/ if ((vba_get_info(ctlr) & VBA_BYTE_SWAP_MSK) == VBA_NO_HW_BYTE_SWAP) /* * The VME adapter indicates that it does not support hardware byte * swap. Set the sysconfigtab parameter "dmaex_sw_byte_swap" to * indicate to the driver or application code that software byte swap * is needed prior to sending and after receiving data. Software byte * swap is only needed when a mode other than VME_BS_NOSWAP has been * specified. */ dmaex_sw_byte_swap = 1; /***************************************************** * Call the kernel routine vba_get_info to check * * the VMEbus adapter type. * *****************************************************/ if ((vba_get_info(ctlr) & VBA_TYPE_MSK) == VBA_TYPE_UNIV) /* * Set the sysconfigtab parameter "dmaex_univ_adapter" to indicate * to the driver or application code that the VMEbus adapter is the * Universe II. */ dmaex_univ_adapter = 1; else /* * VMEbus adapter is not the Universe II. Clear the sysconfigtab entry. */ dmaex_univ_adapter = 0; #endif /***************************************************** * 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) { /*************************************************** * The software flag indicates that the 'dmaex' * * device hardware is present. * ***************************************************/ #ifdef VBA_TYPE_MSK /**************************************************** * If the device driver is written for or migrating * * to Tru64 UNIX V4.0F or greater and will never * * run on an earlier Tru64 UNIX release, the * * preferred method for bus probing is shown below. * * The new routine, vba_badaddr was added to Tru64 * * UNIX V4.0F. * ****************************************************/ /*************************************************** * Call vba_badaddr to determine if the device is * * present. vba_badaddr returns the value zero (0) * * if the device is present or a nonzero value if * * the device is not present. * * * * The size of the transfer specified by the len * * parameter determines the type of transfer to * * VMEbus. A value if 1, 2, 4, or 8 will result in * * a byte, word, long or quadword transfer respec- * * tively. If the I/O handle was mapped with the * * VME_DENSE flag set, only long(4) and quadword(8)* * transfers will be performed. * * * * The I/O handle specified by the ioh parameter * * must have a valid byte alignment that * * corresponds to the len parameter. * * * * If the VME adapter is the Universe II, all I/O * * handles are mapped to PCI DENSE space regardless* * of the VME_DENSE flag. The Universe II requires * * that the processor be byte/word I/O capable. All* * I/O transactions to the VMEbus will be through * * the processors byte/word I/O memory space. This * * will allow byte, word, long and quadword trans- * * actions to be performed to the VMEbus. * ***************************************************/ if (vba_badaddr(ctlr, sc->csr_handle + DMAEX_CSR_OFF, ctlr->driver->addr1_atype, DMAEX_CSR_SIZE)) return(0); #else /**************************************************** * Device drivers that need to be compatible with * * Tru64 UNIX version V4.0F and earlier releases * * must still use the older method of setting up * * and probing the bus. This method is shown below. * ****************************************************/ /**************************************************** * All I/O handle mapping for the Universe II is * * through PCI DENSE space regardless of the * * setting of the VME_DENSE flag. Universe II * * support requires that the platform be * * byte/word capable. This requirement allows all * * VMEbus read and write operations to operate * * correctly. * ****************************************************/ /**************************************************** * Calls the iohandle_to_phys routine to convert * * an I/O handle to a valid system physical address.* * * * A driver configuration flag, dmaex_univ_adapter, * * is used to indicate if the PCI to VME adapter is * * the Universe II. If a Universe II, set the flag. * ****************************************************/ if ( (!ctlr->driver->addr1_atype & VME_DENSE) && (!dmaex_univ_adapter) ) /************************************************** * The I/O handle is mapped to PCI SPARSE space * * and the PCI to VME adapter is not the * * Universe II. * **************************************************/ phys_addr = iohandle_to_phys(sc->csr_handle + DMAEX_CSR_OFF, HANDLE_BYTE | HANDLE_SPARSE_SPACE); else /************************************************** * The I/O handle is mapped to PCI DENSE space or * * the PCI to VME adapter is the Universe II. * **************************************************/ phys_addr = iohandle_to_phys(sc->csr_handle + DMAEX_CSR_OFF, HANDLE_LONGWORD | HANDLE_DENSE_SPACE); sva = (caddr_t)PHYS_TO_KSEG((vm_offset_t)phys_addr); /*************************************************** * Call BADADDR to determine if the 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. * ***************************************************/ if (BADADDR(sva, DMAEX_CSR_SIZE, ctlr)) return(0); #endif /*************************************************** * Reset the device. The write_io_port routine * * 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 routine. 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 routine 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 * * routine 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->isr_event); event_clear( (event_t *)&sc->isr_event); /*************************************************** * Create events to be used by the ioctl routine * * 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 routine succeeded. * ***************************************************/ return (1); /* return success status */ } /*************************************************** * Controller Initialization Section * * * *--------------- dmaex_cattach -------------------* * * * The bus configuration routine calls the * * driver's attach routine during dynamic and * * static configuration. This routine is usually * * used to perform tasks necessary to establish * * communication with the actual device. * * * * The dmaex_cattach routine 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. * ***************************************************/ static 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(1); } /*************************************************** * Controller Unattach Section * * * *----------- dmaex_ctlr_unattach -----------------* * * * The device driver's controller unattach * * routine is called directly from the bus * * configuration code's controller unconfigure * * routine when a driver is being unloaded. * * * * Statically configured device drivers cannot be * * unloaded. The routine 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. * ***************************************************/ static 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 cannot * * 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 the interrupt handler installed by ioctl * * command 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 mmapping 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 use by the Tru64 UNIX read or * * write file system routines, 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 resources used by the device's * * interrupt service routine. * ***************************************************/ event_terminate( (event_t *)&sc->isr_event); /*************************************************** * Release system resources reserved for posting * * the 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 routine checks that the device * * is open uniquely. * ***************************************************/ static 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 * * return 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 routine clears the DMAEXOPEN * * flag to allow other processes to use the * * device. * ***************************************************/ static int 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 * * the 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 routine 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 driver's mmap mode * * sc->mmap_mode, a mapped VMEbus address can be * * mapped into user space for user PIO operations * * to the device. A kernel physically contiguous * * write or read buffer mapped to VMEbus DMA space * * can be mapped to user space. * ***************************************************/ static 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 routine, it must invoke the ioctl * * routine with SETUP_VME_FOR_MMAP_PIO command * * parameters. This ioctl command 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 the number of bytes to be mapped onto the * * VMEbus. * ***************************************************/ if (!sc->pio_handle) { printf("dmaex_mmap: You must do an outbound mapping first. \n"); error++; } else { /* * Obtain the flags for iohandle_to_phys routine. If the flags * HANDLE_SPARSE_SPACE or HANDLE_DENSE_SPACE are not specified, setup * appropriate flags based upon SPARSE/DENSE mapping to the VMEbus. */ flags = (u_int)(sc->pio_access_type); #ifdef HANDLE_LINEAR_SPACE if (!(flags & HANDLE_LINEAR_SPACE)) { #endif if (!(flags & (HANDLE_DENSE_SPACE | HANDLE_SPARSE_SPACE))) { if ((sc->pio_vme_am) & VME_DENSE) flags = (HANDLE_LONGWORD | HANDLE_DENSE_SPACE); else flags = (HANDLE_LONGWORD | HANDLE_SPARSE_SPACE); } #ifdef HANDLE_LINEAR_SPACE } #endif /*************************************************** * Convert the io_handle that represents the CPU * * I/O space to VMEbus mapping to a bus physical * * address. * ***************************************************/ phys_addr = ((caddr_t)iohandle_to_phys(sc->pio_handle, flags)); DMAEX_DBG2("*** io_handle = 0x%lx\n",(u_long)sc->pio_handle); } break; case MMAP_K_TO_U_MEM_WRT: /*************************************************** * Prior to the user-level device driver calling * * the mmap routine, it must invoke the ioctl * * routine with SETUP_DMA_BLK_WRT ioctl command * * parameters. This ioctl command allows the * * the user-level driver to specify the VMEbus * * address, the VMEbus address modifiers, the swap * * modes, the data size, and the number of bytes * * to be written during the DMA transfer. This * * routine allows the physically contiguous * * kernel memory buffer to be mapped into user * * space. The memory buffer was allocated by this * * driver with the contig_malloc routine. * ***************************************************/ 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 the SETUP_DMA_BLK_WRT ioctl command was invoked, 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 and translate the KSEG address * into a physical address. */ phys_addr = (char *)KSEG_TO_PHYS(sc->blk_wrt_buf_ptr); } } break; case MMAP_K_TO_U_MEM_RD: /*************************************************** * Prior to the user-level device driver calling * * the mmap routine, it must invoke the ioctl * * routine with SETUP_DMA_BLK_RD ioctl command * * parameters. This ioctl command allows the * * the user-level driver to specify the VMEbus * * address, the VMEbus address modifiers, the swap * * modes, the data size, and the number of bytes * * to be read during the DMA transfer. This * * routine allows the physically contiguous * * kernel memory buffer to be mapped into user * * space. The memory buffer was allocated by this * * driver with the contig_malloc routine. * ***************************************************/ 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 the SETUP_DMA_BLK_RD ioctl command was invoked, 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 and translate the KSEG address * into a physical address. */ phys_addr = (char *)KSEG_TO_PHYS(sc->blk_rd_buf_ptr); } } break; default: printf("dmaex_mmap: Invalid mmap selection mode\n"); error++; } /* * Return -1 to caller if an error condition was detected. */ if (error) return(-1); DMAEX_DBG2("*** Bus Physical address = 0x%lx offset = 0x%x\n",phys_addr,off); /*************************************************** * 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 the routine. * ***************************************************/ kpfnum = (long) (((u_long)phys_addr + off) >> 13); DMAEX_DBG2("*** Page frame number = 0x%lx\n",kpfnum); return((int)kpfnum); } /*************************************************** * * *--------- dmaex_setup_blk_mode ------------------* * * * This routine sets up the VME adapters DMA * * engine for either a DMA block mode write or * * read operation. The actual DMA is performed by * * the DO_DMA_BLK_WRT or DO_DMA_BLK_RD ioctl * * commands. * ***************************************************/ static 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 a 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 the caller specified a user buffer, use the caller's buffer; * otherwise, use the physically 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 the caller specified a user buffer, use the callers buffer; * otherwise, use the physically 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 routine. * ***************************************************/ 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 the * * 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 the * * 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 routine performs driver- * * specific ioctl commands. * * * ***************************************************/ static 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 an 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 * * physically contiguous * * write cma_dd kernel * * buffer. * * MMAP_K_TO_U_MEM_RD - will return page frame * * number representing a * * physically 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 routine. * * The dmaex_strategy routine is invoked by * * physio when dmaex_read or dmaex_write is called * * by the user application through the Tru64 UNIX * * read or write routines. The dmaex_strategy * * routine can perform the transfer using * * programmed I/O, using the VME adapter's DMA * * engine or 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 * * routine to determine how the data is to be * * transferred. The transfer mode is returned in * * the ioctl data routine. * * 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 * * routine. 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 routine 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 an 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 * * routine 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 routine when * * the transfer mode equals BLOCK_DMA_MODE. * * BLOCK_DMA_MODE causes the dmaex_strategy * * routine 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 are 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 command SET_STRATEGY_INFO_BLK_DMA and * * the results of the DMA transfer. The data is * * returned through the data parameter of the * * ioctl routine. * * 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 the ioctl * * command SET_STRATEGY_INFO_BLK_DMA and the * * infomation that was updated by dmaex_strategy * * routine when the transfer mode was * * BLOCK_DMA_MODE. No parameters are passed to * * this ioctl command. * ***************************************************/ 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 the dmaex_strategy routine * * when the transfer mode equals PIO_XFER_MODE. * * PIO_XFER_MODE causes the dmaex_strategy * * routine 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 routine 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 * * the dmaex_stratgey PIO_XFER_MODE mapping * * information. It returns the initial mapping * * information specified to the ioctl command * * SETUP_VME_FOR_STRATEGY_PIO and the results of * * the PIO transfer. The data is returned through * * the data parameter of the ioctl routine.