Loadable Driver Interfaces
Device drivers must be dynamically loadable. Drivers should also be unloadable to help
conserve memory resources. Drivers that can be unloaded are also easier to test,
debug, and patch.
Each device driver is required to implement _init(9E), _fini(9E), and _info(9E) entry points
to support driver loading and unloading. The following example shows a typical implementation of
loadable driver interfaces.
Example 6-1 Loadable Interface Section
static void *statep; /* for soft state routines */
static struct cb_ops xx_cb_ops; /* forward reference */
static struct dev_ops xx_ops = {
DEVO_REV,
0,
xxgetinfo,
nulldev,
xxprobe,
xxattach,
xxdetach,
xxreset,
nodev,
&xx_cb_ops,
NULL,
xxpower
};
static struct modldrv modldrv = {
&mod_driverops,
"xx driver v1.0",
&xx_ops
};
static struct modlinkage modlinkage = {
MODREV_1,
&modldrv,
NULL
};
int
_init(void)
{
int error;
ddi_soft_state_init(&statep, sizeof (struct xxstate),
estimated_number_of_instances);
/* further per-module initialization if necessary */
error = mod_install(&modlinkage);
if (error != 0) {
/* undo any per-module initialization done earlier */
ddi_soft_state_fini(&statep);
}
return (error);
}
int
_fini(void)
{
int error;
error = mod_remove(&modlinkage);
if (error == 0) {
/* release per-module resources if any were allocated */
ddi_soft_state_fini(&statep);
}
return (error);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
_init() Example
The following example shows a typical _init(9E) interface.
Example 6-2 _init() Function
static void *xxstatep;
int
_init(void)
{
int error;
const int max_instance = 20; /* estimated max device instances */
ddi_soft_state_init(&xxstatep, sizeof (struct xxstate), max_instance);
error = mod_install(&xxmodlinkage);
if (error != 0) {
/*
* Cleanup after a failure
*/
ddi_soft_state_fini(&xxstatep);
}
return (error);
}
The driver should perform any one-time resource allocation or data initialization during driver loading
in _init(). For example, the driver should initialize any mutexes global to the
driver in this routine. The driver should not, however, use _init(9E) to allocate
or initialize anything that has to do with a particular instance of the
device. Per-instance initialization must be done in attach(9E). For example, if a driver for
a printer can handle more than one printer at the same time, that
driver should allocate resources specific to each printer instance in attach().
Note - Once _init(9E) has called mod_install(9F), the driver should not change any of the
data structures attached to the modlinkage(9S) structure because the system might make copies
or change the data structures.
_fini() Example
The following example demonstrates the _fini() routine.
int
_fini(void)
{
int error;
error = mod_remove(&modlinkage);
if (error != 0) {
return (error);
}
/*
* Cleanup resources allocated in _init()
*/
ddi_soft_state_fini(&xxstatep);
return (0);
}
Similarly, in _fini(), the driver should release any resources that were allocated in
_init(). The driver must remove itself from the system module list.
Note - _fini() might be called when the driver is attached to hardware instances. In
this case, mod_remove(9F) returns failure. Therefore, driver resources should not be released until
mod_remove() returns success.
_info() Example
The following example demonstrates the _info(9E) routine.
int
_info(struct modinfo *modinfop)
{
return (mod_info(&xxmodlinkage, modinfop));
}
The driver is called to return module information. The entry point should be
implemented as shown above.