Device Driver Tutorial

Exit Print View

Updated: July 2014
 
 

Writing the Loadable Module Configuration Entry Points

    Every kernel module of any type must define at least the following three loadable module configuration entry points:

  • The _init(9E) routine initializes a loadable module. The _init(9E) routine must at least call the mod_install(9F) function and return the success or failure value that is returned by mod_install(9F).

  • The _info(9E) routine returns information about a loadable module. The _info(9E) routine must at least call the mod_info(9F) function and return the value that is returned by mod_info(9F).

  • The _fini(9E) routine prepares a loadable module for unloading. The _fini(9E) routine must at least call the mod_remove(9F) function and return the success or failure value that is returned by mod_remove(9F). When mod_remove(9F) is successful, the _fini(9E) routine must undo everything that the _init(9E) routine did.

The mod_install(9F), mod_info(9F), and mod_remove(9F) functions are used in exactly the same way in every driver, regardless of the functionality of the driver. You do not need to investigate what the values of the arguments of these functions should be. You can copy these function calls from this example and paste them into every driver you write.

In this section, the following code is added to the dummy.c source file:

/* Loadable module configuration entry points */
int
_init(void)
{
    cmn_err(CE_NOTE, "Inside _init");
    return(mod_install(&ml));
}

int
_info(struct modinfo *modinfop)
{
    cmn_err(CE_NOTE, "Inside _info");
    return(mod_info(&ml, modinfop));
}

int
_fini(void)
{
    cmn_err(CE_NOTE, "Inside _fini");
    return(mod_remove(&ml));
}

Declaring the Loadable Module Configuration Entry Points

The _init(9E), _info(9E), and _fini(9E) routine names are not unique to any particular kernel module. You customize the behavior of these routines when you define them in your module, but the names of these routines are not unique. These three routines are declared in the modctl.h header file. You need to include the modctl.h header file in your dummy.c file. Do not declare these three routines in dummy.c.

Defining the Module Initialization Entry Point

The _init(9E) routine returns type int and takes no arguments. The _init(9E) routine must call the mod_install(9F) function and return the success or failure value that is returned by mod_install(9F).

The mod_install(9F) function takes an argument that is a modlinkage(9S) structure. See Defining the Module Linkage Structures for information about the modlinkage(9S) structure.

This driver is supposed to write a message each time an entry point is entered. Use the cmn_err(9F) function to write a message to a system log. The cmn_err(9F) function usually is used to report an error condition. The cmn_err(9F) function also is useful for debugging in the same way that you might use print statements in a user program. Be sure to remove cmn_err() calls that are used for development or debugging before you compile your production version driver. You might want to use cmn_err() calls in a production driver to write error messages that would be useful to a system administrator.

The cmn_err(9F) function requires you to include the cmn_err.h header file, the ddi.h header file, and the sunddi.h header file. The cmn_err(9F) function takes two arguments. The first argument is a constant that indicates the severity of the error message. The message written by this driver is not an error message but is simply a test message. Use CE_NOTE for the value of this severity constant. The second argument the cmn_err(9F) function takes is a string message.

The following code is the _init(9E) routine that you should enter into your dummy.c file. The ml structure is the modlinkage(9S) structure that is discussed in Defining the Module Linkage Structures.

int
_init(void)
{
    cmn_err(CE_NOTE, "Inside _init");
    return(mod_install(&ml));
}

Defining the Module Information Entry Point

The _info(9E) routine returns type int and takes an argument that is a pointer to an opaque modinfo structure. The _info(9E) routine must return the value that is returned by the mod_info(9F) function.

The mod_info(9F) function takes two arguments. The first argument to mod_info(9F) is a modlinkage(9S) structure. See Defining the Module Linkage Structures for information about the modlinkage(9S) structure. The second argument to mod_info(9F) is the same modinfo structure pointer that is the argument to the _info(9E) routine. The mod_info(9F) function returns the module information or returns zero if an error occurs.

Use the cmn_err(9F) function to write a message to the system log in the same way that you used the cmn_err(9F) function in your _init(9E) entry point.

The following code is the _info(9E) routine that you should enter into your dummy.c file. The ml structure is discussed in Defining the Module Linkage Structures. The modinfop argument is a pointer to an opaque structure that the system uses to pass module information.

int
_info(struct modinfo *modinfop)
{
    cmn_err(CE_NOTE, "Inside _info");
    return(mod_info(&ml, modinfop));
}

Defining the Module Unload Entry Point

The _fini(9E) routine returns type int and takes no arguments. The _fini(9E) routine must call the mod_remove(9F) function and return the success or failure value that is returned by mod_remove(9F).

When mod_remove(9F) is successful, the _fini(9E) routine must undo everything that the _init(9E) routine did. The _fini(9E) routine must call mod_remove(9F) because the _init(9E) routine called mod_install(9F). The _fini(9E) routine must deallocate anything that was allocated, close anything that was opened, and destroy anything that was created in the _init(9E) routine.

The _fini(9E) routine can be called at any time when a module is loaded. In normal operation, the _fini(9E) routine often fails. This behavior is normal because the kernel allows the module to determine whether the module can be unloaded. If mod_remove(9F) is successful, the module determines that devices were detached, and the module can be unloaded. If mod_remove(9F) fails, the module determines that devices were not detached, and the module cannot be unloaded.

    The following actions take place when mod_remove(9F) is called:

    • The kernel checks whether this driver is busy. This driver is busy if one of the following conditions is true:

    • A device node that is managed by this driver is open.

    • Another module that depends on this driver is open. A module depends on this driver if the module was linked using the –N option with this driver named as the argument to that –N option. See the ld(1) man page for more information.

  • If the driver is busy, then mod_remove(9F) fails and _fini(9E) fails.

  • If the driver is not busy, then the kernel calls the detach(9E) entry point of the driver.

    • If detach(9E) fails, then mod_remove(9F) fails and _fini(9E) fails.

    • If detach(9E) succeeds, then mod_remove(9F) succeeds, and _fini(9E) continues its cleanup work.

The mod_remove(9F) function takes an argument that is a modlinkage(9S) structure. See Defining the Module Linkage Structures for information about the modlinkage(9S) structure.

Use the cmn_err(9F) function to write a message to the system log in the same way that you used the cmn_err(9F) function in your _init(9E) entry point.

The following code is the _fini(9E) routine that you should enter into your dummy.c file. The ml structure is discussed in Defining the Module Linkage Structures.

int
_fini(void)
{
    cmn_err(CE_NOTE, "Inside _fini");
    return(mod_remove(&ml));
}

Including Loadable Module Configuration Header Files

The _init(9E), _info(9E), _fini(9E), and mod_install(9F) functions require you to include the modctl.h header file. The cmn_err(9F) function requires you to include the cmn_err.h header file, the ddi.h header file, and the sunddi.h header file.

The following header files are required by the three loadable module configuration routines that you have written in this section. Include this code near the top of your dummy.c file.

#include <sys/modctl.h>  /* used by _init, _info, _fini */
#include <sys/cmn_err.h> /* used by all entry points for this driver */
#include <sys/ddi.h>     /* used by all entry points for this driver */
#include <sys/sunddi.h>  /* used by all entry points for this driver */