[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


B    Device Driver Example Source Listings

This appendix contains the source listings for the following device drivers:


[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


B.1    Source Listing for the /dev/none Device Driver

/***************************************************
 *                                                 *
 *           Copyright (c) 1995 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     *
 * which is not supplied by Digital.               *
 ***************************************************/

/*************************************************** * nonereg.h Header file for none.c 13-Apr-1996 * * * * * * Define ioctl macros for the /dev/none driver. * ***************************************************/

#define DN_GETCOUNT _IOR(0,1,int) #define DN_CLRCOUNT _IO(0,2)

/*************************************************** * * * Device register offset definition for the NONE * * device. * * * ***************************************************/

#define NONE_CSR 0 /* 64-bit read/write CSR/LED register */

/***************************************************
 * none.c  Driver for NONE device    13-April-1996 *
 *                                                 *
 * The /dev/none device driver is an example       *
 * driver that supports a fictitious NONE device.  *
 * The /dev/none device driver shows you how       *
 * to write a device driver that can be statically *
 * or dynamically configured into the kernel.      *
 * The /dev/none driver is implemented to operate  *
 * on the TURBOchannel bus.  However, comments     *
 * describe what you need to change to support the *
 * following buses: EISA, ISA, and PCI.  To help   *
 * you more easily locate sections in the driver   *
 * that need to deal with multiple bus issues, the *
 * following words appear in the comment line:     *
 *                                                 *
 *        !!* BUS-SPECIFIC CHANGE *!!              *
 *                                                 *
 * The comments following the above words provide  *
 * information on what changes you need to make    *
 * to a device driver to support more than one     *
 * bus.                                            *
 ***************************************************/
/***************************************************
 *            A WORD ABOUT PSEUDODEVICE DRIVERS    *
 ***************************************************
 *                                                 *
 * The /dev/none driver is written as though it    *
 * controls a real hardware device and operates    *
 * on the TURBOchannel bus.  Therefore, do not     *
 * treat the /dev/none driver as a pseudodevice    *
 * driver.                                         *
 *                                                 *
 * To learn how to write a pseudodevice driver,    *
 * see Section B.2 of Writing Device Drivers:      *
 * Tutorial.  This section contains a source       *
 * listing for an example pseudodevice driver.     *
 *                                                 *
 * Because the /dev/none driver does not control   *
 * a real hardware device and does not operate on  *
 * a real bus, do not attempt to open it unless    *
 * you make the appropriate modifications.         *
 *                                                 *
 ***************************************************/

/*************************************************** * FEATURES OF THE /dev/none DRIVER * *************************************************** * * * The following list highlights some features of * * the /dev/none driver: * * * * o Single binary module * * * * The /dev/none driver is written to produce a * * single driver image. This single driver * * image has 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/none driver's single binary module * * (the none.mod file) can be: * * * * - Statically configured into a Digital UNIX * * kernel (/vmunix) * * * * - Statically configured into a boot-link * * kernel (a /sysconfigtab text file) * * * * Typically, only device driver writers * * testing foreign devices and systems * * engineers (Digital internal) testing new * * hardware platforms in the boot path before * * creating kits use the boot-link kernel * * technology. * * * * o Dynamic configuration * * * * The /dev/none driver's single binary module * * (the none.mod file) can be dynamically * * configured into a Digital UNIX kernel (/vmunix) * * when a user makes a request (by using the * * sysconfig utility) at single user or multiuser * * time. * ***************************************************/ /*************************************************** * INTERFACES that /dev/none IMPLEMENTS * *************************************************** * * * o register_configuration * * o callback_register_configuration * * o register_major_number * * o callback_register_major_number * * o none_configure * * o noneprobe * * o nonecattach * * o none_ctlr_unattach * * o noneopen * * o noneclose * * o noneread * * o nonewrite * * o noneintr * * o noneioctl * /*************************************************** * Tim Burke, Karl Ebner, Mark Parenti, and * * Al Wojtas * * * * Digital Device Driver Project * * * ***************************************************/ /*************************************************** * Include Files Section * ***************************************************/

/*************************************************** * Common driver header files * ***************************************************/

#include <sys/param.h> #include <sys/systm.h> #include <sys/ioctl.h> #include <sys/tty.h> #include <sys/user.h> #include <sys/proc.h> #include <sys/map.h> #include <sys/buf.h> #include <sys/vm.h> #include <sys/file.h> #include <sys/uio.h> #include <sys/types.h> #include <sys/errno.h> #include <sys/conf.h> #include <sys/kernel.h> #include <sys/devio.h> #include <hal/cpuconf.h> #include <sys/exec.h> #include <io/common/devdriver.h> #include <sys/sysconfig.h> #include <kern/kalloc.h> /*************************************************** * !!* BUS-SPECIFIC CHANGE *!! * *************************************************** * If you are writing a device driver to * * operate on more than one bus, you must include * * the correct bus-specific header file. The * * following list shows the correct header files * * for the EISA, ISA, PCI, and TURBOchannel buses: * * * * #include <io/dec/eisa/eisa.h> * * #include <io/dec/eisa/isa.h> * * #include <io/dec/pci/pci.h> * * #include <io/dec/tc/tc.h> * * * * Because the fictitious NONE device controller * * is connected to a TURBOchannel bus, the tc.h * * file is included in the /dev/none driver for * * illustrative purposes. If your driver operates * * on all of these buses, you would include all of * * these bus-specific header files. * ***************************************************/ #include <io/dec/tc/tc.h> #include <machine/cpu.h> #include <io/ESA100/nonereg.h> /* Device register header file */

/*************************************************** * The /dev/none 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/none driver. * * There can be at most 8 instances of the NONE * * device controller on the system. This means * * that MAX_NNONE is the maximum number of * * controllers that the /dev/none driver 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 configuration. * * * * Note that MAX_NNONE is used in the * * none_attributes table, specifically as the * * maximum value for the numunit attribute field. * * The NNONE variable is the data value address * * for the numunit attribute field. * ***************************************************/

#define MAX_NNONE 8

/***************************************************
 * Declarations Section                            *
 ***************************************************/

/*************************************************** * Bits used in read/write operations * ***************************************************/

#define DN_RESET 0001 /* Device ready for data transfer */ #define DN_ERROR 0002 /* Indicate error */ /*************************************************** * Defines for softc structure * ***************************************************/

#define DN_OPEN 1 /* Device open bit */ #define DN_CLOSE 0 /* Device close bit */ /*************************************************** * Forward declarations of driver interfaces * ***************************************************/ int noneprobe(), nonecattach(), noneintr(); int noneopen(), noneclose(), noneread(), nonewrite(); int noneioctl(), none_ctlr_unattach();

/*************************************************** * controller and driver Structures * ***************************************************/ /*************************************************** * Declare an array of pointers to controller * * structures * ***************************************************/ struct controller *noneinfo[MAX_NNONE]; /*************************************************** * Declare and initialize driver structure * ***************************************************/ struct driver nonedriver = { noneprobe, /* probe */ 0, /* slave */ nonecattach, /* cattach */ 0, /* dattach */ 0, /* go */ 0, /* addr_list */ 0, /* dev_name */ 0, /* dev_list */ "none", /* ctlr_name */ noneinfo, /* ctlr_list */ 0, /* xclu */ 0, /* addr1_size */ 0, /* addr1_atype */ 0, /* addr2_size */ 0, /* addr2_atype */ none_ctlr_unattach, /* ctlr_unattach */ 0 /* dev_unattach */ }; /*************************************************** * Declare softc structure * ***************************************************/ struct none_softc { int sc_openf; /* Open flag */ int sc_count; /* Count of characters written to device */ int sc_state; /* Device state, not currently used */ } none_softc[MAX_NNONE]; int none_config = FALSE; /* State flags indicating driver configured */ int none_devno = NO_DEV; /* No major number assigned yet. */ int NNONE = 1; /* One controller supported by default */

/*************************************************** * The handler_add interface is used to register * * the interrupt handler for the /dev/none driver. * * The none_id_t array contains IDs that are used * * to enable, disable, and deregister the * * interrupt handlers. * ***************************************************/

ihandler_id_t *none_id_t[MAX_NNONE];

/*************************************************** * The following list describes the use of the * * none_is_dynamic, callback_return_status, and * * num_none variables: * * * * o none_is_dynamic * * * * The none_is_dynamic variable will be used to * * control any differences in tasks performed * * by the statically or dynamically configured * * /dev/none 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 callback_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/none driver. * * * * o num_none * * * * The num_none variable is used to count the * * number of controllers probed. The noneprobe * * interface increments this variable each time * * it probes a NONE controller. * ***************************************************/

int none_is_dynamic = 0; int num_none = 0; int callback_return_status = ESUCCESS;

/*************************************************** * External function references. These are needed * * for the devsw declaration. * ***************************************************/

extern int nodev(), nulldev();

/************************************************** * !!* BUS SPECIFIC CHANGE *!! * *************************************************** * Bus-Specific Option Data * *************************************************** * You specify bus-specific option data for the * * EISA, ISA, PCI, TURBOchannel, and VME buses * * in the sysconfigtab database. You supply the * * bus-specific option data for each bus that * * your driver operates on. * * * * Typically, third-party driver writers specify * * the bus-specific option data in a * * sysconfigtab file fragment. See * * Writing Device Drivers: Tutorial and the * * bus-specific books for information on the * * syntax associated with the following * * bus-specific option data attribute fields: * * * * o EISA_Option * * o ISA_Option * * o PCI_Option * * o TC_Option * * o VBA_Option * ***************************************************/

/************************************************** * The following code sets the members of the * * the dsent structure to appropriate values * * for the /dev/none 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/none driver. * **************************************************/

struct dsent none_devsw_entry = { noneopen, /* d_open */ noneclose, /* d_close */ nodev, /* d_strategy */ noneread, /* d_read */ nonewrite, /* d_write */ noneioctl, /* 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, /* d_funnel */ 0, /* d_bflags */ 0, /* d_cflags */ };

/***************************************************
 *        !!* BUS-SPECIFIC CHANGE *!!              *
 ***************************************************
 * Your goal should be to write one device driver  *
 * that can operate on multiple buses.  If your    *
 * driver operates on Digital-implemented buses    *
 * (EISA, ISA, PCI, and TURBOchannel), then you    *
 * define something similar to the following:      * 
 *                                                 *
 *      #define DN_BUSNAME1    "tc"   TURBOchannel *
 *      #define DN_BUSNAME2    "isa"  eisa bus     *
 *      #define DN_BUSNAME3    "eisa" isa bus      *
 *      #define DN_BUSNAME4    "pci"  pci bus      *
 *                                                 *
 * These constants are passed to the               *
 * configure_driver interface, which the /dev/none *
 * driver's  none_configure interface calls.       *
 *                                                 *
 * Another way to define a constant to handle      *
 * multiple buses is to use the asterisk (*),      *
 * which signifies a wildcard character to the     *
 * configure_driver interface.  In other words,    *
 * the asterisk (*) tells the configure_driver     *
 * interface that the driver can operate on any of *
 * the Digital-supported buses.  The /dev/none     *
 * driver uses the asterisk (*) to define the bus  *
 * name.                                           *
 ***************************************************/

#define DN_BUSNAME1    "DRIVER_WILDNAME /* This can be any */
/* Digital-supported bus */

/*************************************************** * The following code declares variables that are * * required by the cfgmgr framework. You use * * these variables as fields in the driver's * * attribute table. The attribute table for the * * /dev/none driver is called none_attributes. * ***************************************************/ static int majnum = NO_DEV; static int noneversion = 0; static int none_developer_debug = 0; static unsigned char mcfgname[CFG_ATTR_NAME_SZ] = ; static unsigned char unused[300] = ; static unsigned char cma_dd[120] = ; static unsigned char tc_optiondata[300] = ;

/*************************************************** * 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 * * are not needed by the /dev/none driver. * * The cfgmgr framework 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 none_attributes. * * The device driver method of cfgmgr fills in * * the elements in none_attributes because * * the operation types designated by these * * are CFG_OP_CONFIGURE. The device driver * * method of cfgmgr reads the entries for the * * /dev/none driver from the sysconfigtab * * database. (A sysconfigtab file fragment * * was created for the /dev/none 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/none driver verifies each * * from the cfg_attr_t structure passed into * * the none_configure interface. * * * * Several of the following fields are used * * in the /dev/none 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 none_attributes[] = { /* Fields used in this driver. */ {"Module_Config_Name", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)mcfgname,2,CFG_ATTR_NAME_SZ,0}, {"CMA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)cma_dd,0,300,0}, {"numunit", CFG_ATTR_INTTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE, (caddr_t)&NNONE,0,MAX_NNONE,0}, {"TC_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)&tc_optiondata,0,300,0}, /*************************************************** * NOTE * * * * There are a number of fields (attributes) that * * appear in the sysconfigtab file fragment that * * are not needed by the /dev/none driver. * * The cfgmgr framework 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. * * * * The following device driver-related attributes * * are not used by the /dev/none driver and are * * included in the attributes table to avoid * * the warning messages. * ***************************************************/ {"Module_Path", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Subdir", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Block_Subdir", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Char_Subdir", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Major_Req", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Block_Major", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Block_Minor", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Block_Files", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Char_Major", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Char_Minor", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Char_Files", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_User", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Group", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"Device_Mode", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"EISA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"ISA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"VBA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, {"PCI_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE, (caddr_t)unused,0,300,0}, /********************************************** * The /dev/none device driver modifies the * * following attributes during a configure * * or unconfigure operation. The cfgmgr * * framework 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}, {"version", CFG_ATTR_INTTYPE, CFG_OP_QUERY, (caddr_t)&noneversion,0,9999999,0}, /********************************************** * Device driver-specific entries that * * describe configuration and operation of * * the driver should go here. * **********************************************/ /********************************************** * The None_Developer_Debug attribute is used * * with the printf interface to display * * runtime messages on the console terminal. * * A device driver writer might want to * * define such an attribute to collect * * detailed information relating to a driver * * problem. When the attribute is set to 1 * * by the cfgmgr framework (through the * * CFG_OP_RECONFIGURE operation) or * * initialized by sysconfigtab in the * * bootpath, these messages appear on the * * console terminal. These messages can * * provide the driver writer with * * configuration information that relates to * * detailed bus configuration information. * * * * The None_Developer_Debug attribute * * (or another attribute similar to it) is * * used for detailed internal debugging of * * code by Digital personnel or someone who * * has a source license. * * * * Note that the None_Developer_Debug * * attribute's operations are query, * * reconfigure, and configure. * * * * none_developer_debug = 1 turns on these * * messages * * none_developer_debug = 0 turns off these * * messages * * * * You also need to use the #ifdef and * * #endif statements as follows: * * * * #ifdef NONE_DEBUG * * driver code * * #endif * **********************************************/ {"None_Developer_Debug", CFG_ATTR_INTTYPE, CFG_OP_QUERY | CFG_OP_RECONFIGURE | CFG_OP_CONFIGURE, (caddr_t)&none_developer_debug,0,1,0}, {,0,0,0,0,0,0} };

/*************************************************** * none_configure() * *************************************************** * Name: none_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 * * * * Kernel support interface calls: * * * * o atoi * * o bcopy * * o devsw_add * * o devsw_del * * o cfgmgr_get_state * * o configure_driver * * o printf * * o register_callback * * o register_configuration * * o register_major_number * * o strcmp * * o strcpy * * o unconfigure_driver * * * * Called by: * * * * o cfgmgr framework * * * * Return Codes: * * * * Success: ESUCCESS * * * * Failure: EBUSY * * EINVAL * * ESRCH * * ENOTSUP * * * * Description: * * * * The none_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 none_configure interface cooperates with * * the cfgmgr framework to complete configure, * * unconfigure, query, and other requests. * * These requests are differentiated by the "op" * * argument. * * * * Specifically, the none_configure interface * * shows the code a device driver must supply to * * produce a single binary module. This code * * allows the /dev/none driver to be statically or * * dynamically configured into the kernel. * ***************************************************/

none_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 none_configure interface declares the * * following variables: * * * * retval Stores the return value * * from the * * register_configuration * * interface. * * * * i Used in the for loop. * * * * driver_cfg_state Stores the configuration * * state (either static or * * dynamic) of the driver. * ***************************************************/ int retval; int i; int driver_cfg_state;

/*************************************************** * * * MAX_DEVICE_CFG_ENTRIES represents the maximum * * number of attributes that the cfgmgr framework * * configures on behalf of the device driver. * ***************************************************/

#define MAX_DEVICE_CFG_ENTRIES 18

/*************************************************** * The cfg_attr_t list passed into the * * none_configure interface's indata argument * * contains the strings that are stored in the * * sysconfigtab database for this subsystem (the * * /dev/none driver). * ***************************************************/

#define NONE_DEBUG #ifdef NONE_DEBUG cfg_attr_t cfg_buf[MAX_DEVICE_CFG_ENTRIES]; #endif /* NONE_DEBUG */

        switch (op) {

/*************************************************** * Configure (load) the driver. * * * ***************************************************/

case CFG_OP_CONFIGURE:

#ifdef NONE_DEBUG printf("none_configure: CFG_OP_CONFIGURE.\n"); #endif /* NONE_DEBUG */

/*************************************************** * Pass Attributes Verification * * * * Attributes passed through the cfg_attr_t * * structure are not known to be valid until the * * /dev/none 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 * * none_attributes (struct type * * cfg_subsys_attr_t) * * * * o Display the contents and status of the * * attributes in the sysconfigtab database for * * the /dev/none driver * * * * o Report cfgmgr's status that indicates a * * failure to load any of the attribute fields * * into the none_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); } /* return(EINVAL); */ } break; } /* * If this device driver has already been configured, * either statically or dynamically, then return this * none_configure call indicating an error when the * cfgmgr framework calls the CFG_OP_CONFIGURE entry * point of the driver's configure interface. */ if(none_config == TRUE) 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/none driver   *
 * is called nonedriver.  This structure was       *
 * declared and initialized to appropriate values  *
 * in the Declarations Section of the /dev/none    *
 * 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
                                   for a specific bus.  This mechanism
                                   is how a hardware device is matched
                                   to its associated driver.
                                 */
                                  strcpy(mcfgname,"none"); 
                              }
                              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){
                   return(EINVAL); /* cfgmgr fatal error determining
                                     * the state of the "none" subsystem
                                     */
              }

              if(driver_cfg_state == SUBSYSTEM_STATICALLY_CONFIGURED)  { 

                 callback_return_status = ESUCCESS;

                 none_is_dynamic = SUBSYSTEM_STATICALLY_CONFIGURED;

                 register_callback(callback_register_configuration,
                                   CFG_PT_PRECONFIG,
                                   CFG_ORD_NOMINAL, (long) 0L );

                 register_callback(callback_register_major_number,
                                   CFG_PT_POSTCONFIG,
                                   CFG_ORD_NOMINAL, (long) 0L );
              }
              else { 


                 none_is_dynamic = SUBSYSTEM_DYNAMICALLY_CONFIGURED; 

                 retval = register_configuration(); 
                 if(retval != ESUCCESS)
                    return(retval); 

                 if((retval = configure_driver(mcfgname, DRIVER_WILDNUM,
                    DN_BUSNAME1, &nonedriver)) != ESUCCESS) { 

                     return(retval);
                 }

                 retval = register_major_number(); 
                 if(retval != ESUCCESS) 
                    return(retval);

               }

             break;

/*************************************************** * Unconfigure (unload) the driver. * ***************************************************/

case CFG_OP_UNCONFIGURE:

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef NONE_DEBUG printf("none_configure: CFG_OP_UNCONFIGURE.\n"); #endif /* NONE_DEBUG */ /*************************************************** * Return ESUCCESS if the driver is not * * currently dynamically configured. A * * statically configured driver CANNOT be * * unconfigure/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(none_is_dynamic == SUBSYSTEM_STATICALLY_CONFIGURED) { return(ESUCCESS); }

/*************************************************** * 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_none; i++) { if (none_softc[i].sc_openf != 0) { return(EBUSY); } }

/*************************************************** * Call cdevsw_del to remove the driver * * entry points from the in-memory resident * * cdevsw table. This is done prior to * * deleting the loadable configuration * * and handlers to prevent users from * * accessing the device in the middle of * * deconfigure operation. * ***************************************************/

retval = devsw_del(mcfgname, MAJOR_INSTANCE); if (retval == NO_DEV) { return(ESRCH); }

/*************************************************** * 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 tc * * bus. The controller name and number are * * wildcarded. This causes all instances * * that match the specified driver structure * * to be deregistered. Through the * * bus-specific code, this interface results * * in a call to the none_ctlr_unattach * * interface for each instance of the * * controller. * ***************************************************/

if (unconfigure_driver(DN_BUSNAME1, DRIVER_WILDNUM, &nonedriver, DN_BUSNAME1, DRIVER_WILDNUM) != 0) {

/*************************************************** * DEBUG STATEMENT * ***************************************************/ #ifdef NONE_DEBUG printf("none_configure:unconfigure_driver failed.\n"); #endif /* NONE_DEBUG */

return(ESRCH); } }
none_is_dynamic = 0; none_config = FALSE; break;

/************************************************** * Requests to query a loadable subsystem will * * only succeed if the CFG_OP_QUERY: entry * * returns success. * **************************************************/

case CFG_OP_QUERY: break;

/************************************************** * Code to perform the tasks associated with a * * system manager request to reconfigure the * * currently loaded device driver. * **************************************************/

case CFG_OP_RECONFIGURE: break;

              default: /* Unknown operation type */
                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             *
 *          - none_configure                       *
 *                                                 *
 *      o During static Configuration              *
 *          - cfgmgr framework callback            *
 *            Specifically, the /dev/none driver   *
 *            registers an interface called        *
 *            callback_register_configuration.     *
 *            The cfgmgr framework calls           *
 *            callback_register_configuration,     *
 *            which contains a call to the         *
 *            register_configuration interface.    *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Failure:        ENOMEM                     *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * The register_configuration interface is         *
 * responsible for registering the controller and  *
 * device information for the /dev/none driver.    *
 * The dynamically configured /dev/none driver     *
 * directly calls the register_configuration       *
 * interface.  The statically configured /dev/none *
 * driver calls register_configuration through a   *
 * register callback called                        *
 * callback_register_configuration.  (The          *
 * none_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. *
 ***************************************************/

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

/***************************************************
 * Register a controller structure for the         *
 * /dev/none driver.  The system manager can       *
 * specify attribute fields for the none: entry in *
 * etc/sysconfigtab database that determine the    *
 * number of controller structures to be created.  *
 * It is here that the user-configurable attribute *
 * NNONE would be used to regulate the number of   *
 * controller structures to be created up to the   *
 * MAX_NNONE value defined in the driver.          *
 ***************************************************/


   ctlr_register_ptr->revision = CTLR_CONFIG_REVISION; 
   strcpy(ctlr_register_ptr->subsystem_name, mcfgname); 
   strcpy(ctlr_register_ptr->bus_name,DN_BUSNAME1); 
   ctlr_register_ptr->devdriver = &nonedriver; 

/***************************************************
 * Call the create_controller_struct to create the *
 * controller structure associated with the        *
 * /dev/none driver.                               * 
 ***************************************************/


   status = create_controller_struct(ctlr_register_ptr); 
   if(status != ESUCCESS)
       return (status); 

/***************************************************
 * Register a device structure for the /dev/none   *
 * driver.                                         * 
 ***************************************************/


   device_register_ptr->revision = DEVICE_CONFIG_REVISION; 
   strcpy(device_register_ptr->device_type,"disk"); 
   strcpy(device_register_ptr->device_name,"nonedev"); 
   strcpy(device_register_ptr->controller_name,"none"); 
   device_register_ptr->controller_num = 0; 

/***************************************************
 * Call the create_device_struct to create the     *
 * device structure associated with the /dev/none  *
 * driver.                                         * 
 ***************************************************/


   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 driver's I/O services interfaces (and other   *
 * information) and reserves a major number.  It   *
 * accomplishes these tasks by calling the         *
 * devsw_add interface.                            *
 ***************************************************/

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.                              *
 ***************************************************/


     if (num_none == 0) { 
         return (ENODEV);
      }

/***************************************************
 * Call the 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 device entry.    *
 ***************************************************/


     majnum = devsw_add(mcfgname, MAJOR_INSTANCE,
                        majnum, &none_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) { 
           return (ENODEV);
     }

/***************************************************
 * Store the major number returned by the          *
 * devsw_add interface so that it can be used      *
 * later to unconfigure the device.                *
 ***************************************************/


     none_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. * ***************************************************/

none_config = TRUE; return(ESUCCESS); }

/***************************************************
 *       callback_register_configuration()         *
 ***************************************************
 * Name:   callback_register_configuration()       *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 *    int    point  - Driver callback point        *
 *    int    order  - Priority at callback         *
 *    ulong  argument  - User argument             *
 *    ulong  event_arg  - Argument passed by       *
 *                        kernel dispatcher        *
 *                                                 *                        
 *                                                 *
 * Kernel support interface calls:                 *
 *                                                 *
 *      o cfgmgr_set_status                        *
 *                                                 *
 * Driver interface calls:                         *
 *                                                 *
 *      o register_configuration                   *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 *          o cfgmgr framework 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 interface handles the static        *
 * callback specific error handling but utilizes   *
 * the same configuration interface the driver     *
 * uses when dynamically configured.               *
 ***************************************************/

void
callback_register_configuration(point, order, argument, event_arg)
    int point; 
    int order; 
    ulong argument; 
    ulong event_arg; 
{
int  status; 

/***************************************************
 * Detect if a previous failure has occurred in    *
 * statically configuring this driver. If so, exit *
 * the callback without doing any configuration    *
 * work.
 ***************************************************/


   if(callback_return_status != ESUCCESS)
      return; 

/***************************************************
 *  Register this driver's configuration with the  *
 * kernel's topology.                              *
 ***************************************************/


   status  = register_configuration(); 

/***************************************************
 * 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 interface must be      *
 * prepared for this event.                        *
 ***************************************************/


   if(status != ESUCCESS) { 
        cfgmgr_set_status(mcfgname); 
        callback_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  argument - User argument              *
 *    ulong  event_arg  - Argument passed by       *
 *                        kernel dispatcher        *
 *                                                 *                        
 *                                                 *
 * Kernel support interface calls:                 *
 *                                                 *
 *      o cfgmgr_set_status                        *
 *                                                 *
 * Driver interface calls:                         *
 *                                                 *
 *      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 configuring.                        *
 *                                                 *
 ***************************************************/

void
callback_register_major_number(point,
                               order,
                               argument,
                               event_arg)
    int point; 
    int order; 
    ulong argument; 
    ulong event_arg; 
{
int  status; 

/***************************************************
 * Detect if a previous failure has occurred in    *
 * statically configuring this driver. If so, exit *
 * the callback without doing any configuration    *
 * work.
 ***************************************************/


   if(callback_return_status != ESUCCESS)
      return; 

/***************************************************
 * Call the 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 interface must be      *
 * prepared for this event.                        *
 ***************************************************/


   if(status != ESUCCESS) { 
        cfgmgr_set_status(mcfgname); 
        callback_return_status = status; 
      return;
   }
}

/*************************************************** * noneprobe() * *************************************************** * Name: noneprobe * * * * Arguments (for TURBOchannel bus): * * * * bus_io_handle I/O handle passed to the * * /dev/none driver's probe * * interface * * * * ctlr Pointer to controller structure * * * * Kernel support interface calls: * * * * o BADADDR * * o handler_add * * o handler_del * * o handler_enable * * o mb * * o read_io_port * * o write_io_port * * * * Called by: * * * * o Bus configuration code (confl1) * * * * Return Codes: * * * * Success: Nonzero value * * * * Failure: 0 (the value zero) * * * * Description: * * * * The bus configuration code's confl1 interface * * calls the driver's probe interface during * * device autoconfiguration. A statically * * configured device driver typically calls the * * BADADDR interface to determine if a device is * * present during device autoconfiguration. A * * dynamically configured device driver should not * * call BADADDR during device autoconfiguration. * * The /dev/none driver's noneprobe interface * * shows how to call BADADDR. * * * * Specifically, the noneprobe interface * * determines if the device is present on the * * system. If the device is present, noneprobe * * returns a nonzero value. If the the device is * * not present, nonprobe returns the value 0 * * (zero). * ***************************************************/

*************************************************** * !!* BUS-SPECIFIC CHANGE *!! * *************************************************** * * * The first argument associated with a driver's * * probe interface is bus specific. To write * * portable device drivers across multiple bus * * architectures, you need to know the first * * argument associated with the probe interface * * for that bus. See the bus-specific driver book * * for a description of the first argument * * associated with the probe interface for that * * bus. * * * * The second argument for a driver's probe * * interface is always a pointer to a controller * * structure. You can use the controller * * structure pointer to determine the bus to which * * the device controller is connected. Typically, * * you reference the bus_name member of the bus * * structure through the controller structure's * * bus_hd member. * ***************************************************/

noneprobe(bus_io_handle, ctlr) io_handle_t bus_io_handle; struct controller *ctlr;

{

/*************************************************** * * * The ihandler_t and handler_intr_info structures * * will be used to register the interrupt handler * * for the driver. * ****************************************************/

ihandler_t handler; struct handler_intr_info info; int unit = ctlr->ctlr_num; register io_handle_t reg = bus_io_handle;

/*************************************************** * Return a probe failure if the number of units * * probed is greater than or equal to the number * * of units the system is configured for. * ***************************************************/

if( num_none >= NNONE) return (0);

/*************************************************** * Dynamically register the interrupt handlers * * for the /dev/none device driver. * * * * To accomplish this task, you need to set the * * members of the ihandler_t and handler_intr_info * * structures to appropriate values. The * * following code shows how these members get set * * in the /dev/none driver's noneprobe interface. * ***************************************************/

/*************************************************** * Specify the bus that this controller is * * attached to. * ***************************************************/

handler.ih_bus = ctlr->bus_hd;

/*************************************************** * Set up the members of the handler_intr_info * * structure. The following code sets the * * configuration_st member to point to the * * controller structure for which an associated * * interrupt handler will be written. * ***************************************************/

info.configuration_st = (caddr_t)ctlr;

/*************************************************** * Specifies the driver type as a controller. * ***************************************************/

info.config_type = CONTROLLER_CONFIG_TYPE;

/**************************************************** * Specifies the interrupt handler. The interrupt * * handler for the /dev/none driver is called * * noneintr. * ****************************************************/

info.intr = noneintr;

/*************************************************** * This parameter will be passed to the driver's * * interrupt handler, which in this example is * * noneintr. * ***************************************************/

info.param = (caddr_t)unit;

/*************************************************** * Specifies bus registration information. In * * this example, the bus registration information * * is contained in the handler_intr_info * * structure. Thus, ih_bus_info is set to the * * address of this structure (info). * ***************************************************/

handler.ih_bus_info = (char *)&info;

/*************************************************** * Save the return id from handler_add. This id * * will be used later to deregister the interrupt * * handler. * * * * After saving the return id, call handler_add * * and handler_enable to register and enable the * * interrupt handler. * ***************************************************/

none_id_t[unit] = handler_add(&handler); if (none_id_t[unit] == NULL) {

return(0); /* Return failure status */ }

if (handler_enable(none_id_t[unit]) != 0) { handler_del(none_id_t[unit]); return(0); /* Return failure status */ } /*************************************************** * Increment the num_none variable to indicate * * that noneprobe probed at least one controller. * ***************************************************/ num_none++;

/***************************************************
 * Determine if the device is present by calling   *
 * the BADADDR interface.  If the device is        *
 * not present, return the value zero (0).         *
 * Otherwise, reset the device and assure that a   *
 * write to I/O space isi completed.               *
 *                                                 *
 * Note the check of the none_is_dynamic variable  *
 * to determine if this is the statically or       *
 * dynamically configured /dev/none driver.        *
 * You can call BADADDR only with statically       *
 * configured drivers.                             *
 ***************************************************/

if (none_is_dynamic == SUBSYSTEM_STATICALLY_CONFIGURED) { if (BADADDR( (caddr_t) reg + NONE_CSR, sizeof(long), NULL) !=0) { return (0); } }

     write_io_port(reg + NONE_CSR, 4,
                   0, DN_RESET); /* Reset the device */
     mb(); /* Ensure a write to I/O space is completed */ 

/*************************************************** * If the error bit is set, noneprobe returns the * * value zero (0) to the bus configuration code. * * Otherwise, it calls write_io_port to write to * * the CSR/LED register followed by a call to mb * * to ensure that a write to I/O space is * * completed. * ***************************************************/ if(read_io_port(reg + NONE_CSR, 4, 0) & DN_ERROR) { return (0); }

write_io_port(reg + NONE_CSR, 4, 0, 0); /* Write to the */ /* CSR/LED register */ mb(); /* Ensure a write to I/O space is completed */

/*************************************************** * Return a nonzero value. The device is present. * ***************************************************/ return (1); }

/*************************************************** * nonecattach() * *************************************************** * Name: nonecattach * * * * Arguments: * * * * ctlr Pointer to controller structure * * * * Kernel support interface calls: None * * * * Called by: * * * * o Bus configuration code (confl1) * * * * Return Codes: None * * * * Description: * * * * The bus configuration code's confl1 interface * * calls the driver's attach interface during * * dynamic and static configuration. The * * nonecattach interface does not currently * * perform any tasks (other than to return to * * the bus configuration code). It is provided * * here as a stub for future development. * ***************************************************/

nonecattach(ctlr) struct controller *ctlr; { /* Code for nonecattach interface goes here.*/ return; }

/*************************************************** * none_ctlr_unattach() * *************************************************** * Name: none_ctlr_unattach * * * * Arguments: * * * * bus Pointer to bus structure * * ctlr Pointer to controller structure * * * * Kernel support interface calls: * * * * o handler_disable * * o handler_del * * * * Called by: * * * * o Bus configuration code (confl1) * * * * Return Codes: * * * * Success: ESUCCESS * * * * Failure: ENODEV * * * * Description: * * * * A device driver's controller unattach interface * * is called indirectly from the bus configuration * * code's confl1 interface when a driver is being * * unloaded. The /dev/none driver's controller * * unattach interface is called * * none_ctlr_unattach. Its major task is to * * deregister its interrupt handler. * ***************************************************/

int none_ctlr_unattach(bus, ctlr) struct bus *bus; struct controller *ctlr; {

register int unit = ctlr->ctlr_num;

/*************************************************** * Validate the unit number. * ***************************************************/

if ((unit > num_none) || (unit < 0)) { return(ENODEV); /* Return error status */ }

/*************************************************** * A statically configured device does not need to * * call the handler_disable and handler_del * * interfaces. Thus, a check is performed on the * * none_is_dynamic variable to determine if this * * is the statically configured /dev/none driver. * ***************************************************/

if (none_is_dynamic == SUBSYSTEM_STATICALLY_CONFIGURED) { return(ENODEV); /* Return error status */ }

/*************************************************** * 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. * ***************************************************/

if (handler_disable(none_id_t[unit]) != 0) { return(ENODEV); /* Return error status */ }

if (handler_del(none_id_t[unit]) != 0) { return(ENODEV); /* Return error status */ } return(ESUCCESS); /* Return success status */ }

/***************************************************
 *           noneopen()                            *
 ***************************************************
 * Name:        noneopen                           *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 * dev    Major/minor device number                *
 * flag   Flags from /usr/sys/include/sys/file.h   *
 * format Format of special device                 *
 *                                                 *
 * Kernel support interface calls: None            *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 * The kernel calls the driver's open interface as *
 * a result of an open system call.                *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        ESUCCESS                   *
 *                                                 *
 *      Failure:        EBUSY                      *
 *                      ENODEV                     *
 *                      ENXIO                      *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * A device driver's open interface is called by   *
 * the kernel as a result of an open system call.  *
 * Its task is to open the specified device.       *
 *                                                 *
 * The open interface for the /dev/none driver is  *
 * called noneopen.  The noneopen interface checks *
 * to ensure that the open is unique, marks the    *
 * device as open, and returns the constant        *
 * ESUCCESS to indicate success.                   *
 ***************************************************/

noneopen(dev, flag, format) dev_t dev; int flag; int format; {

/*************************************************** * Perform the following initializations: * * * * o Initialize unit to the minor device number * * * * o Initialize the pointer to the controller * * structure associated with this NONE device * * * * o Initialize the pointer to the none_softc * * structure associated with this NONE * * device * ***************************************************/

register int unit = minor(dev); struct controller *ctlr = noneinfo[unit]; struct none_softc *sc = &none_softc[unit];

/*************************************************** * If the device does not exist, return no such * * device. * ***************************************************/

if(unit >= num_none) return (ENODEV);

/*************************************************** * Make sure the open is unique. * ***************************************************/

if (sc->sc_openf == DN_OPEN) return (EBUSY);

/*************************************************** * If the device is initialized, set sc_openf and * * return ESUCCESS to indicate success. Otherwise,* * the device does not exist. Return an error * * code. * ***************************************************/

if ((ctlr !=0) && (ctlr->alive & ALV_ALIVE)) { sc->sc_openf = DN_OPEN; return(ESUCCESS); }

/*************************************************** * Return an error code to indicate device does * * not exist. * ***************************************************/ else return(ENXIO); }

/***************************************************
 *           noneclose()                           *
 ***************************************************
 * Name:        noneclose                          *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 * dev    Major/minor device number                *
 * flag   Flags from /usr/sys/include/sys/file.h   *
 * format Format of special device                 *
 *                                                 *
 * Kernel support interface calls:                 *
 *                                                 *
 *      o mb                                       *
 *      o write_io_port                            *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 * The kernel calls the driver's close interface   *
 * as a result of a close system call.             *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        ESUCCESS                   *
 *                                                 *
 *      Failure:        Error number from errno.h  *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * A device driver's close interface is called by  *
 * the kernel as a result of a close system call.  *
 * Its task is to close the specified device.      *
 *                                                 *
 * The close interface for the /dev/none driver is *
 * called noneclose.  Specifically, the noneclose  *
 * interface turns off the open flag for the       *
 * specified NONE device.                          * 
 ***************************************************/

noneclose(dev, flag, format) dev_t dev; int flag; int format; {

/*************************************************** * Perform the following initializations: * * * * o Initialize unit to the minor device number * * * * o Initialize the pointer to the controller * * structure associated with this NONE device * * * * o Initialize the pointer to the none_softc * * structure associated with this NONE device * * * * o Initialize the I/O handle (the reg variable) * * to the address of the device registers (the * * addr member of the controller structure). * ***************************************************/

register int unit = minor(dev); struct controller *ctlr = noneinfo[unit]; struct none_softc *sc = &none_softc[unit]; register io_handle_t reg = (io_handle_t) ctlr->addr;

/*************************************************** * Turn off interrupts by calling write_io_port * * to write to the NONE_CSR register. * ***************************************************/

write_io_port(reg + NONE_CSR, 4, 0, 0);

/*************************************************** * Ensure that a write to I/O space is completed. * ***************************************************/

mb();

/**************************************************** * Turn off the open flag for the specified device. * ****************************************************/

sc->sc_openf = DN_CLOSE;

/*************************************************** * Return success. * ***************************************************/

return(ESUCCESS); }

/*************************************************** * noneread() * *************************************************** * Name: noneread * * * * Arguments: * * * * dev Major/minor device number * * uio Pointer to uio structure * * flag Access mode of device * * * * Kernel support interface calls: None * * * * Called by: * * * * The kernel calls the driver's read interface as * * a result of a read system call. * * * * Return Codes: * * * * Success: ESUCCESS * * * * Failure: An error number * * * * Description: * * * * A device driver's read interface is called by * * the kernel as a result of a read system call. * * Its main task is to read data from a device. * * * * The read interface for the /dev/none driver is * * called noneread. The noneread interface simply * * returns success to the read system call because * * the /dev/none driver always returns EOF on read * * operations. * * * ***************************************************/

noneread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { return (ESUCCESS); /* Return success */ }

/*************************************************** * nonewrite() * *************************************************** * Name: nonewrite * * * * Arguments: * * * * dev Major/minor device number * * uio Pointer to uio structure * * flag Access mode of device * * * * Kernel support interface calls: * * * * o panic * * * * Called by: * * * * The kernel calls the driver's write interface * * as a result of a read system call. * * * * Return Codes: * * * * Success: ESUCCESS * * * * Failure: An error number * * from errno.h * * * * Description: * * * * A device driver's write interface is called by * * the kernel as a result of a write system call. * * Its main task is to write data to a device. * * * * The write interface for the /dev/none driver is * * called nonewrite. The nonewrite interface * * copies data from the address space pointed to * * by the uio structure to the device. Upon a * * successful write, nonewrite returns ESUCCESS. * * * ***************************************************/

nonewrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag;

{

/*************************************************** * Perform the following initializations and * * declarations: * * * * o Initialize unit to the minor device number * * * * o Initialize the pointer to the controller * * structure associated with this NONE device * * * * o Initialize the pointer to the none_softc * * structure associated with this NONE device * * * * o Declare a count variable to store the * * size of the write request * * * * o Declare a pointer to an iovec structure * ***************************************************/

int unit = minor(dev); struct controller *ctlr = noneinfo[unit]; struct none_softc *sc = &none_softc[unit]; unsigned int count; struct iovec *iov;

/*************************************************** * While true, get the next I/O vector. * ***************************************************/

while(uio->uio_resid > 0) { iov = uio->uio_iov; if(iov->iov_len == 0) { uio->uio_iov++; uio->uio_iovcnt--; if(uio->uio_iovcnt < 0) panic("none write"); continue; }

/*************************************************** * Figure out how big the write request is. * ***************************************************/

count = iov->iov_len;

/*************************************************** * Note that the data is consumed. * ***************************************************/

iov->iov_base += count; iov->iov_len -= count; uio->uio_offset += count; uio->uio_resid -= count;

/*************************************************** * Count the bytes written. * ***************************************************/

sc->sc_count +=count; } return (ESUCCESS); }

/*************************************************** * noneintr() * *************************************************** * Name: noneintr * * * * Arguments: * * * * parameter Parameter registered with the * * handler_add interface * * * * Kernel support interface calls: None * * * * Called by: * * * * The kernel calls the driver's interrupt * * handler as a result of a hardware device * * interrupt. * * * * Return Codes: None * * * * Description: * * * * A device driver's interrupt handler is called * * by the kernel as a result of a hardware device * * interrupt. Its main task is to handle these * * hardware device interrupts. * * * * The interrupt handler for the /dev/none driver * * is called noneintr. Specifically, the noneintr * * interface does not perform any tasks because * * the /dev/none driver does control a real * * hardware device. Therefore, no interrupt can * * be generated. Thus, the interface is provided * * here as a stub for future development. * * * * NOTE * * * * All device drivers should register interrupt * * handlers by using the ihandler_t, * * handler_intr_info, and handler interfaces. In * * this case, the parameter passed to the driver's * * interrupt handler is the value you stored in * * the param member of the handler_intr_info * * structure. * ***************************************************/

noneintr(parameter) caddr_t parameter;

{

/*************************************************** * Declare and initialize structures * ***************************************************/

struct controller *ctlr = noneinfo[0]; /* Controller number */ /* not defined */ struct none_softc *sc = &none_softc[0]; /* Controller number */ /* not defined */

/* Code to perform the interrupt processing */

}

/*************************************************** * noneioctl() * *************************************************** * Name: noneioctl * * * * Arguments: * * * * dev Major/minor device number * * cmd The ioctl command * * data ioctl command-specific data * * flag Access mode of device * * * * Kernel support interface calls: None * * * * Called by: * * * * The kernel calls the driver's ioctl interface * * as a result of an ioctl system call. * * * * Return Codes: * * * * Success: ESUCCESS * * * * Failure: An error number from * * errno.h * * * * Description: * * * * A device driver's ioctl interface is called by * * the kernel as a result of an ioctl system call. * * Its main task is to perform general-purpose * * I/O control. * * * * The ioctl interface for the /dev/none driver is * * called noneioctl. Specifically, the noneioctl * * interface obtains and clears the count of bytes * * that was previously written by nonewrite. When * * a user program issues the command to obtain the * * count, noneioctl returns the count through the * * data pointer that the kernel passes to the * * noneioctl interface. When a user program asks * * to clear the count, the noneioctl interface * * does so. * ***************************************************/

noneioctl(dev, cmd, data, flag) dev_t dev; unsigned int cmd; caddr_t data; int flag;

{

/*************************************************** * Perform the following initializations and * * declarations: * * * * o Initialize unit to the minor device number. * * * * o Declare a pointer to variable that stores * * the character count. * * * * o Initialize the pointer to the none_softc * * structure associated with this NONE device. * ***************************************************/

int unit = minor(dev); int *res; struct none_softc *sc = &none_softc[unit];

/*************************************************** * For GETCOUNT operations, set the res variable * * to point to the kernel memory allocated by the * * ioctl system call. The ioctl system call * * copies the data to and from user address space. * ***************************************************/

res = (int *) data;

/*************************************************** * Save the count, if necessary. * ***************************************************/

if(cmd == DN_GETCOUNT) *res = sc->sc_count;

/*************************************************** * Clear the count, if necessary. * ***************************************************/

if(cmd == DN_CLRCOUNT) sc->sc_count = 0;

/*************************************************** * Success * ***************************************************/

return (ESUCCESS); }


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Chapter] [Index] [Help]


B.2    Source Listing for the /dev/edpseudo Device Driver

/***************************************************
 * edpseudo.c  Driver for edpseudo device          *
 *             13-April-1996                       *
 *                                                 *
 * The /dev/edpseudo device driver shows you how   *
 * to write a pseudodevice driver that can be      *
 * statically or dynamically configured into the   *
 * kernel.                                         *
 ***************************************************/

/***************************************************
 *        FEATURES OF THE /dev/edpseudo DRIVER     *
 ***************************************************
 *                                                 *
 * The following list highlights some features of  *
 * the /dev/edpseudo driver:                       *
 *                                                 *
 * o Single binary module                          *
 *                                                 *
 *   The /dev/edpseudo driver is written to        *
 *   produce a single binary module (the           *
 *   executable image).  This single binary        *
 *   module has a file extension of .mod.  You     *
 *   compile a device driver as a single binary    *
 *   module so that you can statically or          *
 *   dynamically configure it into a Digital       *
 *   UNIX kernel at any point in time.             *
 *                                                 *
 * o Static configuration                          *
 *                                                 *
 * The /dev/edpseudo driver's single binary module *
 * (the edpseudo.mod file) can be:                 *
 *                                                 *
 *    - Statically configured into a Digital UNIX  *
 *      kernel (/vmunix)                           *
 *                                                 *
 *    - Statically configured into a  boot-link    *
 *      kernel (a /sysconfigtab text file)         *
 *                                                 *
 *      Typically, only device driver writers      *
 *      testing foreign devices and systems        *
 *      engineers (Digital internal) testing new   *
 *      hardware platforms in the boot path before *
 *      creating kits use the boot-link kernel     *
 *      technology.                                *
 *                                                 *
 * o Dynamic configuration                         *
 *                                                 *
 * The /dev/edpseudo driver's single binary module *
 * (the edpseudo.mod file) can be dynamically      *
 * configured into a Digital UNIX kernel (/vmunix) *
 * when a user makes a request (by using the       *
 * sysconfig utility) at single user or multiuser  *
 * time.                                           * 
 ***************************************************/
/***************************************************
 *    INTERFACES that /dev/edpseudo IMPLEMENTS     *
 ***************************************************
 *                                                 *
 * o register_major_number                         *
 * o callback_register_major_number                *
 * o edpseudo_configure				   *
 * o edpseudoprobe                                 *
 * o edpseudoopen                                  *
 * o edpseudoclose                                 *
 * o edpseudoread                                  *
 * o edpseudowrite                                 *
 * o edpseudoioctl                                 *
 *                                                 *
 ***************************************************/
/***************************************************
 *            Include Files Section                *
 ***************************************************/

/***************************************************
 * Common driver header files                      *
 ***************************************************/
#include <sys/param.h> 
#include <sys/systm.h> 
#include <sys/ioctl.h> 
#include <sys/tty.h> 
#include <sys/user.h> 
#include <sys/proc.h> 
#include <sys/map.h> 
#include <sys/buf.h> 
#include <sys/vm.h> 
#include <sys/file.h> 
#include <sys/uio.h> 
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/conf.h>
#include <sys/kernel.h> 
#include <sys/devio.h> 
#include <hal/cpuconf.h> 
#include <sys/exec.h> 
#include <io/common/devdriver.h> 
#include <sys/sysconfig.h>
#include <kern/kalloc.h>

#include <machine/cpu.h> 
#include <io/ESA100/edpseudoreg.h>  /* Device register header file */ 

/****************************************************
 * The /dev/edpseudo driver's register_major_number *
 * interface uses the following #defines when       *
 * obtaining a major number.                        *
 ***************************************************/

#define NO_DEV -1
#define MAJOR_INSTANCE 1 

/***************************************************
 * Declarations Section                            *
 ***************************************************/

/***************************************************
 *        Bits used in read/write operations       * 
 ***************************************************/

#define DN_RESET 0001 /* Device ready for data transfer */
#define DN_ERROR 0002 /* Indicate error */ 

/***************************************************
 *       Defines for softc structure               * 
 ***************************************************/

#define DN_OPEN  1 /* Device open bit */
#define DN_CLOSE 0 /* Device close bit */ 

/***************************************************
 *     Forward declarations of driver interfaces   *
 ***************************************************/

int edpseudoopen(), edpseudoclose(), edpseudoread(),
edpseudowrite(); int edpseudoioctl(); 

/***************************************************
 * Declare softc structure                         *
 ***************************************************/

struct edpseudo_softc {
	int sc_openf; /* Open flag */ 
	int sc_count; /* Count of characters written to device */ 
	int sc_state; /* Device state, not currently used */ 
} edpseudo_softc[MAX_NNONE]; 

int edpseudo_config = FALSE;  /* State flags indicating driver */
                              /* configured */
int edpseudo_devno = NO_DEV; /* No major number assigned yet. */
int NNONE = 1;

/***************************************************
 * The following list describes the use of the     *
 * edpseudo_is_dynamic, callback_return_status,    *
 * and num_edpseudo variables:                     *
 *                                                 *
 *  o edpseudo_is_dynamic                          *
 *                                                 *
 *    The edpseudo_is_dynamic variable will be     *
 *    used to control any differences in tasks     *
 *    performed by the statically or dynamically   *
 *    configured /dev/edpseudo 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 callback_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/edpseudo driver.                        *
 *                                                 *
 *  o num_edpseudo                                 *
 *                                                 *
 *    The register_major_number interface uses the *
 *    num_edpseudo variable to perform a check if  *
 *    there are devices present.                   * 
 ***************************************************/

int edpseudo_is_dynamic = 0; 
int callback_return_status = ESUCCESS;

int num_edpseudo = 0;   /* Count on the number of controllers probed */

/**************************************************
 * The following code sets the members of the     *
 * the dsent structure to appropriate  values     *
 * for the /dev/edpseudo 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/edpseudo driver.     * 
 **************************************************/

struct dsent edpseudo_devsw_entry = {
        edpseudoopen,        /* d_open */
        edpseudoclose,       /* d_close */
        nodev,               /* d_strategy */
        edpseudoread,        /* d_read */
        edpseudowrite,       /* d_write */
        edpseudoioctl,       /* 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,          /* d_funnel */
        0,                   /* d_bflags */
        0,                   /* d_cflags */
}; 

/***************************************************
 * The following code declares variables that are  *
 * required by the cfgmgr framework.               *
 * You use these variables as fields in the        *
 * driver's attribute table.  The attribute table  *
 * for the /dev/edpseudo driver is called          *
 * edpseudo_attributes.                            * 
 ***************************************************/
static int majnum = 0;
static int dsflags = 0;
static int edpseudoversion = 0;
static int edpseudo_developer_debug = 0;
static unsigned char mcfgname[CFG_ATTR_NAME_SZ] = ; 
static unsigned char modtype[CFG_ATTR_NAME_SZ] = ;
static unsigned char devmajor[CFG_ATTR_NAME_SZ] = ;
static unsigned char unused[300] = ;
static unsigned char cma_dd[120] = ;

/***************************************************
 * 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   *
 * the /dev/edpseudo driver does not need.  The    *
 * The cfgmgr framework consumes these fields 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.                   *
 ***************************************************/

cfg_subsys_attr_t edpseudo_attributes[] = { 

/***********************************************
 * Declares an array of cfg_subsys_attr_t      *
 * structures and calls it edpseudo_attributes.*
 * The cfgmgr framework fills in the           * 
 * elements in edpseudo_attributes because     *
 * the operation types designated by these     *
 * are CFG_OP_CONFIGURE.  The cfgmgr framework *
 * reads the entries for the /dev/edpseudo     *
 * driver from the sysconfigtab database.      *
 * (A sysconfigtab file fragment was created   *
 * for the /dev/edpseudo driver.  The          *
 * sysconfigdb utility appends this            *
 * sysconfigtab file fragment to the           * 
 * sysconfigtab database.)                     *
 *                                             *
 * To determine if any of these element reads  *
 * failed, the /dev/edpseudo driver verifies   *
 * each from the cfg_attr_t structure passed   *
 * into the edpseudo_configure interface.      *
 *                                             *
 * Several of the following fields are used    *
 * in /dev/edpseudo 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.                            *
 ***********************************************/

/* Fields used in this driver. */
{"Module_Config_Name",  CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)mcfgname,2,CFG_ATTR_NAME_SZ,0},

{"Device_Char_Major",   CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)devmajor,0,CFG_ATTR_NAME_SZ,0},
{"CMA_Option",          CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)cma_dd,0,300,0},
{"numunit",             CFG_ATTR_INTTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE,
                        (caddr_t)&NNONE,0,MAX_NNONE,0},

/***************************************************
 *        NOTE                                     *
 *                                                 *
 * There are a number of fields (attributes) that  *
 * appear in the sysconfigtab file fragment that   *
 * are not needed by the /dev/edpseudo driver.     *
 * The cfgmgr framework 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.                   *
 *                                                 *
 * The following device driver-related attributes  *
 * are not used by the /dev/edpseudo driver and    *
 * are included in the attributes table to avoid   *
 * the warning messages.                           *
 ***************************************************/

{"Module_Path",         CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_Subdir",       CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_Block_Subdir", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_Char_Subdir",  CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_Major_Req",    CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_Block_Major",  CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_Block_Minor",  CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_Block_Files",  CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_Char_Major",   CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_Char_Minor",   CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_Char_Files",   CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_User",         CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_Group",        CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},
{"Device_Mode",         CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE,
                        (caddr_t)unused,0,300,0},

/**********************************************
 * The /dev/edpseudo device driver modifies   *
 * the following attributes during a configure*
 * or unconfigure operation.  The cfgmgr      *
 * framework 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},
{"dsflags",                CFG_ATTR_INTTYPE, CFG_OP_QUERY,
                           (caddr_t)&dsflags,0,8,0},
{"version",                CFG_ATTR_INTTYPE, CFG_OP_QUERY,
                           (caddr_t)&edpseudoversion,0,9999999,0},

/**********************************************
 * Device driver specific entries describing  *
 * configuration and operation of the driver  *
 * should go here.			      *
 *********************************************/

/**********************************************
 * The Edpseudo_Developer_debug attribute is  *
 * used with the printf interface to display  *
 * runtime messages on the console terminal.  *
 * A device driver writer might want to       *
 * define such an attribute to collect        *
 * detailed information relating to a driver  *
 * problem.  When the attribute is set to 1   *
 * by the cfgmgr framework (through the       *
 * CFG_OP_RECONFIGURE operation) or           *
 * initialized by sysconfigtab in the         *
 * bootpath, these messages appear on the     *
 * console terminal.  These messages can      *
 * provide the driver writer with             *
 * configuration information that relates to  *
 * detailed bus configuration information.    *
 *                                            *
 * The Edpseudo_Developer_debug attribute     *
 * (or another attribute similar to it) is    *
 * used for detailed internal debugging of    *
 * code by Digital personnel or someone who   *
 * has a source license.                      *
 *                                            *
 * Note that the Edpseudo_Developer_debug     *
 * attribute's operations are query,          *
 * reconfigure, and configure.                *
 *                                            *
 * edpseudo_developer_debug = 1 turns on      *
 *                       these messages       *
 *                                            *
 * edpseudo_developer_debug = 0 turns off     *
 *                       these messages       *
 *                                            *
 * You also need to use the #ifdef and        *
 * #endif statements as follows:              *
 *                                            *
 *                                            *
 *  #ifdef NONE_DEBUG                         *
 *  driver code                               *
 *  #endif                                    *
 **********************************************/

{"Edpseudo_Developer_Debug",   CFG_ATTR_INTTYPE, CFG_OP_QUERY |
			   CFG_OP_RECONFIGURE | CFG_OP_CONFIGURE,
                        (caddr_t)&edpseudo_developer_debug,0,1,0},
{,0,0,0,0,0,0}
};

/***************************************************
 *        register_major_number()                  *
 ***************************************************
 * Name:  register_major_number                    *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 *                                                 *
 * Kernel support interface calls:                 *
 *                                                 *
 *      o atoi                                     *
 *      o cfgmgr_set_status                        *
 *      o atoi                                     *
 *      o atoi                                     *
 *      o devsw_reserve				   *
 *      o devsw_add                                *
 *      o strcmp                                   *
 *      o strcpy                                   *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 *          o cfgmgr framework                     *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        ESUCCESS                   *
 *                                                 *
 *      Failure:        EBUSY                      *
 *                      EINVAL                     *
 *                      ESRCH                      *
 *                                                 *
 * Description:                                    *
 *                                                 *
 ***************************************************/
int
register_major_number()
{


/**********************************************
 * If no devices are present upon completion  *
 * of device driver configuration, set the    *
 * cfgmgr status unconfigured and exit the    *
 * callback interface.                        *
 *********************************************/

     if (num_edpseudo == 0) {
         return (ENODEV);
      }

/*
 * This call to devsw_add is responsible for adding
 * the device driver information to the device switch table.
 * Each allocation provides a bdev and cdev device entry
 * for the device table.
 *
 */

     majnum = devsw_add(mcfgname, MAJOR_INSTANCE, 
		        majnum, &edpseudo_devsw_entry); 

     if (majnum == NO_DEV) {

/***************************************************
 *            The call to devsw_add could fail if *
 *            the driver is requesting a specific  *
 *            major number and that number is      *
 *            currently in use, or if the devsw   *
 *            table is currently full.             *
 ***************************************************/

           return (ENODEV);
     } 

/***************************************************
 *            Stash away the dev_t so that it can  *
 *            be used later to unconfigure the     *
 *            device.  Save off the minor number   *
 *            information.  This will be returned  *
 *            by the query call.                   *
 ***************************************************/

     edpseudo_devno = majnum; 

/***************************************************
 *            This member indicates that the       *
 *            beginning minor number will be       *
 *            zero (0).                            *
 ***************************************************/

     begunit = 0; 

/***************************************************
 *           This is a character driver.  For this *
 *           reason no block major number is       *
 *           assigned.                             *
 ***************************************************/

     edpseudo_config = TRUE; 

     return(ESUCCESS);
}

/***************************************************
 *     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.                            *
 *                                                 *
 ***************************************************/
void
void
callback_register_major_number(point, order, args, event_arg)
 int point;
 int order;
 ulong args;
 ulong event_arg;
{
int  status;

/*
 *   Detect if a previous failure has occured in
 * statically configuring this driver. If so, the
 * exit the callback without doing any configuration
 * work.
 */

   if(callback_return_status != ESUCCESS)
      return;

/*
 *  Register this drivers configuration with the
 * kernel's topology.
 */

   status  = register_major_number();

/*
 *   If configuration registration is successful then
 * just return from this callback. Else signal the
 * cfgmgr that this driver has failed to configure and
 * set the 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) {
        cfgmgr_set_status(mcfgname);
        callback_return_status = status;
      return;
   }
}

/***************************************************
 *           edpseudo_configure()                      *
 ***************************************************
 * Name:        edpseudo_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         *
 *                                                 *
 * Kernel support interface calls:                 *
 *                                                 *
 *      o atoi                                     *
 *      o bcopy                                    *
 *      o devsw_add                                *
 *      o devsw_del                                *
 *      o cfgmgr_get_state                         *
 *      o configure_driver                         *
 *      o makedev                                  *
 *      o printf                                   *
 *      o register_callback                        *
 *      o register_major_number                    *
 *      o callback_register_major_number           *
 *      o strcmp                                   *
 *      o strcpy                                   *
 *      o unconfigure_driver                       *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 *          o cfgmgr framework                     *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        ESUCCESS                   *
 *                                                 *
 *      Failure:        EBUSY                      *
 *                      EINVAL                     *
 *                      ESRCH                      *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * The edpseudo_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 edpseudo_configure interface cooperates with*
 * the cfgmgr framework to complete configure,     *
 * unconfigure, query, and other requests.         *
 * These requests are differentiated by the "op"   *
 * argument.                                       *
 *                                                 *
 * Specifically, the edpseudo_configure interface  *
 * shows the code a device driver must supply to   *
 * produce a single binary module. This code       *
 * allows the /dev/edpseudo driver to be loaded as *
 * a static or dynamic driver.                     *
 ***************************************************/

edpseudo_configure(op, indata, indatalen, outdata, outdatalen)
   cfg_op_t op;        
   cfg_attr_t *indata;       
   size_t indatalen;         
   cfg_attr_t *outdata;      
   size_t outdatalen;        
{
        int     retval; 
        int     i;      
        int     driver_cfg_state;

        switch(op)
              case CFG_OP_CONFIGURE: 
#ifdef NONE_DEBUG
printf("edpseudo_configure: CFG_OP_CONFIGURE.\n");
#endif /* NONE_DEBUG */

        /*
         * If this device driver has already been configured
         * either statically or dynamically then return this
         * edpseudo_configure call indicating an error.
         */
              if(edpseudo_config == TRUE)
                 return(EINVAL); 

/***************************************************
 * The following code performs a check on the      *
 * configuration name.  If the configuration name  *
 * is NULL, subsequent code uses the controller    *
 * name stored in the driver structure's ctlr_name *
 * member.  The driver structure for /dev/edpseudo *
 * driver is called edpseudodriver. This structure *
 * was declared and initialized to appropriate     *
 * values in the Declarations Section of the       *
 * /dev/edpseudo 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.
                  */
                 strcpy(mcfgname,"edpseudo");
                 }
                 else { 
                      /* Module_Config_Name attribute (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){
                   return(EINVAL);  /* CFGMGR fatal error determining */
		}

              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 of this subsystem should still be configured
                  * in the boot path.
                  */

                 callback_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 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 the cfgmgr framework
                  * through the cfgmgr_set_state interface.
                  */

                 edpseudo_is_dynamic = SUBSYSTEM_STATICALLY_CONFIGURED;

                 register_callback( callback_register_major_number,
                                    CFG_PT_POSTCONFIG,
                                    CFG_ORD_NOMINAL, (long) 0 );
              }
              else {

                 /* 
                  * During Dynamic Loadable configuration the cfgmgr is
                  * present when the following functionality is executed.
                  * The error returns here will signal the cfgmgr that
                  * the subsystems configuration has failed.
                  */

	         edpseudo_is_dynamic = SUBSYSTEM_DYNAMICALLY_CONFIGURED;

                 retval = register_major_number();
                 if(retval != ESUCCESS)
                    return(retval);

               }
             break;

/***************************************************
 * Unconfigure (unload) the driver.                *
 ***************************************************/

              case CFG_OP_UNCONFIGURE: 

/***************************************************
 *              DEBUG STATEMENT                    *
 ***************************************************/
#ifdef NONE_DEBUG
printf("edpseudo_configure: CFG_OP_UNCONFIGURE.\n");
#endif /* NONE_DEBUG */
/***************************************************
 *          Fail the unconfiguration if the driver *
 *          is not currently configured as a       *
 *          dynamic subsystem. Statically          *
 *          configured subsystems CANNOT be        *
 *          unconfigure/unloaded.                  *
 *                                                 *
 *          Static subsystems will all physically  *
 *          remain in the kernel whether or not    *
 *          they are configured. Dynamic subsystem *
 *          when they are unconfigured are also    *
 *          unloaded from the kernel.              *
 ***************************************************/

                if(edpseudo_is_dynamic == SUBSYSTEM_STATICALLY_CONFIGURED)
		{
                    return(ESUCCESS);
                } 

/***************************************************
 *          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_edpseudo; i++) {
                        if (edpseudo_softc[i].sc_openf != 0) {
                                return(EBUSY);
                        } 
                }

/***************************************************
 *        Call devsw_del to remove the driver      *
 *        entry points from the in-memory resident *
 *        devsw 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) {
                        return(ESRCH);
                }  

#ifdef NONE_DEBUG
printf("edpseudo_configure:unconfigure_driver failed.\n");
#endif /* NONE_DEBUG */

                edpseudo_is_dynamic = 0; 
                edpseudo_config = FALSE; 
                break;

/**************************************************
 *  Code to perform the tasks associated with a   *
 *  system manager request to reconfigure the     *
 *  currently loaded device driver.               *
 **************************************************/

              case CFG_OP_RECONFIGURE: 
              break;

/**************************************************
 *  Requests to query a dynamically configured    *
 *  device driver will succeed only if the        *
 *  CFG_OP_QUERY: entry point returns success.    *
 **************************************************/

              case CFG_OP_QUERY: 
              break;

              default: /* Unknown operation type */
                return(ENOTSUP); 
        }

/***************************************************
 *       The driver's configure interface has      *
 *       completed successfully.  Return a success *
 *       status.                                   *
 ***************************************************/

        return(ESUCCESS); 
}

/***************************************************
 *           edpseudoopen()                        *
 ***************************************************
 * Name:        edpseudoopen                       *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 * dev    Major/minor device number                *
 * flag   Flags from /usr/sys/include/sys/file.h   *
 * format Format of special device                 *
 *                                                 *
 * Kernel support interface calls: None            *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 * The kernel calls the driver's open interface as *
 * a result of an open system call.                *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        ESUCCESS                   *
 *                                                 *
 *      Failure:        EBUSY                      *
 *                      ENODEV                     *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * A device driver's open interface is called by   *
 * the kernel as a result of an open system call.  *
 * Its task is to open the specified device.       *
 *                                                 *
 * The open interface for the /dev/edpseudo driver *
 * is called edpseudoopen.  The edpseudoopen       *
 * interface checks to ensure that the open is     *
 * unique, marks the device as open, and returns   *
 * the constant ESUCCESS to indicate success.      *
 **************************************************/


edpseudoopen(dev, flag, format)
dev_t dev;  
int flag;   
int format; 
{
/***************************************************
 * Perform the following initializations:          *
 *                                                 *
 *  o Initialize unit to the minor device number   *
 *  o Initialize the pointer to the controller     *
 *      structure associated with this NONE device *
 *  o Initialize the pointer to the edpseudo_softc * 
 *      structure associated with this NONE        *
 *      device                                     *
 ***************************************************/
	register int unit = minor(dev);            
	struct controller *ctlr = edpseudoinfo[unit];  
	struct edpseudo_softc *sc = &edpseudo_softc[unit]; 

/***************************************************
 * Make sure the open is unique.                   *
 ***************************************************/
	if (sc->sc_openf == DN_OPEN)
		return (EBUSY); 
        return(ESUCCESS);
}

/***************************************************
 *           edpseudoclose()                       *
 ***************************************************
 * Name:        edpseudoclose                      *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 * dev    Major/minor device number                *
 * flag   Flags from /usr/sys/include/sys/file.h   *
 * format Format of special device                 *
 *                                                 *
 * Kernel support interface calls: None            *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 * The kernel calls the driver's close interface   *
 * as a result of a close system call.             *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        ESUCCESS                   *
 *                                                 *
 *      Failure:        Error number from errno.h  *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * A device driver's close interface is called by  *
 * the kernel as a result of a close system call.  *
 * Its task is to close the specified device.      *
 *                                                 *
 * The edpseudoclose interface turns off the open  *
 * flag for the specified pseudodevice.            *
 ***************************************************/

edpseudoclose(dev, flag, format)
dev_t dev;  
int flag;   
int format; 
{

/****************************************************
 * Turn off the open flag for the specified device. *
 ****************************************************/
	sc->sc_openf = DN_CLOSE; 
        return (ESUCCESS); 
}

/***************************************************
 *           edpseudoread()                        *
 ***************************************************
 * Name:        edpseudoread                       *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 * dev           Major/minor device number         *
 * uio           Pointer to uio structure          *
 * flag          Access mode of device             *
 *                                                 *
 * Kernel support interface calls: None            *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 * The kernel calls the driver's read interface as *
 * a result of a read system call.                 *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        The number of bytes        *
 *                      actually read.             *
 *                                                 *
 *      Failure:        An error number            *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * A device driver's read interface is called by   *
 * the kernel as a result of a read system call.   *
 * Its main task is to read data from a device.    *
 *                                                 *
 * The edpseudoread interface simply returns       *
 * success because the /dev/edpseudo driver always *
 * returns EOF on read operations.                 * 
 ***************************************************/

edpseudoread(dev, uio, flag)
dev_t dev;       
struct uio *uio; 
int flag; 
{
	return (ESUCCESS); /* Return success */
}

/***************************************************
 *           edpseudowrite()                       *
 ***************************************************
 * Name:        edpseudowrite                      *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 * dev           Major/minor device number         *
 * uio           Pointer to uio structure          *
 * flag          Access mode of device             *
 *                                                 *
 * Kernel support interface calls:                 *
 *                                                 *
 *      o panic                                    *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 * The kernel calls the driver's write interface   *
 * as a result of a read system call.              *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        ESUCCESS                   *
 *                                                 *
 *      Failure:        An error constant          *
 *                      from errno.h               *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * A device driver's write interface is called by  *
 * the kernel as a result of a write system call.  *
 * Its main task is to write data to a device.     *
 *                                                 *
 * The edpseudowrite interface copies data from    *
 * the address space pointed to by the uio         *
 * structure to the device.  Upon a successful     *
 * write, edpseudowrite returns the value zero (0) *
 * to the write system call.                       *
 ***************************************************/

edpseudowrite(dev, uio, flag)
dev_t dev;        
struct uio *uio;  
int flag; 
{
/***************************************************
 * Perform the following initializations and       *
 * declarations:                                   *
 *                                                 *
 *  o Initialize unit to the minor device number   *
 *  o Initialize the pointer to the controller     *
 *    structure associated with this NONE device   *
 *  o Initialize the pointer to the edpseudo_softc *
 *    structure associated with this NONE device   *
 *  o Declare a count variable to store the        *
 *    size of the write request                    *
 *  o Declare a pointer to an iovec structure      *
 ***************************************************/
	int unit = minor(dev);                    
	struct controller *ctlr = edpseudoinfo[unit]; 
	struct edpseudo_softc *sc = &edpseudo_softc[unit]; 
	unsigned int count;                        
	struct iovec *iov;                         
/***************************************************
 * While true, get the next I/O vector.            *
 ***************************************************/
	while(uio->uio_resid > 0) {     
		iov = uio->uio_iov;     
		if(iov->iov_len == 0) { 
			uio->uio_iov++;
			uio->uio_iovcnt--;
			if(uio->uio_iovcnt < 0) 
				panic("edpseudo write");
			continue;
		}
/***************************************************
 * Figure out how big the write request is.        *
 ***************************************************/
	count = iov->iov_len; 
/***************************************************
 * Note that the data is consumed.                 *
 ***************************************************/
	iov->iov_base += count; 
	iov->iov_len -= count; 
	uio->uio_offset += count; 
	uio->uio_resid -= count; 
/***************************************************
 * Count the bytes written.                        *
 ***************************************************/
	sc->sc_count +=count; 
	}
	return (ESUCCESS);
}

/***************************************************
 *           edpseudoioctl()                       *
 ***************************************************
 * Name:        edpseudoioctl                      *
 *                                                 *
 * Arguments:                                      *
 *                                                 *
 * dev           Major/minor device number         *
 * cmd           The ioctl command                 *
 * data          ioctl command-specific data       *
 * flag          Access mode of device             *
 *                                                 *
 * Kernel support interface calls: None            *
 *                                                 *
 * Called by:                                      *
 *                                                 *
 * The kernel calls the driver's ioctl interface   *
 * as a result of an ioctl system call.            *
 *                                                 *
 * Return Codes:                                   *
 *                                                 *
 *      Success:        ESUCCESS                   *
 *                                                 *
 *      Failure:        An error number from       *
 *                      errno.h                    *
 *                                                 *
 * Description:                                    *
 *                                                 *
 * A device driver's ioctl interface is called by  *
 * the kernel as a result of an ioctl system call. *
 * Its main task is to perform general-purpose     *
 * I/O control.                                    *
 *                                                 *
 * The edpseudoioctl interface obtains and clears  *
 * the count of bytes that was previously written  *
 * by edpseudowrite.  When a user program issues   *
 * the command to obtain the count, edpseudoioctl  *
 * returns the count through the data pointer that *
 * the kernel passes to the edpseudoioctl          *
 * interface.  When a user program asks to clear   *
 * the count, the edpseudoioctl interface does so. *
 ***************************************************/

edpseudoioctl(dev, cmd, data, flag)
dev_t dev;             
unsigned int cmd;      
caddr_t data;          
int flag;              
{

/***************************************************
 * Perform the following initializations and       *
 * declarations:                                   *
 *  o Initialize unit to the minor device number   *
 *  o Declare a pointer to variable that stores    *
 *    the character count                          *
 *  o Initialize the pointer to the edpseudo_softc *
 *    structure associated with this NONE devicei  * 
 ***************************************************/

	int unit = minor(dev); 
	int *res; 
	struct edpseudo_softc *sc = &edpseudo_softc[unit]; 

/***************************************************
 * For GETCOUNT operations, set the res variable   *
 * to point to the kernel memory allocated by the  *
 * ioctl system call.  The ioctl system call       *
 * copies the data to and from user address space. *
 ***************************************************/

	res = (int *) data; 

/***************************************************
 * Save the count, if necessary.                   *
 ***************************************************/

	if(cmd == DN_GETCOUNT)
		*res = sc->sc_count; 

/***************************************************
 * Clear the count, if necessary.                  *
 ***************************************************/

	if(cmd == DN_CLRCOUNT)
		sc->sc_count = 0;  

/***************************************************
 * Success                                         *
 ***************************************************/

	return (ESUCCESS); 
}