/**************************************************** * * * Copyright (c) 1996 by * * Digital Equipment Corporation, Maynard MA * * All rights reserved. * * * * The information in this software is subject to * * change without notice and should not be * * construed as a commitment by Digital Equipment * * Corporation. * * * * Digital assumes no responsibility for the use * * or reliability of its software on equipment * * that is not supplied by Digital. * ****************************************************/ /*************************************************** * dmaexreg.h Device Register Header File for * * dmaex.c 13-April-1994 * * * * Device register offsets for DMAEX device * ***************************************************/ #define DMAEX_CSR_OFF 0 /* One byte control/status register */ #define DMAEX_COUNT_OFF 1 /* Short byte count */ #define DMAEX_ADDR_OFF 4 /* 32-bit VMEbus transfer address */ /**************************************************** * Convenience defines for calls to sizeof operator * ****************************************************/ #define DMAEX_CSR_SIZE sizeof(char) #define DMAEX_COUNT_SIZE sizeof(short) #define DMAEX_ADDR_SIZE sizeof(vme_addr_t) /*************************************************** * Bits for csr device offset register * ***************************************************/ #define IE 0001 /* Interrupt Enable */ #define DMA_GO 0002 /* Start DMA */ #define RESET 0010 /* Ready for data transfer */ #define ERROR 0020 /* Indicate error */ #define READ 0040 /* Indicate data transfer is read */ o VMEbus /dev/dmaex dmaex.c source file /**************************************************** * Copyright (c) 1996 by * * Digital Equipment Corporation, Maynard MA * * All rights reserved. * * * * The information in this software is subject to * * change without notice and should not be * * construed as a commitment by Digital Equipment * * Corporation. * * * * Digital assumes no responsibility for the use * * or reliability of its software on equipment * * that is not supplied by Digital. * ****************************************************/ /**************************************************** * dmaex.c Driver for dmaex device 12-Jan-1996 * * * * The /dev/dmaex device driver is an example * * driver that supports a fictitious `dmaex' * * device. * * * * The /dev/dmaex driver shows you how to write a * * a device driver that can be statically config- * * ured into the kernel. Dynamic configuration of * * the device driver into the kernel is not * * currently supported. The dev/dmaex driver is * * implemented to operate on the VMEbus. * * * ****************************************************/ /**************************************************** * The following list highlights some features of * * the /dev/dmaex driver: * * * ***************** NOTE ***************************** * * * Refer to Digital UNIX V4.0 Writing Device * * Drivers: Tutorial for discussions on writing * * device drivers and the single binary module * * technology. * * * * Refer to the Digital UNIX V4.0 Release notes for * * VMEbus changes and how configuration information * * is now passed to VMEbus device drivers. * * * * Refer to Writing VMEbus device drivers for * * VME specific information. This book will be * * updated in the next release of Digital UNIX. * * * **************************************************** * * * o Single Binary module * * * * The /dev/dmaex is written to produce a single * * driver image. This single driver image has a * * a file extension of .mod. You compile a * * device driver to produce a single binary * * module that can be statically or dynamically * * configured into a Digital UNIX kernel at any * * point in time. * * * * o Static Configuration * * * * The /dev/dmaex driver's single binary module * * (the dmaex.mod) can be: * * * * - Statically configured into Digital UNIX * * kernel (/vmunix). * * * * - Statically configured into a sysconfigtab * * database image when a user boots a * * Digital UNIX kernel (/vmunix) or when the * * OSFboot links the sysconfigtab image. * * * * o Dynamic configuration * * * * Not supported in this release of Digital UNIX. * * * ****************************************************/ /**************************************************** * 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 dmaexprobe * * o dmaexopen * * o dmaexclose * * o dmaexread * * o dmaexwrite * * o dmaexminphys * * o dmaexstrategy * * o dmaexintr1 * * o dmaexintr2 * * o dmaex_timeout * ****************************************************/ /**************************************************** * 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 /**************************************************** * 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 * * interface uses the following #defines when * * obtaining a major number * ****************************************************/ #define NO_DEV -1 #define MAJOR_INSTANCE 1 /**************************************************** * Data structure sizing approach * **************************************************** * * * The following #define will be used to allocate * * data structures needed 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 #define EACCFAULT 200 /* sc_error: Access violation */ #define ENOMAPREG 201 /* sc_error: No mapping registers */ #define EBUFTOOBIG 202 /* sc_error: Buffer too big */ /**************************************************** * Forward declarations of driver interfaces * ****************************************************/ int dmaex_configure(); static int register_configuration(); static int register_major_number(); static void callback_register_configuration(); static void callback_register_major_number(); int dmaexprobe(), dmaexminphys(), dmaexopen(), dmaexclose(), dmaexread(), dmaexwrite(), dmaexstrategy(), dmaexintr1(), dmaexintr2(), dmaex_timeout(); /**************************************************** * * * Declare an array of buf structures, an array of * * controller structure pointers, an array of * * io_handle_t's and initialize the driver * * structure. * * * * MAX_NDMAEX is used as the maximum number of * * controllers that this driver can supports. * * * ****************************************************/ struct buf dmaexbuf[MAX_NDMAEX]; struct controller *dmaexinfo[MAX_NDMAEX]; io_handle_t reg[MAX_NDMAEX]; struct driver dmaexdriver = { dmaexprobe, /* probe */ 0, /* slave */ 0, /* cattach */ 0, /* dattach */ 0, /* go */ 0, /* addr_list */ 0, /* dev_name */ 0, /* dev_list */ "dmaex", /* ctlr_name */ dmaexinfo, /* ctlr_list */ 0, /* xclu */ 0x8, /* addr1_size */ VME_A24_UDATA_D32, /* addr1_atype */ 0, /* addr2_size */ 0, /* addr2_atype */ 0, /* ctlr_unattach */ 0 /* dev_unattach */ }; /**************************************************** * Declare softc structure to allow the /dec/dmaex * * device driver's interface to share data. * ****************************************************/ struct dmaex_softc { int sc_open; /* DMAEXOPEN, DMAEXCLOSE */ int sc_error; /* Driver specific error code */ struct buf *bp; /* To save buffer pointer */ /* for use by dmaexintr */ dma_handle_t sc_dma_handle; /* handle used by dma_* routines */ } dmaex_softc[MAX_NDMAEX]; int dmaex_config = FALSE; /* state flag indicating driver configured */ int dmaex_devno = NO_DEV; /* No major number assigned yet */ /**************************************************** * The handler_add interface is used to register * * the interrupt handler for the dev/dmaex driver. * * the dmaex_id_t array contains IDs that are used * * to enable, disable and deregister the interrupt * * handlers. * * * * This fictitious device driver needs to install * * two interrupts per controller. The device can * * present two unique interrupts to the system. * ****************************************************/ #define NINTS_PER_DMAEX 2 ihandler_id_t *dmaex_id_t[MAX_NDMAEX][NINTS_PER_DMAEX]; /* handler ID's */ /**************************************************** * The following list describes the use of the * * dmaex_is_dynamic, dmaexcallback_return_status, * * and num_dmaex variables. * * * * o dmaex_is_dynamic * * * * The dmaex_is_dynamic variable will be used to * * control any differences in tasks performed by * * statically or dynamically configured * * /dev/dmaex driver. Implementing a device * * driver to handle a static or dynamic * * configuration request gives customers maximum * * flexibility in how they configure the driver * * into the kernel. * * * * o dmaexcallback_return_status * * * * The callback return status variable is used by * * the callback_register_major_number interface * * to determine if a previous failure has occurred* * in statically configuring the /dev/dmaex * * driver. * * * * o num_dmaex * * * * The num_dmaex variable is used to count the * * number of controllers probed. The dmaexprobe * * interface increments this variable each time * * it probes a DMAEX controller. * ****************************************************/ int num_dmaex = 0; int dmaex_is_dynamic = 0; int dmaexcallback_return_status = ESUCCESS; int dmaex_int_wait = 1; /* number of seconds to wait for */ /* interrupt timeout */ /**************************************************** * External function references. These are needed * * for the devsw declaration and device driver * ****************************************************/ extern int nodev(), nulldev(); /**************************************************** * External function references. These are needed * * for device driver timeouts * ****************************************************/ extern void timeout(), untimeout(); extern int hz; /* The value for hz is defined in param.c */ /**************************************************** * Typically, third-party device drivers specify * * the bus-specific option data in a sysconfigtab * * file fragment. See Writing Device Drivers: * * Tutorial and the bus specific books for informa- * * tion on the syntax associated with VMEbus option * * data attribute fields (VBA_Option). * ****************************************************/ /**************************************************** * The following code sets the members of the * * dsent structure to appropriate values for the * * dev/dmaex driver. The address of this structure * * is passed to the devsw_add interface, which * * registers the I/O services interfaces in the * * dsent table and reserves a major number for the * * /dev/dmaex driver. * ****************************************************/ struct dsent dmaex_devsw_entry = { dmaexopen, /* d_open */ dmaexclose, /* d_close */ dmaexstrategy, /* d_strategy */ dmaexread, /* d_read */ dmaexwrite, /* d_write */ nodev, /* d_ioctl */ nodev, /* d_dump */ nodev, /* d_psize */ nodev, /* d_stop */ nodev, /* d_reset */ nodev, /* d_select */ 0, /* d_mmap */ 0, /* d_segmap */ NULL, /* d_ttys */ DEV_FUNNEL_NULL, /* d_funnel */ 0, /* d_bflags */ 0, /* d_cflags */ }; /**************************************************** * All VMEbus device drivers MUST specify "vba" as * * the bus name. This constant is passed to the * * configure driver interface, which the /dev/dmaex * * driver's dmaex_configure interface calls. * ****************************************************/ #define DMAEX_BUSNAME1 "vba" /**************************************************** * The following code declares variables that are * * required by the cfgmgr framework's driver * * method. You use these variables as fields in * * the driver's attribute table. The attribute * * table for the /dev/dmaex driver is called * * dmaex_attributes. * ****************************************************/ static int NDMAEX = 1; /* Number of controllers to create for driver */ static int majnum = NO_DEV; static int begunit = 0; static int dmaex_version = 0; static int dmaex_developer_debug = 1; static unsigned char subsysname[CFG_ATTR_NAME_SZ] = ""; static unsigned char mcfgname[CFG_ATTR_NAME_SZ] = ""; static unsigned char devmajor[CFG_ATTR_NAME_SZ] = ""; static unsigned char unused[300] = ""; static unsigned char cma_dd[120] = ""; static unsigned char vba_option[300] = ""; /**************************************************** * Setup dmaex device driver debugging macros. * * * * dmaex_developer_debug = 0 - no debug messages * * dmaex_developer_debug = 1 - level 1 messages * * dmaex_developer_debug = 2 - level 2 messages * * dmaex_developer_debug = 3 - level 1 & 2 messages * ****************************************************/ #define DMAEX_DEBUG #ifdef DMAEX_DEBUG #define DMAEX_DBG1 if (dmaex_developer_debug & 0x01) printf #define DMAEX_DBG2 if (dmaex_developer_debug & 0x02) printf #else #define DMAEX_DBG1; #define DMAEX_DBG2; #endif /***************************************************** * The cfgmgr framework uses the driver's attribute * * table (a structure of type cfg_subsys_attr_t) * * during static and dynamic configuration of a * * device driver into the kernel. The cfgmgr * * framework initializes the attributes table early * * in the kernel boot process and whenever it * * receives a sysconfig command request from * * either the kernel or the user command line. * * * * NOTE * * * * There are a number of fields (attributes) that * * appear in the sysconfigtab file fragment that * * that do not appear in the device driver's * * attribute table. The cfgmgr framework's device * * driver method consumes these fields (attributes) * * to perform kernel configuration work and to make * * device special files. To avoid warning messages * * from the cfgmgr framework, you should add these * * attributes to the driver's attribute table * *****************************************************/ /***************************************************** * Declares an array of cfg_subsys_attr_t structures * * and calls it dmaex_attributes. The device driver * * method of cfgmgr fills in the elements in * * dmaex_attributes because the operation types * * designated by these are CFG_OP_CONFIGURE. The * * device driver method of cfgmgr reads the entries * * for the /dev/dmaex driver from the sysconfigtab * * database. (A sysconfigtab file fragment was * * created for the /dev/dmaex driver. * * The sysconfigdb utility appends this sysconfigtab * * file fragment to the /etc/sysconfigtab database.) * * * * To determine if any of these element reads * * failed, the /dev/dmaex driver verifies each from * * the cfg_attr_t structure passed into the * * dmaex_configure interface. * * * * Several of the following fields are used in the * * /dev/dmaex device driver while other fields exist * * to illustrate what can be loaded by cfgmgr into * * the driver attributes table. * * * * These fields can represent tunable parameters to * * indicate whether the driver is statically or * * dynamically configured into the kernel. * *****************************************************/ cfg_subsys_attr_t dmaex_attributes[] = { /* Fields used in this driver */ {"Subsystem_Description",CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (caddr_t)subsysname,0,CFG_ATTR_NAME_SZ,0}, {"Module_Config_Name", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (caddr_t)mcfgname,2,CFG_ATTR_NAME_SZ,0}, {"Device_Char_Major", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (caddr_t)devmajor,0,CFG_ATTR_NAME_SZ,0}, {"Device_Char_Minor", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (caddr_t)unused,0,300,0}, {"Device_Char_Files", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (caddr_t)unused,0,300,0}, {"Device_Major_Req", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (caddr_t)unused,0,300,0}, {"Device_User", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (caddr_t)unused,0,300,0}, {"Device_Group", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (caddr_t)unused,0,300,0}, {"Device_Mode", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (caddr_t)unused,0,300,0}, {"CMA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (caddr_t)cma_dd,0,120,0}, {"numunit", CFG_ATTR_INTTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (caddr_t)&NDMAEX,0,MAX_NDMAEX,0}, {"VBA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY, (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}, {"version", CFG_ATTR_INTTYPE, CFG_OP_QUERY, (caddr_t)&dmaex_version,0,9999999,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_cofigure() * ***************************************************** * Name: dmaex_configure * * * * Arguments: * * * * op Configure operation * * indata Input data structure, cfg_attr_t * * indatalen Size of input data structure * * outdata Formal parameter not used * * outdatalen Formal parameter not used * * * * Called by: * * * * o cfgmgr framework * * * * Return codes: * * * * Success ESUCCESS * * * * Failure EBUSY * * EINVAL * * ESRCH * * ENOTSUP * * Description: * * * * The dmaex_configure interface is called * * indirectly by the cfgmgr framework. The cfgmgr * * framework is responsible for calling all single * * binary modules for registration and integration * * into the kernel. The cfgmgr framework requires * * that a single binary module has both an * * attributes table and a configure interface before * * it (the single binary module) can be registered * * as part of the cfgmgr framework and the Digital * * UNIX kernel. * * * * The dmaex_configure cooperates with the cfgmgr * * framework to complete configure, unconfigure, * * query, and other requests. These request are * * differentiated by the "op" arguments. * * * * Specifically, the dmaex_configure interface shows * * the code the device driver must supply to produce * * a single binary module. This code allows the * * /dev/dmaex driver to be statically configured * * into the kernel. * *****************************************************/ int dmaex_configure(op, indata, indatalen, outdata, outdatalen) cfg_op_t op; cfg_attr_t *indata; size_t indatalen; cfg_attr_t *outdata; size_t outdatalen; { /***************************************************** * The dmaex_configure interface declares the * * following variables: * * * * i Used for loop control * * driver_cfg_state Stores the configuration state * * (either static or dynamic) * *****************************************************/ int i; int driver_cfg_state; /*************************************************** * MAX_DEVICE_CFG_ENTRIES represents maximum * * number of attributes that the cfgmgr frameworks * * configures on behalf of the device driver. * ***************************************************/ #define MAX_DEVICE_CFG_ENTRIES 19 /*************************************************** * The cfg_attr_t list passed into the * * dmaex_configure interface's indata argument * * contains the strings that are stored in the * * sysconfigtab database for this subsystem (the * * /dev/dmaex driver). * ***************************************************/ cfg_attr_t cfg_buf[MAX_DEVICE_CFG_ENTRIES]; switch (op) { /*************************************************** * Configure (load) the driver. * ***************************************************/ case CFG_OP_CONFIGURE: DMAEX_DBG1("dmaex_configure: CFG_OP_CONFIGURE.\n"); /*************************************************** * Pass Attributes Verification * * * * Attributes passed through the cfg_attr_t * * structure are not known to be valid until the * * /dev/dmaex driver can check their status in * * the indata argument. A status other than * * CFG_FRAME_SUCCESS is an error condition. You * * can use this code to: * * * * o Check cfgmgr loading problems with the * * dmaex_attributes (struct type * * cfg_subsys_attr_t). * * * * o Display the contents and status of the * * attributes in the sysconfigtab database for * * the /dev/dmaex driver * * * * o Report cfgmgr's status that indicates a * * failure to load any of the attribute fields * * into the dmaex_attributes structure. * ***************************************************/ bcopy(indata, cfg_buf[0].name, indatalen * (sizeof(cfg_attr_t))); for( i = 0; i < indatalen; i++) switch(cfg_buf[i].type) { case CFG_ATTR_STRTYPE: break; default: switch(cfg_buf[i].status) { case CFG_FRAME_SUCCESS: break; default: printf("%s:",cfg_buf[i].name); switch(cfg_buf[i].status) { case CFG_ATTR_EEXISTS: printf("**Attribute does not exist\n"); break; case CFG_ATTR_EOP: printf("**Attribute does not support operation\n"); break; case CFG_ATTR_ESUBSYS: printf("**Subsystem Failure\n"); break; case CFG_ATTR_ESMALL: printf("**Attribute size/value too small\n"); break; case CFG_ATTR_ELARGE: printf("**Attribute size/value too large\n"); break; case CFG_ATTR_ETYPE: printf("**Attribute invalid type\n"); break; case CFG_ATTR_EINDEX: printf("**Attribute invalid index\n"); break; case CFG_ATTR_EMEM: printf("**Attribute memory allocation error\n"); break; default: printf("**Unknown attribute: "); printf("%x\n", cfg_buf[i].status); } /* * An error status has been detected * return an error of invalid */ return(EINVAL); } break; } /***************************************************** * If this device driver has already been configured * * either statically or dynamically then return this * * dmaex_configure call indicating an error when the * * cfgmgr framework calls the CFG_OP_CONFIGURE entry * * point of the driver's configure interface. * *****************************************************/ if(dmaex_config == TRUE) { printf("dmaex_configure: already configured\n"); return(EINVAL); } /*************************************************** * The following code performs a check on the * * device driver name that the cfgmgr framework * * uses to statically or dynamically configure the * * driver into the kernel. If the device driver * * name is NULL, subsequent code uses the * * controller name stored in driver structure's * * ctlr_name member. The driver structure for the * * /dev/dmaex driver is called dmaexdriver. This * * structure was declared and initialized to * * appropriate values in the Declarations Section * * of the /dev/dmaex driver. * * * * The name specified in the ctlr_name member will * * be replaced if the mcfgname variable is not * * NULL. The value stored in mcfgname supersedes * * the controller name stored in ctlr_name during * * configuration of the driver. * ***************************************************/ if ( strcmp(mcfgname,"") == 0 ) { /* * If the operator is interested in knowing * the configuration name of this driver, * you can set this attribute to CFG_OP_QUERY in * the driver's cfg_subsys_attr_t structure. * * The mcfgname variable is important because * this name is later matched to the driver * name in the Driver_Name field of the option * data attribute of a specific bus. This * mechanism is how a hardware device is matched * to its associated driver. */ strcpy(mcfgname,"dmaex"); } else { /* * mcfgname from sysconfigtab is used to configure * the device driver in the following calls to the * configure_driver interface. */ } if (cfgmgr_get_state(mcfgname, &driver_cfg_state) != ESUCCESS) { /* * CFGMGR fatal error determining the state of the "dmaex" subsystem */ printf("dmaex_configure: cfgmgr_get_state failed\n"); return(EINVAL); } if (driver_cfg_state == SUBSYSTEM_STATICALLY_CONFIGURED) { /* * During Static cfgmgr configuration subsystem callbacks * scheduled to run in the boot path have no way to determine * if the previous callbacks worked. This global flag is looked * at in each of the callback jackets to determine if this * subsystem should still be configured in the boot path. */ dmaexcallback_return_status = ESUCCESS; /* * During Static configuration the cfgmgr schedules work * to be done at pre and post autoconfiguration but it * does does not actually know at this time whether a subsystem * will successful configure into a kernel. The callbacks * are responsible for determining the status of a subsystems * configuration in the boot path and reporting failure to * framework via the cfgmgr_set_state() call. */ dmaex_is_dynamic = SUBSYSTEM_STATICALLY_CONFIGURED; register_callback( callback_register_configuration, CFG_PT_PRECONFIG, CFG_ORD_NOMINAL, (long) 0 ); register_callback( callback_register_major_number, CFG_PT_POSTCONFIG, CFG_ORD_NOMINAL, (long) 0 ); } else { /* * Dynamic configuration is not supported - return error */ printf("**dynamic configuration not supported\n"); dmaex_is_dynamic = 0; return(ENOTSUP); } break; /*************************************************** * Unconfigure (unload) the driver. * ***************************************************/ case CFG_OP_UNCONFIGURE: DMAEX_DBG1("dmaex_configure: CFG_OP_UNCONFIGURE.\n"); /*************************************************** * Return ESUCCESS if the driver is not currently * * dynamically configured. A statically configured * * driver CANNOT be unconfigured/unloaded. * * * * Static drivers will all physically remain in * * kernel whether or not they are configured. * * Dynamic drivers when they are unconfigured are * * also unloaded from the kernel. * ***************************************************/ if(dmaex_is_dynamic == SUBSYSTEM_STATICALLY_CONFIGURED) { return(ESUCCESS); } printf("**can not unconfigure - dynamic support not supported\n"); return(EINVAL); /************************************************** * 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; /************************************************** * Requests to query a subsystem will only * * succeed if the CFG_OP_QUERY: entry point * * returns success. * **************************************************/ case CFG_OP_QUERY: DMAEX_DBG1("dmaex_configure: CFG_OP_QUERY.\n"); break; default: printf("**Unknown operation type\n"); return(ENOTSUP); } /*************************************************** * The driver's configure interface has * * completed successfully. Return a success * * status. * ***************************************************/ return(ESUCCESS); } /*************************************************** * register_configuration() * *************************************************** * Name: register_configuration * * * * Arguments: None * * * * Kernel support interface calls: * * * * o create_controller_struct * * o create_device_struct * * * * Called by: * * * * o During dynamic configuration * * o dmaex_configure * * * * o During static configuration * * o cfgmgr framework callback * * This interface registered with the * * cfgmgr to be called at a specific * * point of the configuration process. * * * * Return Codes: * * * * Failure: ENOMEM * * * * Description: * * * * The register_configuration interface is * * responsible for registering the controller and * * and device information for the /dev/dmaex * * driver. The dynamically configured /dev/dmaex * * driver directly calls the register_configuration* * interface. The statically configured /dev/dmaex* * driver calls register_configuration through a * * register callback called callback_register_ * * configuration. (The dmaex_configure interface * * calls the register_callback interface to * * register the register_configuration interface * * so that it can be called at some later time. * * * * Writing an interface similar to * * register_configuration is the method for * * specifying the controller and device information* * associated with a device driver. * * * * The register_configuration interface causes * * the appropriate controller and device structures* * to be created and integrated into the system * * (hardware) configuration tree for statically and* * dynamically configured drivers. * * * * This method requires device driver writers to * * determine how much operator control should be * * allowed over the system's topology creation * * support code. If appropriate, driver writers * * can define sysconfigtab database fields that * * will allow the operator to control what * * hardware-related information that this * * interface passes to the appropriate structures. * ***************************************************/ static int register_configuration() { /*************************************************** * Declare controller_config and device_config * * structures and structure pointers. These * * structures are the storage mechanism for * * populating the controller and device structures * * associated with a specific device driver. * ***************************************************/ struct controller_config ctlr_register; struct controller_config *ctlr_register_ptr = &ctlr_register; struct device_config device_register; struct device_config *device_register_ptr = &device_register; int status; DMAEX_DBG1("dmaex: register_configuration:\n"); /*************************************************** * Register a controller structure for the * * /dev/dmaex driver. The system manager can * * specify attribute fields for the dmaex: entry * * in /etc/sysconfigtab database that determine * * the number of controller structures to be * * created. It is here that the user-configurable * * attribute NDMAEX would be used to regulate the * * number of controller structures to be created * * up to MAX_NDMAEX value defined by the driver. * ***************************************************/ ctlr_register_ptr->revision = CTLR_CONFIG_REVISION; strcpy(ctlr_register_ptr->subsystem_name, mcfgname); strcpy(ctlr_register_ptr->bus_name,DMAEX_BUSNAME1); ctlr_register_ptr->devdriver = &dmaexdriver; /*************************************************** * Call the create_controller_struct to create the * * controller structure associated with the * * /dev/dmaex driver. * ***************************************************/ status = create_controller_struct(ctlr_register_ptr); if(status != ESUCCESS) { printf("register_configuration: create_controller_struct failed\n"); return (status); } /*************************************************** * Register a device structure for the /dev/dmaex * * driver. This driver does not require a device * * structure but the code is included here for * * reference and is commented out * ***************************************************/ /* device_register_ptr->revision = DEVICE_CONFIG_REVISION; strcpy(device_register_ptr->device_type,"disk"); strcpy(device_register_ptr->device_name,"dmaexdev"); strcpy(device_register_ptr->controler_name,mcfgname); device_register_ptr->controller_num = DRIVER_WILDNUM; status = create_device_struct(device_register_ptr); if (status != ESUCCESS) return(status); */ return(ESUCCESS); } /*************************************************** * register_major_number() * *************************************************** * Name: register_major_number * * * * Arguments: * * * * Kernel support interface calls: * * * * o devsw_add * * * * Called by: * * * * o cfgmgr framework * * * * Return Codes: * * * * Success: ESUCCESS * * * * Failure: ENODEV * * * * Description: * * * * The register_major_number interface registers * * a drivers I/O services interface (and other * * information) and receives a major number. It * * accomplishes these tasks by calling devsw_add * * interface. * ***************************************************/ static int register_major_number() { /*************************************************** * If there are no devices present in the system * * after driver configuration completes, set the * * cfgmgr framework status to unconfigured and * * exit the callback * ***************************************************/ DMAEX_DBG1("dmaex: register_major_num:\n"); if (num_dmaex == 0) { printf("register_major_number: num_dmaex not updated by probe\n"); return (ENODEV); } /*************************************************** * Call devsw_add interface to register the * * driver's I/O services interfaces (and other * * information) and to reserve a major number in * * the device switch table. Each allocation * * provides a block and character entry. * ***************************************************/ majnum = devsw_add(mcfgname, MAJOR_INSTANCE, majnum, &dmaex_devsw_entry); /*************************************************** * call to devsw_add could fail if the driver * * requests a specific major number and that * * major number is currently in use. The call * * could also fail if the device switch table is * * currently full. * ***************************************************/ if (majnum == NO_DEV) { printf("register_major_num: no major number assigned\n"); return (ENODEV); } /*************************************************** * Store the major number returned by the * * devsw_add interface so that is can be used * * later to unconfigure the device. * ***************************************************/ dmaex_devno = majnum; /*************************************************** * This member indicates that the beginning minor * * number will be zero (0). * ***************************************************/ begunit = 0; /*************************************************** * Set this state field to indicate that the * * driver has successfully configured. * ***************************************************/ dmaex_config = TRUE; return(ESUCCESS); } /*************************************************** * callback_register_configuration() * *************************************************** * Name: callback_register_configuration * * * * Arguments: * * * * int point - driver callback point * * int order - Priority at callback * * ulong args - User argument * * * * Kernel support interface calls: * * * * o cfgmgr_set_status * * o register_configuration * * * * Called by: * * * * o cfgmgr framework called through a call * * to the register_callback interface * * * * Return Codes: * * * * Success: ESUCCESS * * * * Failure: EBUSY * * EINVAL * * ESRCH * * * * Description: * * * * This is the Static cfgmgr jacket used to do * * configuration registration in the boot path. * * This jacket routine handles the static callback * * specific error handling but utilizes the same * * configuration routine used by the driver when * * dynamically loading. * * * ***************************************************/ static void callback_register_configuration(int point, int order, ulong args, ulong event_arg) { int status; DMAEX_DBG1("dmaex: callback_register_configuration:\n"); /************************************************ * Detect if a previous failure has occurred in * * statically configuring this driver. If so, * * exit the callback without doing any * * configuration work. * ************************************************/ if(dmaexcallback_return_status != ESUCCESS) { printf ("callback_register_configuration: dmaexcallback_return_status error\n"); return; } /************************************************ * Register this drivers configuration with the * * kernel's topology. * ************************************************/ status = register_configuration(); /************************************************* * If configuration registration is successful, * * then just return from this callback. Other- * * wise signal the cfgmgr framework that this * * driver has failed to configure and set the * * variable callback_return_status to the * * failure condition. * * * *************** Note *************************** * * * A side effect of calling the cfgmgr_set_status* * interface is that the configure interface or * * this driver is called with the unconfigure op * * passed. The unconfigure routine must be * * prepared for this event. * *************************************************/ if(status != ESUCCESS) { printf ("callback_register_configiguration: register_configuration error\n"); cfgmgr_set_status(mcfgname); dmaexcallback_return_status = status; return; } } /*************************************************** * callback_register_major_number() * *************************************************** * Name: callback_register_major_number * * * * Arguments: * * * * int point - driver callback point * * int order - Priority at callback * * ulong args - User argument * * * * Kernel support interface calls: * * * * o cfgmgr_set_status * * o register_major_number * * * * Called by: * * * * o cfgmgr framework as static callback * * * * Return Codes: * * * * Success: ESUCCESS * * * * Failure: EBUSY * * EINVAL * * ESRCH * * * * Description: * * * * This is the Static cfgmgr jacket used to * * do major number registration in the boot path. * * This jacket routine handles the static callback * * specific error handling but utilizes the same * * configuration routine used by the driver when * * dynamically loading. * * * ***************************************************/ static void callback_register_major_number(int point, int order, ulong args, ulong event_arg) { int status; DMAEX_DBG1("dmaex: callback_register_major_number:\n"); /************************************************* * Detect if a previous failure has occurred in * * statically configuring this driver. If so, * * exit the callback without doing any * * configuration work. * *************************************************/ if(dmaexcallback_return_status != ESUCCESS) { printf ("callback_register_major_number: dmaexcallback_return_status error\n"); return; } /************************************************* * Call driver-specific register_major_number * * interface to register the driver's I/O * * services interfaces (and other information) * * and reserve a major number * *************************************************/ status = register_major_number(); /************************************************* * If configuration registration is successful, * * return from this callback. Otherwise, signal * * the cfgmgr framework that this driver has * * failed to configure and set the variable * * callback_return_status to the failure * * condition. * * * *************** Note *************************** * * * A side effect of calling the cfgmgr_set_status* * interface is that the configure interface or * * this driver is called with the unconfigure op * * passed. The unconfigure routine must be * * prepared for this event. * *************************************************/ if(status != ESUCCESS) { printf("callback_register_major_number: register_major_number error \n"); cfgmgr_set_status(mcfgname); dmaexcallback_return_status = status; return; } } /***************************************************** * Autoconfiguration Support Section * * * *--------------- dmaexprobe ------------------------* * * * The dmaexprobe interface is called from the * * VMEbus configuration code during the boot * * phase. The dmaexprobe interface calls the * * BADADDR interface to determine if the device * * is present. If the device is present, dmaexprobe * * registers the interrupt service interfaces(s) and * * returns 1. If the device is not present, * * dmaexprobe returns a zero (0). * *****************************************************/ dmaexprobe(addr, ctlr) 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 * * read_io_port. * ***************************************************/ ihandler_t handler; struct vme_handler_info info; int unit; char csr; u_long phys_addr; caddr_t sva; unit = ctlr->ctlr_num; reg[unit] = addr; DMAEX_DBG1("dmaexprobe: entered\n"); /**************************************************** * Calls the iohandle_to_phys interface to convert * * an I/O handle to a valid system physical address * * present. * ****************************************************/ phys_addr = iohandle_to_phys(reg[unit] + DMAEX_CSR_OFF, HANDLE_BYTE | HANDLE_SPARSE_SPACE); sva = (caddr_t)PHYS_TO_KSEG((vm_offset_t)phys_addr); /*************************************************** * Call BADADDR to determine if device is present. * * BADADDR returns the value zero (0) if the device* * is present. BADADDR returns a nonzero value if * * the device is not present. * ***************************************************/ if (BADADDR(sva, DMAEX_CSR_SIZE, ctlr)) return(0); /*************************************************** * Reset the device. The write_io_port interface * * is called to write the data, and it will * * perform the wbflush to insure the write to I/O * * space completes. * ***************************************************/ write_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0, RESET); mb(); /* perform a memory barrier */ /*************************************************** * Read the byte from the VMEbus by calling the * * read_io_port interface. If the result of the * * bitwise AND is nonzero, dmaexprobe returns the * * value zero (0) to the configuration code to * * indicate that the device is not responding. * ***************************************************/ csr = (char) read_io_port (reg[unit] + 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 dmaexprobe interface calls write_io_port * * to write a byte to the VMEbus. Clear csr reg. * ***************************************************/ write_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0, 0); mb(); /* perform a memory barrier */ /*************************************************** * Register the driver's interrupt service * * interface by calling handler_add. Then enable * * it by calling handler_enable. * ***************************************************/ /* initialize information for handler_add() */ info.gen_intr_info.configuration_st = (caddr_t) ctlr; info.gen_intr_info.intr = dmaexintr1; 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 = dmaexintr2; 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) return (0); if (handler_enable (dmaex_id_t[unit][1]) != 0) { handler_del (dmaex_id_t[unit][1]); return (0); } /*************************************************** * update the number of /dev/dmaex devices probed * ***************************************************/ num_dmaex++; return (1); /* return success status */ } /*************************************************** * Open and Close Device Section * * * *--------------- dmaexopen -----------------------* * * * The dmaexopen interface checks that the device * * is open uniquely. * ***************************************************/ dmaexopen(dev, flag, format) 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 * * initializes sc to the address of the * * dmaex_softc structure associated with this * * DMAEX device. * ***************************************************/ DMAEX_DBG1("dmaexopen: entered\n"); if ((unit >= NDMAEX ) || (unit <= 0)) return (EIO); sc = &dmaex_softc[unit]; if (sc == (struct dmaex_softc *)NULL) return (ENODEV); if (sc->sc_open == DMAEXOPEN) return (EBUSY); /*************************************************** * This sequence of code initializes the * * controller structure pointer to its associated * * DMAEX device. * * * * If the DMAEX device is initialized, set the * * sc_open member to the open bit DMAEXOPEN and * * returns the value zero (0) to indicate success. * * * * Otherwise if the device does not exist, return * * an error. * ***************************************************/ ctlr = dmaexinfo[unit]; if ((ctlr) && (ctlr->alive == ALV_ALIVE)) { sc->sc_open = DMAEXOPEN; return(0); } else return(ENXIO); } /*************************************************** * * *--------------- dmaexclose ----------------------* * * * The dmaexclose interface clears the DMAEXOPEN * * flag to allow other processes to use the * * device. * ***************************************************/ dmaexclose(dev, flag, format) 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 Initializes the reg array to the value * * zero (0). * * * * 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("dmaexclose: entered\n"); sc = &dmaex_softc[unit]; if (sc == (struct dmaex_softc *)NULL) return (ENODEV); sc->sc_open = DMAEXCLOSE; ctlr = dmaexinfo[unit]; if (!(ctlr)) return (ENODEV); if (reg[unit] == (io_handle_t)0) return (ENODEV); write_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0, 0); return(0); } /*************************************************** * Read and Write Device Section * * * *--------------- dmaexread -----------------------* * * * The dmaexread interface calls the physio kernel * * interface to perform the buffer lock, check the * * buffer, and set up the I/O packet. The physio * * interface calls the dmaexstrategy interface to * * access the device. * ***************************************************/ dmaexread(dev, uio, flag) dev_t dev; /* Major/minor device number */ struct uio *uio; /* Pointer to uio structure */ int flag; /* Access mode of device */ { /*************************************************** * The following code: * * * * o Initializes a unit variable to the minor * * device number * * * * o Determines if the minor device number is * * greater than the maximum. If so, return * * EIO to indicate an I/O error. * * * * o Otherwise, call physio to perform the * * buffer lock, check the buffer, and set up * * the I/O packet. * ***************************************************/ register int unit = minor(dev); DMAEX_DBG1("dmaexread: entered\n"); if ((unit >= NDMAEX ) || (unit <= 0)) return (EIO); return (physio(dmaexstrategy, &dmaexbuf[unit], dev, B_READ, dmaexminphys, uio)); } /*************************************************** * * *--------------- dmaexwrite ----------------------* * * * The dmaexwrite interface calls the physio * * kernel interface to perform the buffer lock, * * check the buffer, and set up the I/O packet. * * The physio interface calls the dmaexstrategy * * interface to access the device. * ***************************************************/ dmaexwrite(dev, uio, flag) dev_t dev; /* Major/minor device number */ struct uio *uio; /* Pointer to uio structure */ int flag; /* Access mode of device */ { /*************************************************** * The following code: * * * * o Initializes a unit variable to the minor * * device number * * * * o Determines if the minor device number is * * greater than the maximum. If so, it returns * * EIO to indicate an I/O error. * * * * o Otherwise, it calls physio to perform the * * buffer lock, check the buffer, and set up * * the I/O packet. * ***************************************************/ register int unit = minor(dev); DMAEX_DBG1("dmaexwrite: entered\n"); if ((unit >= NDMAEX) || (unit <= 0)) return (EIO); return (physio(dmaexstrategy, &dmaexbuf[unit], dev, B_WRITE, dmaexminphys, uio)); } dmaexminphys (bp) register struct buf *bp; { /*************************************************** * The following code defines the maximum size of * * the DMA for this device. If the maximum size * * for the DMA exceeds dmaexMAXPHYS, the code * * returns to physio, which calls strategy. This * * can occur several times until the transfer is * * complete. * ***************************************************/ #define dmaexMAXPHYS (64*1024) DMAEX_DBG2("dmaexminphys: entered\n"); if (bp->b_bcount > dmaexMAXPHYS) bp->b_bcount = dmaexMAXPHYS; return; } /*************************************************** * Strategy Section * * * *--------------- dmaexstrategy -------------------* * * * The dmaexstrategy interface is called by the * * phsyio kernel interface. (The physio interface * * is called by the dmaexread and dmaexwrite * * driver interfaces.) The dmaexstrategy * * first makes sure that the user buffer is both * * readable and writable. It determines if the * * buffer size is larger than dmaexMAXPHYS and * * then initiates the I/O. * ***************************************************/ dmaexstrategy(bp) struct buf *bp; /* Pointer to buf structure */ { /*************************************************** * The following code initializes: * * * * o A unit variable to the minor device number * * * * o A controller structure to its associated * * DMAEX device * * * * o A dmaex_softc structure to the address of * * the dmaex_softc associated with this * * DMAEX device * * * * o A variable to store read, write, and enable * * interrupts status information * * * * o A sg_entry structure pointer that the code * * uses in calls to dma_get_curr_sqentry and * * write_io_port * ***************************************************/ register int unit = minor(bp->b_dev); register struct controller *ctlr = dmaexinfo[unit]; register struct dmaex_softc *sc = &dmaex_softc[unit]; char csr; sg_entry_t dmaex_sg; DMAEX_DBG2("dmaexstrategy: entered\n"); /*************************************************** * Check the validity of the unit number and the * * pointers. * ***************************************************/ if ( (!ctlr) || (!sc) ) return (EINVAL); /*************************************************** * The following code saves the buffer pointer * * (bp) for use in dmaexintr1. * * * * It sets up the DMA mapping registers by calling * * the dma_map_alloc and dma_map_load interfaces. * * * * If the return from dma_map_alloc or * * dma_map_load is zero (0), it could not perform * * the requested operation. The dmaexstrategy * * interface: * * * * o Sets the error bit to ENOMAPREG * * * * o Copies the error bit to sc_error * * * * o Sets b_flags to the bitwise inclusive OR of * * the read and error bits * * * * o Completes the I/O operation by calling * * iodone * * * * Return to calling routine (either dmaexread or * * dmaexwrite. * ***************************************************/ sc->bp = bp; sc->sc_dma_handle = (dma_handle_t) NULL; /********************************************************************** * * * To use the vba_set_dma_addr() interface: * * * * declare a variable int flags and * * vme_addr_t vmeaddr = YOUR_VME_ADDRESS * * * * then... * * * * flags = vba_set_dma_addr(ctlr, VME_A32_UDATA_D32 | * * VME_BS_NOSWAP | * * DMA_GUARD_UPPER | * * DMA_ALL,vme_addr); * * * * if (dma_map_load ((unsigned long) bp->b_bcount, * * (vm_offset_t) bp->b_un.b_addr, bp->b_proc, ctlr, * * &sc->sc_dma_handle, 0, flags) == 0) { * * bp->b_error = EIO; * * sc->sc_error = bp->b_error; * * bp->b_flags |= B_ERROR; * * iodone(bp); * * return; * * } * * * **********************************************************************/ if (dma_map_load ((unsigned long) bp->b_bcount, (vm_offset_t) bp->b_un.b_addr, bp->b_proc, ctlr, &sc->sc_dma_handle, 0, VME_A32_UDATA_D32 | VME_BS_NOSWAP | DMA_GUARD_UPPER | DMA_ALL) == 0) { bp->b_error = EIO; sc->sc_error = bp->b_error; bp->b_flags |= B_ERROR; iodone(bp); return; } /*************************************************** * * * If the requested mapping was performed, set up * * the device for transfer. * * * ***************************************************/ dmaex_sg = dma_get_curr_sgentry (sc->sc_dma_handle); write_io_port (reg[unit] + DMAEX_COUNT_OFF, DMAEX_COUNT_SIZE, 0, dmaex_sg->bc); mb(); write_io_port (reg[unit] + DMAEX_ADDR_OFF, DMAEX_ADDR_SIZE, 0, (long)dmaex_sg->ba); mb(); if (bp->b_flags & B_READ) csr = READ | IE; else csr = IE; write_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0, (csr | DMA_GO)); mb(); /*************************************************** * Set a timeout value so we can return if the * * board doesn't respond. It's also * * programmable from the dbx debugger. * ***************************************************/ timeout(dmaex_timeout, (caddr_t) unit, dmaex_int_wait * hz); /*************************************************** * Wait for either timeout or dma to complete * * interrupt * ***************************************************/ sleep(&dmaex_softc[unit], PRIBIO); /*************************************************** * Resources freed here versus in interrupt * * service interface for performance reasons * ***************************************************/ dma_map_unload (DMA_DEALLOC, sc->sc_dma_handle); /*************************************************** * return to physio indicating io is complete * ***************************************************/ iodone(bp); return; } /*************************************************** * Interrupt Section * * * *--------------- dmaexintr1-----------------------* * * * The dmaexintr1 interface releases VMEbus * * mapping registers. It then calls iodone to * * finish the I/O. * * * ***************************************************/ dmaexintr1(unit) int unit; /* Logical unit number for device */ { /*************************************************** * The following code initializes: * * * * o A controller structure to its associated * * DMAEX device * * * * o A dmaex_softc structure to the address of * * the dmaex_softc structure associated with * * this DMAEX device * * * * The code also declares a variable to store the * * return from vme_read_byte and a pointer to a * * buf structure. * * * ***************************************************/ register struct controller *ctlr = dmaexinfo[unit]; register struct dmaex_softc *sc = &dmaex_softc[unit]; char csr; struct buf *bp; untimeout (dmaex_timeout, (caddr_t) unit); DMAEX_DBG2("dmaexintr1: entered\n"); bp = sc->bp; /* Retrieve saved buf pointer */ /*************************************************** * * * Call read_io_port to read a byte from the * * VMEbus. If the error bit is set, an error * * occurred. The code does the following: * * * * o Sets the error bit in b_error * * * * o Sets b_flags to the bitwise inclusive OR * * of the read and error bits to indicate * * an error occurred on this buffer * * * ***************************************************/ csr = read_io_port (reg[unit] + DMAEX_CSR_OFF, DMAEX_CSR_SIZE, 0); if (csr & ERROR) { bp->b_error = EIO; bp->b_flags |= B_ERROR; } /*************************************************** * * * The following code: * * * * o Records the number of bytes remaining in * * b_resid * * * * o Call wakeup returning to strategy() * * * ***************************************************/ bp->b_resid = read_io_port(reg[unit] + DMAEX_COUNT_OFF, DMAEX_COUNT_SIZE, 0); wakeup(&dmaex_softc[unit]); } /*************************************************** * Handle other interrupts from dmaex controller. * * This, and associated code in dmaexprobe() are * * included only for the purpose of illustrating * * how multiple interrupts and there handlers are * * registered. This has no functional meaning in * * this example. * ***************************************************/ dmaexintr2(unit) int unit; /* Logical unit number for device */ { untimeout (dmaex_timeout, (caddr_t) unit); DMAEX_DBG2("dmaexintr2: entered\n"); wakeup(&dmaex_softc[unit]); } dmaex_timeout(unit) int unit; { register struct dmaex_softc *sc = &dmaex_softc[unit]; struct buf *bp; DMAEX_DBG1("dmaex_timeout: entered\n"); bp = sc->bp; /* Retrieve saved buf pointer */ bp->b_error = ETIMEDOUT; bp->b_flags |= B_ERROR; wakeup(&dmaex_softc[unit]); }