Drivers for different types of devices have different sets of entry points, depending on the operations they perform. Some operations, however, are common to all drivers, such as the as _init(9E), _info(9E), and _fini(9E) entry points for module initialization. Chapter 2, Overview of Solaris Device Drivers gives a complete description of these loadable module routines. This section describes only those entry points associated with operations performed by SCSI HBA drivers.
The following code for a SCSI HBA driver illustrates a representative dev_ops(9S) structure. The driver must initialize the devo_bus_ops field in this structure to NULL. A SCSI HBA driver can provide leaf driver interfaces for special purposes, in which case the devo_cb_ops field might point to a cb_ops(9S) structure. In this example, no leaf driver interfaces are exported, so the devo_cb_ops field is initialized to NULL.
The _init(9E) function initializes a loadable module and is called before any other routine in the loadable module.
In a SCSI HBA, the _init(9E) function must call scsi_hba_init(9F) to inform the framework of the existence of the HBA driver before calling mod_install(9F). If scsi_hba_init(9F) returns a nonzero value, _init(9E) should return this value. Otherwise, _init(9E) must return the value returned by mod_install(9F).
The driver should initialize any required global state before calling mod_install(9F).
If mod_install(9F) fails, the _init(9E) function must free any global resources allocated and must call scsi_hba_fini(9F) before returning.
The following code sample uses a global mutex to show how to allocate data that is global to all instances of a driver. The code declares global mutex and soft-state structure information. The global mutex and soft state are initialized during _init(9E).
The _fini(9E) function is called when the system is about to try to unload the SCSI HBA driver. The _fini(9E) function must call mod_remove(9F) to determine if the driver can be unloaded. If mod_remove(9F) returns 0, the module can be unloaded, and the HBA driver must deallocate any global resources allocated in _init(9E) and must call scsi_hba_fini(9F).
_fini(9E) must return the value returned by mod_remove(9F).
The HBA driver must not free any resources or call scsi_hba_fini(9F) unless mod_remove(9F) returns 0.
Example 15-1 shows SCSI HBA module intialization.
static struct dev_ops isp_dev_ops = { DEVO_REV, /* devo_rev */ 0, /* refcnt */ isp_getinfo, /* getinfo */ nulldev, /* probe */ isp_attach, /* attach */ isp_detach, /* detach */ nodev, /* reset */ NULL, /* driver operations */ NULL, /* bus operations */ isp_power, /* power management */ }; /* * Local static data */ static kmutex_t isp_global_mutex; static void *isp_state; int _init(void) { int err; if ((err = ddi_soft_state_init(&isp_state, sizeof (struct isp), 0)) != 0) { return (err); } if ((err = scsi_hba_init(&modlinkage)) == 0) { mutex_init(&isp_global_mutex, "isp global mutex", MUTEX_DRIVER, NULL); if ((err = mod_install(&modlinkage)) != 0) { mutex_destroy(&isp_global_mutex); scsi_hba_fini(&modlinkage); ddi_soft_state_fini(&isp_state); } } return (err); } int _fini(void) { int err; if ((err = mod_remove(&modlinkage)) == 0) { mutex_destroy(&isp_global_mutex); scsi_hba_fini(&modlinkage); ddi_soft_state_fini(&isp_state); } return (err); }