Writing Device Drivers

Module Initialization Entry Points

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.

_init(9E)

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).

_fini(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).


Note -

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.


Example 15-1 SCSI HBA Module Initialization

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);
}