Go to main content

Writing Device Drivers for Oracle® Solaris 11.3

Exit Print View

Updated: March 2019

Data Structures Required for Drivers

The data structures in Figure 5-1 are relied on by the driver. These structures must be provided and be initialized correctly. Without these data structures, the driver might not load properly. As a result, the necessary routines might not be loaded. If an operation is not supported by the driver, the address of the nodev(9F) routine can be used as a placeholder. In some cases, the driver supports the entry point and only needs to return success or failure. In such cases, the address of the routine nulldev(9F) can be used.

Note - These structures should be initialized at compile-time. The driver should not access or change the structures at any other time.

modlinkage Structure

static struct modlinkage xxmodlinkage = {
    MODREV_1,       /* ml_rev */
    &xxmodldrv,     /* ml_linkage[] */
    NULL            /* NULL termination */

The first field is the version number of the module that loads the subsystem. This field should be MODREV_1. The second field points to driver's modldrv structure defined next. The last element of the structure should always be NULL.

modldrv Structure

static struct modldrv xxmodldrv = {
    &mod_driverops,           /* drv_modops */
    "generic driver v1.1",    /* drv_linkinfo */
    &xx_dev_ops               /* drv_dev_ops */

This structure describes the module in more detail. The first field provides information regarding installation of the module. This field should be set to &mod_driverops for driver modules. The second field is a string to be displayed by modinfo(1M). The second field should contain sufficient information for identifying the version of source code that generated the driver binary. The last field points to the driver's dev_ops structure defined in the following section.

dev_ops Structure

static struct dev_ops xx_dev_ops = {
    DEVO_REV,                  /* devo_rev */
    0,                         /* devo_refcnt  */
    xxgetinfo,                 /* devo_getinfo: getinfo(9E) */
    nulldev,                   /* devo_identify: identify(9E) */
    xxprobe,                   /* devo_probe: probe(9E) */
    xxattach,                  /* devo_attach: attach(9E) */
    xxdetach,                  /* devo_detach: detach(9E) */
    nodev,                     /* devo_reset: see devo_quiesce */
    &xx_cb_ops,                /* devo_cb_ops */
    NULL,                      /* devo_bus_ops */
    &xxpower,                  /* devo_power: power(9E) */
    ddi_quiesce_not_needed,    /* devo_quiesce: quiesce(9E) */

The dev_ops(9S) structure enables the kernel to find the autoconfiguration entry points of the device driver. The devo_rev field identifies the revision number of the structure. This field must be set to DEVO_REV. The devo_refcnt field must be initialized to zero. The function address fields should be filled in with the address of the appropriate driver entry point, except in the following cases:

  • Set the devo_identify field to nulldev(9F). The identify() entry point is obsolete.

  • Set the devo_probe field to nulldev(9F) if a probe(9E) routine is not needed.

  • Set the devo_reset field to nodev(9F). The nodev() function returns ENXIO. See devo_quiesce.

  • Set the devo_power field to NULL if a power() routine is not needed. Drivers for devices that provide Power Management functionality must have a power(9E) entry point. See Power Management.

  • Set the devo_quiesce field to ddi_quiesce_not_needed() if the driver does not need to implement quiesce. Drivers that manage devices must provide a quiesce(9E) entry point.

The devo_cb_ops member should include the address of the cb_ops(9S) structure. The devo_bus_ops field must be set to NULL.

cb_ops Structure

static struct cb_ops xx_cb_ops = {
    xxopen,         /* open(9E) */
    xxclose,        /* close(9E) */
    xxstrategy,     /* strategy(9E) */
    xxprint,        /* print(9E) */
    xxdump,         /* dump(9E) */
    xxread,         /* read(9E) */
    xxwrite,        /* write(9E) */
    xxioctl,        /* ioctl(9E) */
    xxdevmap,       /* devmap(9E) */
    nodev,          /* mmap(9E) */
    xxsegmap,       /* segmap(9E) */
    xxchpoll,       /* chpoll(9E) */
    xxprop_op,      /* prop_op(9E) */
    NULL,           /* streamtab(9S) */
    D_MP | D_64BIT, /* cb_flag */
    CB_REV,         /* cb_rev */
    xxaread,        /* aread(9E) */
    xxawrite        /* awrite(9E) */

The cb_ops(9S) structure contains the entry points for the character operations and block operations of the device driver. Any entry points that the driver does not support should be initialized to nodev(9F). For example, character device drivers should set all the block-only fields, such as cb_stategy, to nodev(9F). Note that the mmap(9E) entry point is maintained for compatibility with previous releases. Drivers should use the devmap(9E) entry point for device memory mapping. If devmap(9E) is supported, set mmap(9E) to nodev(9F).

The streamtab field indicates whether the driver is STREAMS-based. Only the network device drivers that are discussed in Drivers for Network Devices are STREAMS-based. All non-STREAMS-based drivers must set the streamtab field to NULL.

The cb_flag member contains the following flags:

  • The D_MP flag indicates that the driver is safe for multithreading. The Oracle Solaris OS supports only thread-safe drivers so D_MP must be set.

  • The D_64BIT flag causes the driver to use the uio_loffset field of the uio(9S) structure. The driver should set the D_64BIT flag in the cb_flag field to handle 64-bit offsets properly.

  • The D_DEVMAP flag supports the devmap(9E) entry point. For information on devmap(9E), see Mapping Device and Kernel Memory.

cb_rev is the cb_ops structure revision number. This field must be set to CB_REV.