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);
}