Drivers: Writing Modules

Drivers Table of Contents | Drivers Index

# 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 be/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.

Drivers Table of Contents | Drivers Index

The Be Book,
...in lovely HTML...
for BeOS Release 5.

Copyright © 2000 Be, Inc. All rights reserved..