Writing Modules

Modules provide services that can be used by other modules, by device drivers, and by the kernel itself. They can be dynamically loaded and unloaded by the kernel, as needed. If a client can't find a module it needs, it will still load, which gives it the opportunity to find another way to perform the desired tasks, or to disable those features of itself.

Modules, like drivers, export an API through a structure that provides pointers to the functions provided by the module, along with other information about the module. You do this by expanding upon the basic module definition in drivers/module.h. For example, you might define your module information structure like this:

#define MY_MODULE_NAME "generic/mymodule/v1"

struct my_module_info {
   module_info module;
   int32 (*function1)();
   int32 (*function2)();
   void (*configure)(int32 parameter, int32 value);
};

Note that the first field in your module information structure is a module_info, which looks like this:

struct module_info {
   const char*name;
   uint32 flags;
   status_t (*std_ops);
};

The name field should be a pointer to the driver's name as indicated in your module's header file (in this example, MY_MODULE_NAME).

The flags field specifies which flags should be in effect for your module. Currently, the B_KEEP_LOADED flag is the only one available; as expected, it tells the kernel not to unload your module when nobody is using it; normally, the first time your module is requested by someone calling get_module(), the kernel loads it. With each subsequent call to get_module(), a reference count is incremented. Every time put_module() is called to release the module, the reference count is decremented. When the counter reaches zero, the module is unloaded. B_KEEP_LOADED prevents unloading from taking place.

std_ops is a pointer to a function that your module must provide. This function is called to handle standard module operations. Currently, there are only two of these operations (initialization and uninitialization). Your module's std_ops() function will probably look something like this:

static status_t std_ops(int32 op, ...) {
   switch(op) {
      case B_MODULE_INIT:
         /* do whatever you need to do */
         break;
      case B_MODULE_UNINIT:
         /* do whatever you need to do */
         break;
      default:
         return B_ERROR;   /* necessary, for future expansion */
   }
   return B_OK;
}

It's important to return B_ERROR for any unknown operations, in case future versions of the kernel define additional operations.

Exporting your module to the outside world is similar to publishing device driver hooks, but since you define the hooks yourself, it's slightly more involved. Your module needs to have a filled-out version of your module's information structure, like this:

static struct my_module_info my_module {
   {
      MY_MODULE_NAME,   /* module name */
      0,   /* flags */
      std_ops
   },
   function1,
   function2,
   configure
};

When loading your module, the kernel looks for a symbol called "modules" that contains a list of pointers to the modules you export, terminated by a NULL:

_EXPORT module_info*modules[] = {
   (module_info*) &my_module,
   NULL
};

This is how the kernel finds out what modules are available for use by drivers (or by other modules). See the "Using Modules" section for details on how modules are accessed by other drivers or modules.


Module Functions

get_module

status_t get_module(const char* path,
                    module_info** info);

Declared in: drivers/module.h

The get_module() function obtains infomation about the module at the path specified and increases the modules internal reference count.

put_module

status_t put_module(const char* path);

Declared in: drivers/module.h

This function decreases the modules internal reference count and if the count is zero the module is unloaded from memory

Creative Commons License
Legal Notice
This work is licensed under a Creative Commons Attribution-Non commercial-No Derivative Works 3.0 License.