Device drivers must be dynamically loadable and should 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. Example 5–1 shows a typical implementation of loadable driver interfaces.
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)); }
Example 5–2 shows a typical _init(9E) interface.
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, it 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, it should allocate resources specific to each printer instance in attach(9E).
Once _init(9E) has called mod_install(9F), the driver should not change any of the data structures attached to the modlinkage(9S) structure, as the system may make copies of them or change them.
The following example demonstrates the _fini(9E) 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() and must remove itself from the system module list.
_fini() may 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(9F) returns success.
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.