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:
#defineMY_MODULE_NAME
"generic/mymodule/v1" struct my_module_info { module_infomodule
; int32 (*function1
)(); int32 (*function2
)(); void (*configure
)(int32parameter
, int32value
); };
Note that the first field in your module information structure is a module_info, which looks like this:
struct module_info { const char*name
; uint32flags
; 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_tstd_ops
(int32op
, ...) { switch(op
) { caseB_MODULE_INIT
: /* do whatever you need to do */ break; caseB_MODULE_UNINIT
: /* do whatever you need to do */ break; default: returnB_ERROR
; /* necessary, for future expansion */ } returnB_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_infomy_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.