This appendix contains the source listings for the following device drivers:
This source listing shows how to write the /dev/none device driver as though it controlled a real hardware device controller that connects to a TURBOchannel bus. For an example of how to implement a probe interface so that a driver can operate on multiple buses, see Section 7.1.4. If you would prefer to study the /dev/none driver with explanations that expand on the inline comments, see the chapters in Part 3.
This source listing shows a pseudodevice driver that the driver writers at EasyDriver Incorporated implement to test static and dynamic configuration. A pseudodevice driver does not control a hardware device controller and, therefore, does not operate on a bus.
/*************************************************** * * * 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); }
/*************************************************** * 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); }