Jump to content United States-English
HP.com Home Products and Services Support and Drivers Solutions How to Buy
» Contact HP
More options
HP.com home
Managing and Developing Dynamically Loadable Kernel Modules > Chapter 1 Managing and Developing Dynamically Loadable Kernel Modules

Developing Dynamically Loadable Kernel Modules

» 

Technical documentation

Complete book in PDF
» Feedback
Content starts here

 » Table of Contents

 » Index

This section explains the process of writing modules in the DLKM format and provides background information specific to device driver development. It focuses on the writing and installation of loadable device drivers because they constitute the majority of supported DLKM modules for HP-UX 11.0 and later releases.

This section is intended primarily for programmers who want to write DLKM modules and/or convert existing static (non-loadable) modules to the DLKM format. It is assumed that the reader has a good understanding of the HP-UX operating system, the C programming language, and the writing and testing of static device drivers.

Since kernel modules written to the DLKM specification can either be configured as dynamically loadable or statically linked, device driver developers must write their DLKM modules to accommodate either configuration.

Writing modules in the DLKM format requires writing additional module initialization code called wrapper code, which enables the DLKM infrastructure to logically connect and disconnect a loadable module to and from the running kernel. Existing traditional device drivers that are going to be configured into the system as loadable modules must be converted to the DLKM format, and must be re-packaged according to the module packaging architecture introduced in HP-UX 11.0.

NOTE: For Itanium-based systems, boot time loading during phase BOOT1 is available for miscellaneous modules only. For phase BOOT2, other supported module types such as WSIO and Streams drivers may be loaded.

This section covers the following topics:

Overview of Boot Time Loading

Dynamically Loadable Kernel Modules were introduced in HP-UX 11.0. This feature allowed kernel modules to be loaded or unloaded dynamically without rebooting. However, loading and unloading of modules was allowed only during system run-time. This prevented very low-level kernel functions that are needed at early system boot from being implemented as DLKM modules.

With this release the Boot Time Loading feature eliminates this limitation for the Itanium-based platform. The HP-UX boot process can be divided into two parts, the architecture-dependent part and the architecture-independent part. The architecture-dependent part is referred to as the boot phase 1, and the architecture-independent part is referred to as boot phase 2. More precisely, boot phase 1 is the time when the kernel does not use pfdat (virtual memory page management table) based memory allocation system, and boot phase 2 is the time when the kernel begins to use the regular virtual memory allocation system that is used during system run-time

When you configure a module to be loaded during boot time, it is necessary to specify the phase in which you want to load the module. A module can specify two kinds of loading phases: “configured loading phase” and “supported loading phase.

The supported loading phase specifies the capability of a module to be loaded in a certain phase of the booting process. The supported loading phase for a module is specified in the module's master file. A module can have more than two supported loading phases defined concurrently. If no phases are specified in the master file, the module is loaded during run-time.

The configured loading phase specifies the phase in which the module needs to be loaded. The configured loading phase for a module is specified in its system file and the valid values are as follows:

BOOT1

attempt to load the module during boot phase 1

BOOT2

attempt to load the module during boot phase 2

INIT

kminit rc script will attempt to load the module

AUTO

the module remains ready to load on demand or via the DLKM auto-load mechanism

The module can specify BOOT1 or BOOT2 for its configured loading phase only when it also has BOOT1 or BOOT2 (or both) specified in its supported loading phase. Otherwise the module configuration process will complain about this module.

NOTE: Only miscellaneous modules (MISC) can be registered during boot phase 1 on Itanium-based systems. Other DLKM types supported in HP-UX 11i Version 1.5 will not work properly in early boot. When their type-specific registration is performed, the needed functionality will not be available during the early phases of booting, (i.e., streams, wsio, etc.).

Modules configured as INIT will attempt to be loaded by the kminit rc script. This is identical to the procedure for DLKMs in the HP-UX 11.0 release, which attempts to load the modules that are registered in the /etc/loadmods file.

Modules configured as AUTO are not loaded automatically in any of the phases unless they are referenced as a dependent of another module(s). If they are not referenced during the boot or init phases as a dependent, then they will be registered during boot phase 2. However they will not be explicitly loaded when the system is re-booted. They will remain ready to load on demand (kmadmin -L), or on access, for module types supporting the auto-load mechanism.

Module dependency

If a module requires another module in order to work properly, you can specify a dependency between them. The module loader attempts to load all the dependent modules when it finds dependencies during loading of a module. This module dependency mechanism is also applicable to Boot Time Loading.

For the system to work properly, the module developer or system administrator needs to exercise care in determining the configured loading phase and the supported loading phase of modules. A module without BOOT1 or BOOT2 specified in its supported loading phase would not be registered during boot phase 1 or boot phase 2. Such modules cannot be loaded in those phases even if they are defined as dependents of other boot-time-loaded modules. A module loading failure in boot time may be critical for your system if the failed module provides essential functionality for your system (i.e., SCSI disk driver).

If there is a possibility that the module will be referred to as a dependent during boot time, then the module must include BOOT1 or BOOT2 in the supported loading phase specification.

Module Registration

All modules that have BOOT1 in their supported loading phase specification are registered in system boot phase 1. All other modules are registered during boot phase 2, regardless of the values specified in their supported loading phase.

NOTE: Only miscellaneous modules (MISC) can be registered during boot phase 1 on Itanium-based systems. Other DLKM types supported in HP-UX 11i Version 1.5 will not work properly in early boot. When their type-specific registration is performed, the needed functionality will not be available during the early phases of booting, (i.e., streams, wsio, etc.).

Module Loading

A module having a configured loading phase of BOOT1 will be loaded just after DLKM has initialized in boot phase 1. This is later than initialization steps for spinlocks and the primitive memory allocation method used by boot phase 1, but prior to beginning I/O initialization.

A module having BOOT2 as its configured loading phase will be loaded just after the statically linked device drivers have been initialized. Any modules either referenced by an auto-loading stub linked into the static kernel, or depended-upon by other modules loaded during the boot process, are loaded when they are referenced at boot time. They must, however, be configured with the applicable loading phase in their supported loading phase specification.

Module Component Files

A DLKM module must be modularly packaged (see “Managing Dynamically Loadable Kernel Modules”). A DLKM module is installed on disk as a set of files, which includes the module's object file and other configuration files needed to install the module into the system. The files are located in a single directory having an arbitrary name. The kminstall(1M) command must then be used to copy the module's installation component files into the locations at which the kernel configuration tools expect to find them.

The component files for a DLKM module are:

  • mod.o - the object file for the module

  • master - the master configuration file for the module

  • system - the system configuration file for the module

  • space.h (optional) - a configuration file that allocates and initializes some module variables (tunable parameters)

  • Modstub.o (optional) - an object file used by the DLKM stubs mechanism

mod.o File Definition

The C program source code for a DLKM module, which is compiled and possibly linked to become mod.o, is similar to the source code for a static module except that it contains the following additional information:

  • The source code for a DLKM module includes two additional header files, <mod_conf.h> and <sys/moddefs.h>.

  • The source code for a DLKM module contains two additional sections of code, one for the module's wrapper and one for the module's _load() and _unload() functions (routines).

Module Wrapper

The source code for a DLKM module must contain a wrapper, defined by the modwrapper structure, specifying the _load() and _unload() function entry points and other data structures used by the module. The name of the modwrapper structure for a DLKM module must be module_name_wrapper.

The wrapper data structures are partially initialized by the DLKM infrastructure using values taken from the module's configuration files. These structures provide information needed during loading and unloading, such as the values needed to populate a loadable driver's device switch table entries for the major device number it supports.

The code definition of the modwrapper structure is defined in header file moddefs.h and shown below. The data structures and #define statement preceding the modwrapper structure are also defined in moddefs.h. The modwrapper structure contains a pointer to the modlink structure, and the modlink structure contains pointers to the mod_operations and mod_type data structures. The #define statement indicates the revision (version) number of the modwrapper structure, which is 1.0 for HP-UX 11.0.

NOTE: The modwrapper revision numbers will be used to maintain backward/forward compatibility of DLKM modules within the DLKM infrastructure.

Example 1-1 An Extract from moddefs.h File Showing modwrapper Structure

#define MODREV  10

struct modlink {
struct mod_operations *ml_ops;
void *ml_type_data;
};

/*
* Module type specific linkage structure.
*/

struct mod_type_data {
char *mtd_info;
void *mtd_pdata;
};

struct modwrapper {
int mw_rev;
int (*mw_load)();
int (*mw_unload)();
void (*mw_halt)();
void *mw_conf_data;
struct modlink *mw_modlink;
};

The elements of the modwrapper structure are:

  • mw_rev - module revision number; use MODREV in your module wrapper

  • mw_load - pointer to module's _load() function

  • mw_unload - pointer to module's _unload() function

  • mw_halt - currently unused; use (void (*)())NULL in your module wrapper

  • mw_conf_data - pointer to configuration data created by configuration tool config(1M)

  • mw_modlink - pointer to a modlink array, which is a NULL terminated array of modlink structures that specify the type-specific operations required by the module

The elements of the modlink structure are:

  • ml_ops - pointer to a mod_operations structure that depends on the type of DLKM module being defined:

    Table 1-3 Title not available (Module Wrapper)

    Module OperationsDLKM Module Type
    gio_mod_opsWSIO class driver
    gio_mod_opsWSIO interface driver
    str_drv_opsSTREAMS driver
    str_mod_opsSTREAMS module
    mod_misc_opsMiscellaneous module

     

  • ml_type_data - pointer to a mod_type_data structure

The elements of the mod_type_data structure are:

  • mtd_info - information string returned to modstat() system call

  • mtd_pdata - pointer to type-specific data that depend on the type of loadable module being defined: for both STREAMS drivers and STREAMS modules, use a pointer to the streams_info_t structure; for all other types, use (void *)NULL

A sample wrapper for a WSIO driver is as follows:

Example 1-2 WSIO Driver Wrapper—Example

/*
* Wrapper Table
*/
extern struct mod_operations gio_mod_ops;
static drv_info_t module_name_drv_info;
extern struct mod_conf_data module_name_conf_data;

/* module type specific data */
static struct mod_type_data module_name_drv_link = {
"module_name - Loadable/Unloadable Test Module",
(void *)NULL
};

static struct modlink module_name_mod_link[] = {
{ &gio_mod_ops, (void *)&module_name_drv_link },
/* WSIO */
{ NULL, (void *)NULL }
};

struct modwrapper module_name_wrapper = {
MODREV,
module_name_load,
module_name_unload,
(void (*)())NULL,
(void *)&module_name_conf_data,
module_name_mod_link
};

When a DLKM module is configured as statically linked, its wrapper is not used by the system.

Load Function

int module_name_load (void *arg);

A module's _load() function is called by the DLKM infrastructure whenever the module is loaded from disk into active memory. The function may be given any name (typically module_name_load); a pointer to the _load() function is obtained from the module's wrapper.

The _load() function must perform any initialization tasks required by the module before the module is logically connected to the kernel. Typical initialization tasks include acquiring private memory for the module and initializing devices and data structures.

If the module is unable to initialize itself, the _load() function must free any memory that it allocated and undo any other action that it took prior to the failure including canceling all outstanding calls to timeout. The _load() function should return 0 on success and an errno value on failure.

The argument passed to the function is type-specific. The use of this argument is described in “Initializing and Terminating DLKM Modules”.

Unload Function

int module_name_unload (void *arg);

The _unload() function is called by the DLKM infrastructure whenever the module is about to be removed from active memory. The function may be given any name (typically module_name_unload); a pointer to the _unload() function is obtained from the module's wrapper.

The _unload() function must clean up any resources that were allocated to the module, and it must remove all references to the module. Typical cleanup tasks include releasing private memory acquired by the module, removing device interrupts, disabling interrupts from the device, and canceling any outstanding timeout requests made by the module.

The module's _unload() function should return 0 on success and an errno value on failure. In the event of failure, the function must leave the module in a sane state, since the module will remain loaded after the return.

The system will never attempt to unload a module that it thinks is busy. However, the system cannot determine under all cases when the module is in use. Currently, a module is considered to be busy when another module that depends on it is also loaded. In addition, WSIO class drivers and STREAMS drivers track the open() and close() calls; these types of modules are busy whenever there is at least one open on the device using the driver. Under most other circumstances, the module must determine for itself whether it is appropriate for it to be unloaded. When a module is still in use, its _unload() function can return a nonzero value to cancel the unload.

The argument passed to the _unload() function by the DLKM infrastructure is the same type-specific value that was passed to the module's _load() function. The use of this argument is described in “Initializing and Terminating DLKM Modules”.

master File Definition

Each DLKM module has its own master file. The format of the master file includes the following section keywords:

  • $VERSION - indicates the version number for the file format. Version is defined as an integer. A single line containing the version is entered. For PA systems, the only supported version is 1.

    For Itanium-based systems, version 2 is a supported file format for Boot Time Loaded modules. If $VERSION is 2 in the master and system files the phases keyword in the master file and the $LOADPHASE keyword in the system file would need to be specified.

  • $LOADABLE - indicates that the module is dynamically loadable. If this section keyword does not exist, the module can only be statically configured into the kernel. Optionally, this section may contain either or both of two keywords:

    stub

    which, by its presence, indicates that the module has stub.

    phases n

    where n is figured by computing the logical OR of the following mask value:

    0x01

    Phase 1 of the boot sequence

    0x02

    Phase2 of the boot sequence

    0x04

    Runtime

    The absence of this keyword indicates that the module supports only runtime loading (equivalent to phases 0x04).

  • $INTERFACE - identifies the interface names and versions on which the module is built. For HP-UX 11.0 and later releases, enter a single line containing the word base.

  • $TYPE - indicates the module type and the type specific information. Valid types are wsio_class, wsio_intfc, streams_mod, streams_drv, and misc.

  • Other sections (if required) - $DRIVER_DEPENDENCY, $TUNABLE, and $DRIVER_INSTALL:

    In the $DRIVER_DEPENDENCY section, enter the names of all other modules that depend upon this module.

    In the $TUNABLE section, enter the names and default values of the tunable parameters (variables) for the module. You may also enter minimum values for tunable parameters.

    In the $DRIVER_INSTALL section, enter the module's name and associated block and/or character major device number(s). To have the system dynamically assign a major number to your module, specify -1 in both the block major and char major fields of $DRIVER_INSTALL.

For complete details on the use of these keywords, see the "Modular Master File" section of the master(4) manpage.

system File Definition

Every DLKM module requires a system file. The system file includes the following mandatory section keyword and four optional section keywords:

  • $VERSION - (mandatory) indicates the version number for the file format. Version is defined as an integer and starts from one. Version 1 is the only supported file format for PA systems. Version 2 is a supported file format for Itanium Processor Family (IPF) Boot Time Loaded modules. If Version is 2 in the master and system files the phases keyword in the master file, and the $LOADPHASE keyword in the system file need to be specified.

    NOTE: The version number for the master file and system file must be the same.
  • $CONFIGURE - (optional) indicates if the module needs to be configured into the system. If $CONFIGURE is Y or y, the module will be configured on the next build; if $CONFIGURE is N or n, the module will not be configured on the next build. kmsystem(1M) provides the interface to modify the flag. The absence of t his section implies a value of n.

  • $LOADABLE - (optional) indicates how the module will be configured. If $LOADABLE is Y or y, the module will be configured as a dynamically configured loadable module; if $LOADABLE is N or n, the module will be statically linked into the kernel, requiring a reboot. kmsystem provides the interface to modify the flag. The absence of this section implies a value of n.

    NOTE: If $CONFIGURE is N or n, $LOADABLE is ignored.

    If the master file for the module does not have a $LOADABLE section, then the system file should not have one either.

  • $LOADPHASE-indicates the planned loading phase that will be used for the next configuration of that module. Once the module is configured and registered with the kernel, it will be used to initialize the configured loading phase. The value of this attribute indicates whether, and/or when, to load the module upon a system reboot. It can be one of the following:

    A

    Auto

    1

    Phase 1 of the boot sequence

    2

    Phase 2 of the boot sequence

    I

    Init phase

    Absence of this section indicates that the module will be configured to be loaded only on demand or by the auto loading mechanism. This is equivalent to specifying:

    $LOADPHASE A

  • $TUNABLE (empty) - place holder for any tunable parameter specified in the associated master file for which you want to specify a value other than the default value. Leave this section blank.

    kmtune(1M) is the interface to modify tunable parameters in the module's system description file and the HP-UX system file (/stand/system by default).

For complete details on the use of these keywords, see the "Modular System File" section of the system(4) manpage.

space.h File Definition

An optional component, the space.h file contains storage allocations and initializations of data structures associated with a DLKM module when the size or initial value of the data structures depend on configurable values such as tunable parameters. In order to communicate these values to the rest of the DLKM module, the values are stored in global variables and accessed by the module via extern declarations in the module's mod.o file.

NOTE: All tunable parameters specified in the master file are defined as global variables in the space.h file. See “Sample DLKM WSIO Class Driver” for clarification.

Modstub.o File Definition

An optional component, the Modstub.o file is statically configured into the kernel as a "place holder" for functions implemented in a loadable module that will be loaded at a later time. Its purpose is to enable the kernel to resolve references to the absent module's functions.

Modstub.o contains “stubs” for entry points defined in the associated loadable module that can be referenced by other statically linked kernel modules currently configured in the system. Access to a stub causes the kernel to auto load the associated loadable module.

Configuring a module that uses stubs requires a full kernel build so that the stubs can be statically linked to the kernel.

Initializing and Terminating DLKM Modules

In a traditional system with statically linked modules, the modules are initialized during system boot. Because dynamically loadable modules can be loaded either during boot or after the system is booted, they must be initialized differently than modules that are statically linked into the kernel during system boot. For HP-UX 11i Version 1.5 systems, loadable kernel modules can be configured to be loaded during the early stages of system boot. On PA systems, dynamically loadable modules must be loaded at run time. Boot time loading on PA platforms will be available in a future HP-UX release.

The rest of this section describes the steps that each type of dynamically loadable module must take to initialize itself and to terminate itself. In addition to WSIO class drivers, WSIO interface drivers, STREAMS drivers, STREAMS modules, and miscellaneous modules, this section includes the initialization and termination tasks for WSIO monolithic drivers. A monolithic driver is both a class driver (one that has a device special file) and an interface driver (one that touches real hardware and registers).

WSIO Class Drivers

To make a WSIO class driver loadable, the driver must provide a _load() and _unload() function and make minor changes to the way that the driver initializes itself. When a driver is statically linked, it typically links its _init() function into the dev_init chain, and the _init() function calls the next driver's _init() function in the chain.

This mechanism is inappropriate for loadable drivers since the dev_init chain is only used during system boot, and there is no mechanism to remove the entry from the chain when the module is unloaded.

Therefore, the module's _load() function should perform both the installation tasks (those normally done during install for static modules) and the driver initialization tasks, and the dev_init chain should be ignored.

Additionally, the _load() function is passed a pointer to an updated version of the drv_info_t structure; the driver must use this version of the drv_info structure when it registers itself with WSIO. A sample _load() function for a WSIO class driver is as follows:

Example 1-3 WSIO Class Driver _load Function

static wsio_drv_info_t module_name_wsio_info = { ... };

/*
* LOAD
*/

static int
module_name_load(void *arg)
{
if (module_name_debug)
printf("module_name> Loading\n");

/* Use drv_info passed to us instead of static version */
module_name_wsio_info.drv_info = (drv_info_t *) arg;

/* Register with WSIO */
if (!wsio_install_driver(&module_name_wsio_info))
{
printf("module_name> wsio_install_driver failed!!\n");
return (ENXIO);
}

/* Perform driver-specific initialization, but do not call
* next function in the dev_init list.
*/
(void) module_name_init();

return (0);
}
NOTE: The wsio_install_driver() function call returns 1 on success. However (and inconsistently), the wsio_uninstall_driver() function call returns 0 on success.

Initialization for a statically linked WSIO class driver is unchanged from historical practice. That is, the initial driver entry point is module_name_install. This function typically installs an _init() function in a list of functions that will be invoked later in the system boot process, and then the driver registers itself with WSIO. For example:

Example 1-4 WSIO Class Driver _install Function

static void module_name_linked_init (void);
static int (*module_name_saved_init)();

/*
* INSTALL
* This function is called if module is statically linked.
*/
int
module_name_install(void)
{
extern int (*dev_init)(void);

/* Link my init function into chain */
module_name_saved_init = dev_init;
dev_init = (int (*)()) &module_name_linked_init;

/* Register driver with WSIO */
return ( wsio_install_driver(&module_name_wsio_info) );
}

The _init() function in the static case must call the next driver's _init() function:

Example 1-5 WSIO Class Driver _init Function

/*
* Device initialization Link
* Called only for statically linked drivers to link init
* routine into list.
*/
static void
module_name_linked_init (void)
{
/* Perform driver-specific initialization */
(void) module_name_init ();

/* Call next init function in chain */
(void) (*module_name_saved_init)();
}

The _unload() function frees resources allocated by the driver and unregisters the driver from WSIO. If it is safe to unload the driver when the number of open devices for the driver goes to zero, then the module need not perform any special checks to determine if it is busy. If any step in the _unload() process fails, the driver must undo any action prior to the failure. A sample _unload() function for a WSIO class driver is as follows:

Example 1-6 WSIO Class Driver _unload Function

/*
* UNLOAD
*/
static int
module_name_unload(void *drv_infop)
{
/* This function is only called when the administrator
* attempts to unload the module and there are no open
* devices using the module. If there is some reason that
* the module should not be unloaded, check it now and
* return non-zero.
*/
if (module_name_no_unload) {
printf("module_name> I'm BUSY\n");
return (EBUSY);
}

/* Unregister with WSIO */
if ( wsio_uninstall_driver(&module_name_wsio_info) ) {
/* Uninstall failed! Return to a loaded, functional
* state. */
printf("module_name> wsio_uninstall_driver
failed!!\n");
return (ENXIO);
}

/* Cancel pending timeouts, free allocated memory and
* resources, etc.
*/

if (module_name_debug)
printf("module_name> Unloaded\n");

return (0);
}
NOTE: The wsio_uninstall_driver() function call returns 0 on success. However (and inconsistently), the wsio_install_driver() function call returns 1 on success.

WSIO Interface Drivers

A WSIO interface driver requires an _attach() function for any interface card it intends to claim. It will also require a _probe() function if it intends to install device probes. When installing a WSIO interface driver, the driver must register itself with WSIO and set up the _attach() function and any device probes.

Because the timing of initialization differs between loadable and statically linked modules, some of the steps that are handled automatically during boot for static drivers must be explicitly performed by the _load() function.

The primary difference in making a WSIO interface driver loadable concerns the handling of the attach list. Historically, drivers have added their _attach() functions to a global list, and the _attach() function was responsible for calling the next driver's _attach() function in the list. However, this method does not allow the driver to remove its _attach() function from the list; thus, this approach cannot support unloading.

New support functions have been added to WSIO to allow interface drivers to add and remove their _attach() functions from the attach list. These functions are:

  • int mod_wsio_attach_list_add(list_type, &attach_func);

  • int mod_wsio_attach_list_remove(list_type, &attach_func);

list_type specifies the attach list to use; valid entries are MOD_WSIO_CORE, MOD_WSIO_PCI, and MOD_WSIO_EISA. Both functions return 0 on success and 1 on failure.

These functions should only be called when the module is dynamically loaded. Statically linked modules should continue to use the existing attach chain.

Device probes are normally associated with interface drivers during the initialization of the WSIO Context Dependent Input/Output (CDIO) at boot time. Since this is only done once, loadable interface drivers must explicitly connect the device probe to the driver's drv_info structure:

void wsio_activate_probe(char *probe_name, struct drv_info *drv_infop);

probe_name is the name of the probe as registered by wsio_register_dev_probe() or wsio_register_probe_func(). drv_infop is a pointer to the drv_info structure for this driver. In general, use a probe provided by the system. For example, parallel_scsi_probe or side_probe. See the HP-UX Driver Development Guide for complete information.

Finally, similar to class drivers, the _load() function for interface drivers is passed a pointer to an updated version of the drv_info_t structure; the interface driver must use this version of the drv_info structure when it registers itself with WSIO.

The sample code below puts all these concepts together to demonstrate the loading and initialization steps for an interface driver. This sample can be configured as either a loadable module or a statically linked module.

Example 1-7 WSIO Interface Driver Loading and Initialization Coding

static wsio_drv_info_t module_name_wsio_info = { ... };
static int (*module_name_saved_attach)();

int module_name_load(void *arg)
{
/* Use the drv_info passed to us instead of the static
* version */
wsio_info.drv_info = (drv_info_t *) arg;

/* Register the driver with WSIO */
if (!wsio_install_driver(&wsio_info))
return ENXIO; /* install failed! */

/* Add the attach function to the DLKM attach list. */
mod_wsio_attach_list_add(MOD_WSIO_CORE, &module_name_core_attach);

/* Register the device probe. */
wsio_register_dev_probe(IF_CLASS, probe_func,
"probe_name");

/* The following step is only required for dynamically
* loaded modules: attach the probe function to the
* drv_info structure */
wsio_activate_probe("probe_name", wsio_info.drv_info);

return 0;
}

int module_name_install(void)
{
/* Add the attach function to the list. */
extern init (*core_attach)();
module_name_saved_attach=
core_attach;
core_attach=
module_name_attach_linked;

/* Register the device probe. */
wsio_register_dev_probe(IF_CLASS, probe_func,
"probe_name");

/* Register the driver with WSIO */
return (wsio_install_driver(&wsio_info);
}

/* Common attach function for both dynamic and
* static modules */
int
module_name_attach(int id, struct isc_table_type *isc)
{
/* Normal attach function operations */
...
}

/* Attach function called from attach chain for static modules
* only
*/
int
module_name_attach_linked (int id, struct isc_table_type *isc)
{
module_name_attach(id, isc);
return ((*module_name_saved_attach)(id, isc));
}

The _unload() function determines if the driver is still busy, and if not, it cleans up all resources that were obtained by the driver. It should free memory that it allocated and remove the attach() function from the attach list. It should also unregister any _probe() function pointers that the driver has registered with other kernel services; for example, the _unload() function should unlink the interrupt service function. If an operation fails while unloading, the _unload() function must be able to restore the driver to a working state and return a nonzero value.

Unlike class drivers, there is no automatic method for the system to determine if an interface driver is still busy. This problem can sometimes be avoided by making class drivers that use the interface driver dependent upon the interface driver. With this dependency relationship in place, the system will not allow the interface driver to be unloaded as long as any class driver that depends upon it is still loaded. If it is not appropriate for the interface driver to rely on dependencies, then it must determine via other means if it is possible to unload the driver. The _unload() function is always free to return a nonzero value, and the module will remain loaded. A sample _unload() function for a WSIO interface driver is as follows:

Example 1-8 WSIO Interface Driver _unload Function

int module_name_unload(void *arg)
{
int ret;
struct isc_table_type *isc;
void *token, *priv_ptr;
return (EINVAL);

/* Remove the attach function from the DLKM attach list. */
if (mod_wsio_attach_list_remove(MOD_WSIO_CORE, &module_name_core_attach)) {
return(ENXIO);
}

/* Unregister the device probe. */
wsio_unregister_dev_probe(IF_CLASS, probe_func,
"probe_name");

/* For each ISC belonging to the driver:
* Cancel pending timeouts, free allocated memory,
* unregister ISR routine, etc.
*/

if (wsio_uninstall_driver(&module_name_wsio_info)) {
/* uninstall failed - go back to loaded state
* undo what has been done in _unload routine
*/
return ENXIO;
}
return(0);
}

WSIO Monolithic Drivers

The _load() function for a monolithic driver must effectively be the union of the _load() functions for the class and interface drivers described previously. Similarly, the _unload() function must effectively be the union of the _unload() functions for the class and interface drivers.

STREAMS Drivers

Initialization of STREAMS drivers is very similar for both the loadable and statically linked module cases. The only difference is that loadable drivers must use the drv_info_t structure that is passed as an argument to the _load() function. The major numbers from this structure must also be used in the streams_info_t structure passed to str_install(). Sample _load() and _install() functions for a STREAMS driver are as follows:

Example 1-9 STREAMS Driver _load and _install Functions

static drv_info_t str_drv_info = { ... };
static drv_info_t *drv_info_p = &str_drv_info;

static streams_info_t str_info = { ... };
static drv_ops_t module_name_str_drv_ops = { ... };

int module_name_load(void *arg)
{
int retval;

/* Use the drv_info passed to us instead of the static
* version */
drv_info_p = (drv_info_t *) arg;
str_info.inst_major = drv_info_p->c_major;

if (module_name_install())
return ENXIO;
return 0;
}

int module_name_install(void)
{
int retval;

/* Install in cdevsw */
if ((retval=install_driver(drv_info_p,
&module_name_str_drv_ops)) != 0)
return (retval); /* install failed */

/* Install in Streams */
if ((retval = str_install(&str_info)) != 0)
{
uninstall_driver(drv_info_p);
return retval;
}
return 0;
}

STREAMS drivers, like WSIO class drivers, automatically track open() and close() system calls for the STREAMS device. The system will prevent a STREAMS driver from unloading whenever the device has one or more open file handles. Of course, the driver can still disallow an unload if this check is insufficient for its needs. A typical _unload() function for a STREAMS driver is as follows:

Example 1-10 STREAMS Driver _unload Function

module_name_unload(void *arg)
{
int retval;

/* Uninstall from Streams */
if ((retval = str_uninstall(&str_info)) != 0)
return retval;

/* Free module specific resources, etc. */
...
return 0;
}

STREAMS Modules

Loadable STREAMS modules have no special requirements during initialization. The argument passed to the _load() function should be ignored. Sample _load() and _install() functions are as follows:

Example 1-11 STREAMS Module _load and _install Functions

static streams_info_t str_info = { ... };

int module_name_load(void *arg)
{
int retval;

if (module_name_install())
return ENXIO;
return 0;
}

int module_name_install(void)
{
/* Install in Streams */
if ((retval = str_install(&str_info)) != 0)
return retval;
return 0;
}

The system automatically tracks pushes and pops of a STREAMS module on active streams; a module cannot be unloaded while it is pushed onto one or more streams. A typical unload() function for a STREAMS module is as follows:

Example 1-12 STREAMS Module _unload Function

int module_name_unload(void *arg)
{
int retval;

/* Uninstall from Streams */
if ((retval = str_uninstall(&str_info)) != 0)
return retval;

/* Free module specific resources, etc. */
...
return 0;
}

Miscellaneous Modules

Miscellaneous modules can implement any feature within the kernel. As such, a miscellaneous module's _load() function must address all of the module's specific needs. Similarly, the module's _unload() function must determine for itself if it is safe to unload. The system will not allow a module to be unloaded if other loaded modules are dependent upon the module. Other than this check, the system performs no other checks when the administrator attempts to remove a miscellaneous module from the kernel.

The argument to the _load() function is not meaningful and should be ignored.

DLKM Module Development Process

Developing a DLKM module such as a device driver requires more up front planning than most application programming projects. At the very least, testing and debugging are more involved, and more knowledge about hardware is required.

This section describes the procedures for developing a DLKM module. Since you can include a DLKM module in the system in one of two ways, either as a Dynamically Configured Loadable Module or as a Statically Configured Loadable Module, this section considers both methods of inclusion. Complete command line invocations are included in the detailed descriptions where appropriate.

Developing a DLKM module is an iterative process. You write, test, and debug a module in a piecemeal fashion, building up to the implementation of the complete module.

During the first phases of development, possible errors in the module code may panic or damage the system, even parts of the system that may seem unrelated to your module. Testing should be done when no other users are on the system and all production data files are backed up. Ideally, testing would be performed on a restricted use system set up specifically for the purpose of developing kernel modules.

NOTE: Before starting the development process, it is always a good idea to make your own backup of /stand/vmunix for emergency recovery purposes.

Although the kmupdate(1M) command always saves the previous /stand/vmunix to the kernel name /stand/vmunix.prev, you must be aware that a subsequent kmupdate(1M) will again overwrite the vmunix.prev kernel.

The recommended way to permanently save /stand/vmunix is to move it to a safe name, such as /stand/vmunix.bkup.

Do Not Copy!: Copying the /stand/vmunix file may result in the eventual loss of the kernel component set necessary to boot the copy.

Furthermore, if /stand/vmunix had been the running kernel, we recommend an immediate reboot from the renamed location:

#mv /stand/vmunix /stand/vmunix.bkup
#shutdown -r

Once /stand/vmunix has been moved, there is no longer a default kernel from which to boot until a new kernel is configured and updated via the kmupdate(1M) command. To boot from a non-default kernel, refer to "Booting from an Alternate Kernel" in the chapter "Administering a System: Booting and Shutdown" in Managing Systems and Workgroups.

Creating the Module's Component Files

Create the mod.o, master, system, space.h (optional), and Modstub.o (optional) files for the DLKM module in a single directory. It is suggested that you use a subdirectory of /usr/conf such as /usr/conf/module_name.

When choosing a name for your module, choose a unique name to avoid conflict with kernel functions and global variables. Consider using your company's name and something that indicates the module's purpose.

To compile your module source code, use the ANSI C compiler /opt/ansic/bin/cc and the compiler options shown below. You can also use the compiler options found in the makefile /stand/build/config.mk or
/stand/build/config.mod.

To compile your module source code, follow these steps:

  1. Change directories to the directory containing the module's component files.

  2. For a 64-bit PA target machine, execute the following cc(1) command:

    /opt/ansic/bin/cc -I. -I/usr/conf -I/usr/conf/sys \
    -Wp,-H300000 +Hx0 +R500 +ESsfc +ESssf +XixdU \ +ES1.Xindirect_calls +ESlit +O2 +Oentrysched +Ofastaccess \ +DA2.0W +DS2.0 -Ae -DLWSYSCALL -DPGPROF -DACLS -DAUDIT \
    -DIDDS -D__ROSE__ -DHPONCPLUS -D__ROSEVILLE__ \
    -DSPP_OBP_BOOT -DSPP_RUNWAY_ERR_ENABLED -DPARISC \
    -DRDB -DNEW_RDB -DKGDB_ON -DIVT_INTERCEPT -DCOMB_FLIPPER \
    -DNEW_MFCTL_W -DSTCP -DIPSEC -D_UNSUPPORTED \
    -D_HPUX_SOURCE -D_XPG4_EXTENDED -D_KERNEL -D__STDC_EXT__ \
    -D_CLEAN_BE -D__TEMP64__ -D__hp9000s800 -D__NO_EM_HDRS \
    -U__hp9000s700 -o mod.o -c module_name.c

  3. For a 64-bit Itanium target machine, execute the following cc(1) command:

    	/opt/ansic/bin/cc -I. -I/usr/conf -I/usr/conf/sys \
    -Wp,-H300000 +kernel +Oshortdata=0 +XixdU +O2 +ESlit +DD64 \
    -Ae -DLWSYSCALL -DPGPROF -DACLS -DAUDIT -DIDDS -D__ROSE__ \
    -DHPONCPLUS -DVARIABLE_UAREA -DSTCP -DIVT_INTERCEPT \
    -D_UNSUPPORTED -D_HPUX_SOURCE -D_XPG4_EXTENDED -D_KERNEL \
    -D__STDC_EXT__ -D_CLEAN_BE -D_SYSCALL_64 -­_NO_PA_HDRS \
    -DKERNEL_DEBUGGER -U__IA64__ -U__hp9000s800 \
    -U__hp9000s700 -U__hppa -o mod.o -c module_name.c

Installing the Module's Component Files

Once the module's mod.o, master, system, and optional files have been created, call the kminstall command to copy those files to certain subdirectories of /usr/conf and /stand. The kminstall command creates the required subdirectories if they do not exist.

kminstall expects to find the module's component files in the current working directory. If module_name already exists on the system, kminstall prints a message and fails.

To install a DLKM module's components, follow these steps:

  1. Change directories to the directory containing the module's component files.

  2. Execute the following kminstall command:

        /usr/sbin/kminstall -a module_name

kminstall copies the module's component files to the appropriate locations. For example, kminstall copies the master file to /usr/conf/master.d/module_name/0.1.0, and the system file to /stand/system.d/module_name/0.1.0. File /usr/conf/master.d/module_name/0.1.0 is known as the module's master configuration file. File /stand/system.d/module_name/0.1.0 is known as the module's system description file.

NOTE: The file locations receiving copies of the module's component files may change in future HP-UX releases.

Activating the DLKM

After a module's component files have been installed using the kminstall command, the module is ready to be configured and registered with the running kernel. Follow the procedures in “DLKM Procedures for Dynamically Configured Loadable Modules” for instructions on how to prepare, configure, register, load, and manage your DLKM as dynamically loadable, on the target system.

To configure and test your DLKM as statically linked refer to “DLKM Procedures for Statically Configured Loadable Modules”.

Creating Device Special Files for the Module

Before devices supported by a driver type DLKM module can be accessed by the system, you need to create special files in the /dev directory using the mknod(1M) command. You do not have to create these files every time you build and boot a new kernel--only when you first add the new module. There must be a special file for each device on your system.

When you set up the special files for a newly added device driver, you must specify the device type (character or block) and the major and minor device numbers. The kernel recognizes any single device by the major-minor number combination encoded in the device special files.

For a dynamically loadable driver, the system assigns a major number to the driver when it first registers with the system. For a statically linked driver, the system assigns a major number to the driver during system boot. The system remembers assigned major numbers from system boot to system boot.

To have the system dynamically assign a major number to your driver, follow these steps:

  1. Specify -1 in both the block major and char major fields in the $DRIVER_INSTALL section of your driver's master file.

  2. Specify -1 in both the b_major and c_major fields of the drv_info_t structure of your driver source code.

    Also, set the following bit values in the flags field in the drv_info_t structure: if you have a block driver, set the DRV_BLOCK value; if you have a character driver set the DRV_CHAR value; if your driver is both a block and a character driver, set both values.

    Note that this step applies only to statically configured modules. The drv_info_t structure in driver source is ignored for dynamic configuration.

To create device special files for your driver, follow these steps:

  1. Use the lsdev(1M) command to identify the major number assigned to the device driver. lsdev lists all device drivers configured into the kernel and their block and character major numbers.

    To extract the block major number from the display for a driver named mydriver, execute the following command:

    /usr/sbin/lsdev -h -d mydriver | awk '{print $2}'

    To extract the character major number from the display for a driver named mydriver, execute the following command:

    /usr/sbin/lsdev -h -d mydriver | awk '{print $1}'

    As an alternative and assuming a dynamically loadable driver, you can execute the following two commands to identify the major number assigned to a driver named mydriver:

    /usr/sbin/kmadmin -L mydriver
    /usr/sbin/kmadmin -Q mydriver
  2. Construct a minor number for each device special file that you create for the driver. See Appendix C, "Major and Minor Numbers," in the Configuring HP-UX For Peripherals document for more information about bit assignments and dev_t.

  3. Use the mknod command to create the device special files for the driver.

    In the example below, mydriver was dynamically assigned the block and character major numbers 65 and 234, respectively. Its minor number, 0x026000, is constructed like that of instr0 (bits 8 through 15 encode 2 as the instance of the interface card, and bits 16 through 19 encode 6 of the device's address).

    /usr/sbin/mknod /dev/mydriver b 65 0x026000
    /usr/sbin/mknod /dev/mydriver c 234 0x026000
NOTE: You may be able to use the ioscan and insf commands to install special files in the /dev directory. If no options are specified, insf creates special files for all new devices in the system. New devices are those devices for which no special files have been previously created. See the ioscan(1M) and insf(1M) manual pages for more details.

Testing the Module

Testing a DLKM module consists of testing all of its functions under a variety of operating conditions and for both configurations: dynamically loadable and statically linked. Debugging a module is largely a process of analyzing the code to determine what could have caused a given problem.

The most common debugging technique is monitoring the kernel code using the printf() function to print statements included in the module source code so that you know what your module is doing during run-time operation. There are also debuggers such as Q4 to examine the kernel code. See the Q4 description file in the /usr/contrib/doc directory for more detail.

You can use a combination of C preprocessor macros, conditional compilation, and control variables to turn printf() messages on or off. The sample template character driver, “dlclass.c”, includes a control variable named dlclass_debug to determine whether or not to generate the messages. All of the messages can be disabled at once by using kmtune -s to change the value of dlclass_debug to 0 before configuring dlclass.

Correcting Errors in the Module's Component Files

Correcting errors in the DLKM module is largely a process of analyzing the code to determine what could have caused a given problem. Some common errors are as follows:

  • Coding problems

  • Installation problems

  • Data structure problems

  • Timing errors

  • Corrupted interrupt stack

  • Accessing critical data

  • Overuse of local driver storage

  • Incorrect DMA address mapping

Updating the Module's Components

Use the -u option of the kminstall command to update a DLKM module's components in the subdirectories of /usr/conf and /stand. Use this command when you modify one or more of the module's component files. If you modify the module source code, you must first recompile the source code in accordance to “Creating the Module's Component Files”.

kminstall expects to find the module's component files in the current directory. If module_name already exists on the system, kminstall updates the module. If module_name does not exist on the system, kminstall prints a warning and adds the module's components to the system.

When updating an existing module, kminstall takes the values of the tunable parameters and the $CONFIGURATION and $LOADABLE flags from the module's current system description file and saves them to the module's new system description file.

To update a DLKM module's components, follow these steps:

  1. Change directories to the directory containing the module's component files.

  2. Execute the following kminstall command:

    /usr/sbin/kminstall -u module_name

kminstall copies the module's component files to the appropriate locations, thereby overwriting the same named files at those locations.

Removing the Module's Components from the System

Use the -d option of the kminstall command to remove a DLKM module's components from the system. If the specified module is listed in the /etc/loadmods file, kminstall prints a warning message and removes the module entry from /etc/loadmods. If the specified module is currently loaded, kminstall tries to unload the module. If the unload fails, it prints a message and exits with an error; otherwise, kminstall tries to unregister the module. If the unregistration fails, kminstall prints a message and exits with an error.

To remove a DLKM module's components, execute the following kminstall command:

   /usr/sbin/kminstall -d module_name

kminstall deletes the module's components from the /usr/conf and
/stand subdirectories.

NOTE: kminstall does not delete the original component files for the module.

If the removed DLKM module is statically linked into the running kernel, you will have to execute config -u to reconfigure the kernel once the module has been removed. Then you will have to shutdown and restart the system for the new configuration to take effect.

Sample DLKM WSIO Class Driver

This section presents a complete skeleton of a DLKM WSIO class driver in HP-UX. The files shown are:

  • dlclass.c - the template character driver

  • master - the master file for the sample character driver

  • system - the system file for the sample character driver; $CONFIGURE and $LOADABLE flags are both set to Y

  • space.h - a configuration file that sets two flags and allocates space for the sample character driver

  • Makefile - a makefile for the template character driver; initiates the compiling of dlclass.c and places a copy of mod.o in the current working directory

  • mod.mk - a file called by Makefile that contains cc command and appropriate options to compile dlclass.c for either a 64-bit PA target machine or a 64-bit Itanium target machine.

This driver skeleton is a complete working character driver that does not use any hardware. It can be added to any HP-UX 11.0 kernel and executed as an example.

The character driver supports a pseudo device. Characters written to the device are passed to the kernel message buffer, so you have to execute the dmesg(1M) command to actually see the output. A read sequence results in the printing of the following hardcoded string compiled into the driver: "Reading from loadable WSIO driver 'dlclass'."

The character driver supports three tunable parameters:

  • dlclass_no_unload (integer)—prevents unloading when nonzero; initially set to 0 in $TUNABLE section of master file

  • dlclass_debug (integer)—writes debugging output into message buffer when nonzero; initially turned on (set to 1) in $TUNABLE section of master file

  • dlclass_bufsz (integer)—size of internal write buffer; initially set to 40 characters in $TUNABLE section of master file

To install, configure, and load the character driver, log in as user root and perform the following steps:

  1. Execute the following command to create a working directory for dlclass: mkdir /usr/conf/dlclass.

  2. Change directories to the/usr/conf/dlclass directory.

  3. Create all component files for dlclass in addition to Makefile and mod.mk in the /usr/conf/dlclass directory.

  4. Execute the following command to generate the dlclass driver: make.

    The make(1) command examines the local Makefile and generates the dlclass driver according to the rules in the Makefile. It compiles dlclass.c into mod.o. It then installs and configures the driver. The make command also loads the driver and extracts the dynamically assigned major number used to create device special file /dev/dlclass.

You can read a message from dlclass by executing the following command: cat /dev/dlclass. The following message prints on your computer screen: "Reading from loadable WSIO driver 'dlclass'."

You can write a message to dlclass by executing a command such as echo Hello Hello > /dev/dlclass. To see the output, execute the following command: dmesg | tail. The system responds as follows:

dlclass> OPEN -- write buffer size = 40
dlclass> 'Hello Hello'
dlclass> CLOSE

But if you write a message more than 40 characters, (cp master
/dev/dlclass
, for example), the write() system call will fail because dlclass cannot remember such a long word. You can change the maximum length of the message by using kmtune -s to set the tunable parameter dlclass_bufsz to a higher value.

NOTE: When dlclass is unloaded (kmadmin -U dlclass), you can auto load dlclass by executing a command such as cat /dev/dlclass.

Sample Component Files

dlclass.c

/*
* Loadable/Unloadable Test Driver - dlclass
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/mod_conf.h>
#include <sys/moddefs.h>
#include <sys/io.h>
#include <sys/wsio.h>

/* Entry Points */
int dlclass_install(void);
static int dlclass_load(void *drv_infop);
static int dlclass_unload(void *drv_infop);
static int dlclass_open(dev_t dev, int flags, intptr_t dummy, int mode);
static int dlclass_close(dev_t dev, int flags, int mode);
static int dlclass_read(dev_t dev, struct uio *uio);
static int dlclass_write(dev_t dev, struct uio *uio);

/* Local functions */
static int dlclass_init (void);
static void dlclass_linked_init (void);

/* Tunable Parameters */
extern int dlclass_no_unload;
extern int dlclass_debug;
extern int dlclass_bufsz;

/* message for dlclass_read() */
static char dlclass_msg[] = "Reading from loadable WSIO driver 'dlclass'.\n";

/*
* Wrapper Table
*/
extern struct mod_operations gio_mod_ops;
static drv_info_t dlclass_drv_info;
extern struct mod_conf_data dlclass_conf_data;

/* module type specific data */
static struct mod_type_data dlclass_drv_link = {
"dlclass - Loadable/Unloadable Test Module",
(void *)NULL
};

static struct modlink dlclass_mod_link[] = {
{ &gio_mod_ops, (void *)&dlclass_drv_link }, /* WSIO */
{ NULL, (void *)NULL }
};

struct modwrapper dlclass_wrapper = {
MODREV,
dlclass_load,
dlclass_unload,
(void (*)())NULL,
(void *)&dlclass_conf_data,
dlclass_mod_link
};

/*
* Driver Header
*/
static drv_info_t dlclass_drv_info = {
"dlclass", /* type */
"pseudo", /* class */
DRV_CHAR|DRV_PSEUDO|DRV_SAVE_CONF|DRV_MP_SAFE,/* flags */
-1, /* b_major */
-1, /* c_major */
NULL, /* cdio */
NULL, /* gio_private */
NULL, /* cdio_private */
};

static drv_ops_t dlclass_drv_ops = {
dlclass_open, /* d_open */
dlclass_close, /* d_close */
NULL, /* d_strategy */
NULL, /* d_dump */
NULL, /* d_psize */
NULL, /* d_mount */
dlclass_read, /* d_read */
dlclass_write, /* d_write */
NULL, /* d_ioctl */
NULL, /* d_select */
NULL, /* d_option1 */
NULL, /* reserved1 */
NULL, /* reserved2 */
NULL, /* reserved3 */
NULL, /* reserved4 */
0 /* d_flags */};

static wsio_drv_data_t dlclass_wsio_data = {
"pseudo_dlclass", /* drv_path */
T_DEVICE, /* drv_type */
DRV_CONVERGED, /* drv_flags */
NULL, /* drv_minor_build - field not used */
NULL, /* drv_minor_decode - field not used*/
};

static wsio_drv_info_t dlclass_wsio_info = {
&dlclass_drv_info,
&dlclass_drv_ops,
&dlclass_wsio_data,
};

static int (*dlclass_saved_init)();

/*
* LOAD
*/
static int
dlclass_load(void *arg)
{
if (dlclass_debug)
printf("dlclass> Loading\n");

/* Use drv_info passed to us instead of static version */
dlclass_wsio_info.drv_info = (drv_info_t *) arg;

/* Register with WSIO */
if (!wsio_install_driver(&dlclass_wsio_info))
{
printf("dlclass> wsio_install_driver failed!!\n");
return (ENXIO);
}

/* Perform driver-specific initialization, but do not call
* next function in the dev_init list.
*/
(void) dlclass_init();

return (0);
}

/*
* UNLOAD
*/
static int
dlclass_unload(void *drv_infop)
{
/* This function is only called when the administrator
* attempts to unload the module and there are no open
* devices using the module. If there is some reason that
* the module should not be unloaded, check it now and
* return non-zero.
*/
if (dlclass_no_unload) {
printf("dlclass> I'm BUSY\n");
return (EBUSY);
}

/* Unregister with WSIO */
if ( wsio_uninstall_driver(&dlclass_wsio_info) ) {
/* Uninstall failed! Return to a loaded,
* functional state. */
printf("dlclass> wsio_uninstall_driver failed!!\n");
return (ENXIO);
}

/* Cancel pending timeouts, free allocated memory and
* resources, etc.
*/

if (dlclass_debug)
printf("dlclass> Unloaded\n");

return (0);
}

/*
* INSTALL
* This function is called if module is statically linked.
*/
int
dlclass_install(void)
{
extern int (*dev_init)(void);

/* Link my init function into chain */
dlclass_saved_init = dev_init;
dev_init = (int (*)()) &dlclass_linked_init;

/* Register driver with WSIO */
return ( wsio_install_driver(&dlclass_wsio_info) );
}

/*
* Device initialization Link
* Called only for statically linked drivers to link init
* routine into list.
*/
static void
dlclass_linked_init (void)
{
/* Perform driver-specific initialization */
(void) dlclass_init ();

/* Call next init function in chain */
(void) (*dlclass_saved_init)();
}

/*
* Device initialization
* This is common code for both statically linked and
* dynamically loaded modules.
*/
static int
dlclass_init (void)
{
/* This driver has no initialization code. But if it did,
* it would go here!
*/

return 0; /* return value is ignored. */
}

/*
* OPEN
*/
static int
dlclass_open(dev_t dev, int flag, intptr_t dummy, int mode)
{
if (dlclass_debug)
printf("dlclass> OPEN -- write buffer size = %d\n",
dlclass_bufsz);

return (0);
}

/*
* CLOSE
*/
static int
dlclass_close(dev_t dev, int flag, int mode)
{
if (dlclass_debug)
printf("dlclass> CLOSE\n");

return (0);
}

/*
* READ
*/
static int
dlclass_read(dev_t dev, struct uio *uio)
{
if (dlclass_debug)
printf("dlclass> READ\n");

if (uio->uio_offset == sizeof(dlclass_msg))
return (0);
if (uio->uio_offset > sizeof(dlclass_msg))
return (ENXIO);

/* copy out my message */
return ( uiomove((caddr_t)(dlclass_msg
+uio->uio_offset),
sizeof(dlclass_msg)-uio->uio_offset,
UIO_READ, uio) );
}

/*
* This code could have problems if uio->uio_offset
* is negative (a page boundary is crossed)
*/

/*
* WRITE
*/
static int
dlclass_write(dev_t dev, struct uio *uio)
{
caddr_t bufp;
char *end;
int len, err;

bufp = kmalloc(dlclass_bufsz+1, M_DYNAMIC, M_WAITOK);
len = MIN(uio->uio_resid, dlclass_bufsz);

/* print out user data */
if ( !(err = uiomove(bufp, len, UIO_WRITE, uio)) ) {
bufp[len] = 0;
printf("dlclass> '%s'", bufp);

if (len == dlclass_bufsz) {
printf("...???\n");
}
}

kfree(bufp, M_DYNAMIC);
return (err);
}
master

*
* master file for "dlclass" module
*

$VERSION
1
$$$

$DRIVER_INSTALL
*
* Driver Block major Char major Required for
* minimal system
dlclass -1 -1
$$$

$LOADABLE
$$$

$INTERFACE
base
$$$

$DRIVER_DEPENDENCY
$$$

$TYPE
dlclass wsio_class pseudo cpmi -1 -1
$$$

$TUNABLE
dlclass_no_unload DLCLASS_NO_UNLOAD 0
dlclass_debug DLCLASS_DEBUG 1
dlclass_bufsz DLCLASS_BUFSZ 40 8
$$$
system

*
* system file for "dlclass" module
*
$VERSION 1
$CONFIGURE Y
$LOADABLE Y
$TUNABLE
$$$
space.h

/*
* Tunable parameters for "dlclass" module
*/
int dlclass_no_unload = DLCLASS_NO_UNLOAD;
int dlclass_debug = DLCLASS_DEBUG;
int dlclass_bufsz = DLCLASS_BUFSZ;
Makefile

#
# This makefile needs to be invoked as follows:
#
# make <options>
#
# Here, options include:
#
# all to configure module and create device file
# config to configure the module in target system
# dev to create the necessary device file
# load to load the module
# status to show status of loaded module(s)
# clobber to unconfigure the module and remove device file
#
# CC must be set to the Ansi-C compiler installed on the target
# system, e.g. CC=/opt/ansic/bin/cc
#
#
# Change these 3, and all character and block device
# drivers should work.
#
MODULE=dlclass
DEV_C=/dev/dlclass
DEV_B=
all: config dev
config: mod.o
kminstall -a $(MODULE) 2> /dev/null
config -M $(MODULE) -u 2> /dev/null

dev: config $(DEV_C) $(DEV_B)

#
# If the module requires a block device, this target can be
# used as a template. Replace "DEV_C" with "DEV_B",
# "Character" with "Block", and the "c" in the mknod command
# with a "b".
#
$(DEV_C):
kmadmin -L $(MODULE)
kmadmin -Q $(MODULE) | awk '/Character Major/ { \
rc=system("mknod $(DEV_C) c " $$3 " 0"); \
exit rc; \
}'


mod.o: $(MODULE).c
@if [ -n "$(ARCH)" ]; \
then \
make -f mod.mk mod.o ARCH=$(ARCH) MODULE=${MODULE}; \
elif [ "`uname -m`" = "ia64" ]; \
then \
make -f mod.mk mod.o ARCH=ia64 MODULE=${MODULE}; \
else \
make -f mod.mk mod.o ARCH=pa64 MODULE=${MODULE}; \
fi

load: config
kmadmin -L $(MODULE) 2> /dev/null

status:
kmadmin -s

clobber:
kminstall -d $(MODULE)
rm -f mod.o $(DEV_C) $(DEV_B)
mod.mk

#
# Makefile to build DLKM modules for HP-UX 11i Version 1.5
#
# Inputs:
#
# MODULE must be defined as the DLKM module to build
# ARCH must be either ia64 or pa64
#
INCDIR= -I. -I/usr/conf -I/usr/conf/sys

#
# Itanium Specific definitions
#

IDENTS_ia64= -DLWSYSCALL -DPGPROF -DACLS -DAUDIT -DIDDS -D__ROSE__ -DHPONCPLUS -DVARIABLE_UAREA -DSTCP -DIVT_INTERCEPT -D_UNSUPPORTED -D_HPUX_SOURCE -D_XPG4_EXTENDED -D_KERNEL -D__STDC_EXT__ -D_CLEAN_BE -D_SYSCALL_64 -D__NO_PA_HDRS -DKERNEL_DEBUGGER -U__IA64__ -U__hp9000s800 -U__hp9000s700 -U__hppa

CC_OPTS_ia64= -Wp,-H300000 +kernel +Oshortdata=0 +XixdU +O2 +Olit=all +DD64 -Ae

#
# PA20 Specific definitions
#

IDENTS_pa64= -DLWSYSCALL -DPGPROF -DACLS -DAUDIT -DIDDS -D__ROSE__ -DHPONCPLUS -D__ROSEVILLE__ -DSPP_OBP_BOOT -DSPP_RUNWAY_ERR_ENABLED -DPARISC -DRDB -DNEW_RDB -DKGDB_ON -DIVT_INTERCEPT -DCOMB_FLIPPER -DNEW_MFCTL_W -DSTCP -DIPSEC -D_UNSUPPORTED -D_HPUX_SOURCE -D_XPG4_EXTENDED -D_KERNEL -D__STDC_EXT__ -D_CLEAN_BE -D__TEMP64__ -D__hp9000s800 -D__NO_EM_HDRS -U__hp9000s700

CC_OPTS_pa64= -Wp,-H300000 +Hx0 +R500 +ESsfc +ESssf +XixdU +ES1.Xindirect_calls +ESlit +O2 +Oentrysched +Ofastaccess +DA2.0W +DS2.0 -Ae

all: mod.o

mod.o: ${MODULE}.o
${LD} -r -o mod.o ${MODULE}.o

${MODULE}.o: ${MODULE}.c
echo "Compiling ${MODULE}.c ..."
${CC} ${INCDIR} ${CC_OPTS_${ARCH}} ${IDENTS_${ARCH}} -o ${MODULE}.o -c ${MODULE}.c
Printable version
Privacy statement Using this site means you accept its terms Feedback to webmaster
© 2001 Hewlett-Packard Development Company, L.P.