JavaScript is required to for searching.
Skip Navigation Links
Exit Print View
Writing Device Drivers     Oracle Solaris 10 1/13 Information Library
search filter icon
search icon

Document Information


Part I Designing Device Drivers for the Oracle Solaris Platform

1.  Overview of Oracle Solaris Device Drivers

2.  Oracle Solaris Kernel and Device Tree

3.  Multithreading

4.  Properties

5.  Managing Events and Queueing Tasks

6.  Driver Autoconfiguration

7.  Device Access: Programmed I/O

8.  Interrupt Handlers

9.  Direct Memory Access (DMA)

10.  Mapping Device and Kernel Memory

11.  Device Context Management

12.  Power Management

13.  Hardening Oracle Solaris Drivers

14.  Layered Driver Interface (LDI)

Part II Designing Specific Kinds of Device Drivers

15.  Drivers for Character Devices

16.  Drivers for Block Devices

17.  SCSI Target Drivers

18.  SCSI Host Bus Adapter Drivers

Introduction to Host Bus Adapter Drivers

SCSI Interface

SCSA HBA Interfaces

SCSA HBA Entry Point Summary

SCSA HBA Data Structures

scsi_hba_tran() Structure

scsi_address Structure

scsi_device Structure

scsi_pkt Structure (HBA)

Per-Target Instance Data

Transport Structure Cloning

SCSA HBA Functions

HBA Driver Dependency and Configuration Issues

Declarations and Structures

Per-Command Structure

Entry Points for Module Initialization

_init() Entry Point (SCSI HBA Drivers)

_fini() Entry Point (SCSI HBA Drivers)

Autoconfiguration Entry Points

attach() Entry Point (SCSI HBA Drivers)

detach() Entry Point (SCSI HBA Drivers)

Entry Points for SCSA HBA Drivers

Target Driver Instance Initialization

tran_tgt_init() Entry Point

tran_tgt_probe() Entry Point

tran_tgt_free() Entry Point

Resource Allocation

tran_init_pkt() Entry Point

Allocation and Initialization of a scsi_pkt(9S) Structure

Allocation of DMA Resources

Reallocation of DMA Resources for Data Transfer

tran_destroy_pkt() Entry Point

tran_sync_pkt() Entry Point

tran_dmafree() Entry Point

Command Transport

tran_start() Entry Point

Interrupt Handler and Command Completion

Timeout Handler

Capability Management

tran_getcap() Entry Point

tran_setcap() Entry Point

Abort and Reset Management

tran_abort() Entry Point

tran_reset() Entry Point

tran_bus_reset() Entry Point

tran_reset_notify() Entry Point

Dynamic Reconfiguration

SCSI HBA Driver Specific Issues

Installing HBA Drivers

HBA Configuration Properties

scsi-reset-delay Property

scsi-options Property

Per-Target scsi-options

x86 Target Driver Configuration Properties

Support for Queuing

19.  Drivers for Network Devices

20.  USB Drivers

21.  SR-IOV Drivers

Part III Building a Device Driver

22.  Compiling, Loading, Packaging, and Testing Drivers

23.  Debugging, Testing, and Tuning Device Drivers

24.  Recommended Coding Practices

Part IV Appendixes

A.  Hardware Overview

B.  Summary of Solaris DDI/DKI Services

C.  Making a Device Driver 64-Bit Ready

D.  Console Frame Buffer Drivers

E.  pci.conf File


HBA Driver Dependency and Configuration Issues

In addition to incorporating SCSA HBA entry points, structures, and functions into a driver, a developer must deal with driver dependency and configuration issues. These issues involve configuration properties, dependency declarations, state structure and per-command structure, entry points for module initialization, and autoconfiguration entry points.

Declarations and Structures

HBA drivers must include the following header files:

#include <sys/scsi/scsi.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

To inform the system that the module depends on SCSA routines, the driver binary must be generated with the following command. See SCSA HBA Interfaces for more information on SCSA routines.

% ld -r xx.o -o xx -N "misc/scsi"

The code samples are derived from a simplified isp driver for the QLogic Intelligent SCSI Peripheral device. The isp driver supports WIDE SCSI, with up to 15 target devices and 8 logical units (LUNs) per target.

Per-Command Structure

An HBA driver usually needs to define a structure to maintain state for each command submitted by a target driver. The layout of this per-command structure is entirely up to the device driver writer. The layout needs to reflect the capabilities and features of the hardware and the software algorithms that are used in the driver.

The following structure is an example of a per-command structure. The remaining code fragments of this chapter use this structure to illustrate the HBA interfaces.

struct isp_cmd {
     struct isp_request     cmd_isp_request;
     struct isp_response    cmd_isp_response;
     struct scsi_pkt        *cmd_pkt;
     struct isp_cmd         *cmd_forw;
     uint32_t               cmd_dmacount;
     ddi_dma_handle_t       cmd_dmahandle;
     uint_t                 cmd_cookie;
     uint_t                 cmd_ncookies;
     uint_t                 cmd_cookiecnt;
     uint_t                 cmd_nwin;
     uint_t                 cmd_curwin;
     off_t                  cmd_dma_offset;
     uint_t                 cmd_dma_len;
     ddi_dma_cookie_t       cmd_dmacookies[ISP_NDATASEGS];
     u_int                  cmd_flags;
     u_short                cmd_slot;
     u_int                  cmd_cdblen;
     u_int                  cmd_scblen;

Entry Points for Module Initialization

This section describes the entry points for operations that are 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() Entry Point (SCSI HBA Drivers)

The _init(9E) function initializes a loadable module. _init() is called before any other routine in the loadable module.

In a SCSI HBA, the _init() 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() returns a nonzero value,_init() should return this value. Otherwise, _init() 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() fails, the _init() function must free any global resources allocated. _init() must call scsi_hba_fini(9F) before returning.

The following example 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().

_fini() Entry Point (SCSI HBA Drivers)

The _fini(9E) function is called when the system is about to try to unload the SCSI HBA driver. The _fini() function must call mod_remove(9F) to determine whether the driver can be unloaded. If mod_remove() returns 0, the module can be unloaded. The HBA driver must deallocate any global resources allocated in _init(9E). The HBA driver must also call scsi_hba_fini(9F).

_fini() must return the value returned by mod_remove().

Note - The HBA driver must not free any resources or call scsi_hba_fini(9F) unless mod_remove(9F) returns 0.

Example 18-1 shows module initialization for SCSI HBA.

Example 18-1 Module Initialization for SCSI HBA

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     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",
        if ((err = mod_install(&modlinkage)) != 0) {
    return (err);

    int     err;
    if ((err = mod_remove(&modlinkage)) == 0) {
    return (err);

Autoconfiguration Entry Points

Associated with each device driver is a dev_ops(9S) structure, which enables the kernel to locate the autoconfiguration entry points of the driver. A complete description of these autoconfiguration routines is given in Chapter 6, Driver Autoconfiguration. This section describes only those entry points associated with operations performed by SCSI HBA drivers. These entry points include attach(9E) and detach(9E).

attach() Entry Point (SCSI HBA Drivers)

The attach(9E) entry point for a SCSI HBA driver performs several tasks when configuring and attaching an instance of the driver for the device. For a typical driver of real devices, the following operating system and hardware concerns must be addressed:

Soft-State Structure

When allocating the per-device-instance soft-state structure, a driver must clean up carefully if an error occurs.


The HBA driver must describe the attributes of its DMA engine by properly initializing the ddi_dma_attr_t structure.

static ddi_dma_attr_t isp_dma_attr = {
     DMA_ATTR_V0,        /* ddi_dma_attr version */
     0,                  /* low address */
     0xffffffff,         /* high address */
     0x00ffffff,         /* counter upper bound */
     1,                  /* alignment requirements */
     0x3f,               /* burst sizes */
     1,                  /* minimum DMA access */
     0xffffffff,         /* maximum DMA access */
     (1<<24)-1,          /* segment boundary restrictions */
     1,                  /* scatter-gather list length */
     512,                /* device granularity */
     0                   /* DMA flags */

The driver, if providing DMA, should also check that its hardware is installed in a DMA-capable slot:

if (ddi_slaveonly(dip) == DDI_SUCCESS) {
    return (DDI_FAILURE);
Transport Structure

The driver should further allocate and initialize a transport structure for this instance. The tran_hba_private field is set to point to this instance's soft-state structure. The tran_tgt_probe field can be set to NULL to achieve the default behavior, if no special probe customization is needed.

tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);

isp->isp_tran                   = tran;
isp->isp_dip                    = dip;

tran->tran_hba_private          = isp;
tran->tran_tgt_private          = NULL;
tran->tran_tgt_init             = isp_tran_tgt_init;
tran->tran_tgt_probe            = scsi_hba_probe;
tran->tran_tgt_free             = (void (*)())NULL;

tran->tran_start                = isp_scsi_start;
tran->tran_abort                = isp_scsi_abort;
tran->tran_reset                = isp_scsi_reset;
tran->tran_getcap               = isp_scsi_getcap;
tran->tran_setcap               = isp_scsi_setcap;
tran->tran_init_pkt             = isp_scsi_init_pkt;
tran->tran_destroy_pkt          = isp_scsi_destroy_pkt;
tran->tran_dmafree              = isp_scsi_dmafree;
tran->tran_sync_pkt             = isp_scsi_sync_pkt;
tran->tran_reset_notify         = isp_scsi_reset_notify;
tran->tran_bus_quiesce          = isp_tran_bus_quiesce
tran->tran_bus_unquiesce        = isp_tran_bus_unquiesce
tran->tran_bus_reset            = isp_tran_bus_reset
tran->tran_interconnect_type    = isp_tran_interconnect_type
Attaching an HBA Driver

The driver should attach this instance of the device, and perform error cleanup if necessary.

i = scsi_hba_attach_setup(dip, &isp_dma_attr, tran, 0);
if (i != DDI_SUCCESS) {
    /* do error recovery */
    return (DDI_FAILURE);
Register Mapping

The driver should map in its device's registers. The driver need to specify the following items:

ddi_device_acc_attr_t    dev_attributes;

     dev_attributes.devacc_attr_version = DDI_DEVICE_ATTR_V0;
     dev_attributes.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
     dev_attributes.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;

     if (ddi_regs_map_setup(dip, 0, (caddr_t *)&isp->isp_reg,
     0, sizeof (struct ispregs), &dev_attributes,
     &isp->isp_acc_handle) != DDI_SUCCESS) {
        /* do error recovery */
        return (DDI_FAILURE);
Adding an Interrupt Handler

The driver must first obtain the iblock cookie to initialize any mutexes that are used in the driver handler. Only after those mutexes have been initialized can the interrupt handler be added.

i = ddi_get_iblock_cookie(dip, 0, &isp->iblock_cookie};
if (i != DDI_SUCCESS) {
    /* do error recovery */
    return (DDI_FAILURE);

mutex_init(&isp->mutex, "isp_mutex", MUTEX_DRIVER,
(void *)isp->iblock_cookie);
i = ddi_add_intr(dip, 0, &isp->iblock_cookie,
0, isp_intr, (caddr_t)isp);
if (i != DDI_SUCCESS) {
    /* do error recovery */
    return (DDI_FAILURE);

If a high-level handler is required, the driver should be coded to provide such a handler. Otherwise, the driver must be able to fail the attach. See Handling High-Level Interrupts for a description of high-level interrupt handling.

Create Power Manageable Components

With power management, if the host bus adapter only needs to power down when all target adapters are at power level 0, the HBA driver only needs to provide a power(9E) entry point. Refer to Chapter 12, Power Management. The HBA driver also needs to create a pm-components(9P) property that describes the components that the device implements.

Nothing more is necessary, since the components will default to idle, and the power management framework's default dependency processing will ensure that the host bus adapter will be powered up whenever an target adapter is powered up. Provided that automatic power management is enabled automatically, the processing will also power down the host bus adapter when all target adapters are powered down ().

Report Attachment Status

Finally, the driver should report that this instance of the device is attached and return success.

    return (DDI_SUCCESS);

detach() Entry Point (SCSI HBA Drivers)

The driver should perform standard detach operations, including calling scsi_hba_detach(9F).