O'Reilly logo

Linux Device Drivers, Second Edition by Alessandro Rubini, Jonathan Corbet

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Intermodule Communication

Very late in the pre-2.4.0 development series, the kernel developers added a new interface providing limited communication between modules. This intermodule scheme allows modules to register strings pointing to data of interest, which can be retrieved by other modules. We’ll look briefly at this interface, using a variation of our master and slave modules.

We use the same master module, but introduce a new slave module called inter. All inter does is to make a string and a function available under the name ime_string (ime means “intermodule example”) and ime_function; it looks, in its entirety, as follows:

static char *string = "inter says 'Hello World'";

void ime_function(const char *who)
{
    printk(KERN_INFO "inter: ime_function called by %s\n", who);
}

int ime_init(void)
{
    inter_module_register("ime_string", THIS_MODULE, string);
    inter_module_register("ime_function", THIS_MODULE, ime_function);
    return 0;
}

void ime_cleanup(void)
{
    inter_module_unregister("ime_string");
    inter_module_unregister("ime_function");
}

This code uses inter_module_register, which has this prototype:

void inter_module_register(const char *string, struct module *module,
                           const void *data);

string is the string other modules will use to find the data; module is a pointer to the module owning the data, which will almost always be THIS_MODULE; and data is a pointer to whatever data is to be shared. Note the use of a const pointer for the data; it is assumed that it will be exported in a read-only mode. inter_module_register will complain (via printk) if the given string is already registered.

When the data is no longer to be shared, the module should call inter_module_unregister to clean it up:

void inter_module_unregister(const char *string);

Two functions are exported that can access data shared via inter_module_register:

const void *inter_module_get(const char *string);

This function looks up the given string and returns the associated data pointer. If the string has not been registered, NULL is returned.

const void *inter_module_get_request(const char *string, const char *module);

This function is like inter_module_get with the added feature that, if the given string is not found, it will call request_module with the given module name and then will try again.

Both functions also increment the usage count for the module that registered the data. Thus, a pointer obtained with inter_module_get or inter_module_get_request will remain valid until it is explicitly released. At least, the module that created that pointer will not be unloaded during that time; it is still possible for the module itself to do something that will invalidate the pointer.

When you are done with the pointer, you must release it so that the other module’s usage count will be decremented properly. A simple call to

void inter_module_put(const char *string);

will release the pointer, which should not be used after this call.

In our sample master module, we call inter_module_get_request to cause the inter module to be loaded and to obtain the two pointers. The string is simply printed, and the function pointer is used to make a call from master into inter. The additional code in master looks like this:

static const char *ime_string = NULL;
static void master_test_inter();

void master_test_inter()
{
    void (*ime_func)();
    ime_string = inter_module_get_request("ime_string", "inter");
    if (ime_string)
        printk(KERN_INFO "master: got ime_string '%s'\n", ime_string);
    else
        printk(KERN_INFO "master: inter_module_get failed");
    ime_func = inter_module_get("ime_function");
    if (ime_func) {
	(*ime_func)("master");
	inter_module_put("ime_function");
    }
}	

void master_cleanup_module(void)
{
    if (ime_string)
        inter_module_put("ime_string");
}

Note that one of the calls to inter_module_put is deferred until module cleanup time. This will cause the usage count of inter to be (at least) 1 until master is unloaded.

There are a few other worthwhile details to keep in mind when using the intermodule functions. First, they are available even in kernels that have been configured without support for loadable modules, so there is no need for a bunch of #ifdef lines to test for that case. The namespace implemented by the intermodule communication functions is global, so names should be chosen with care or conflicts will result. Finally, intermodule data is stored in a simple linked list; performance will suffer if large numbers of lookups are made or many strings are stored. This facility is intended for light use, not as a general dictionary subsystem.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required