Driver Autoconfiguration
Autoconfiguration is the process of getting the driver's code and static data loaded into memory and registered with the system. It also involves configuring (attaching) individual device instances that are controlled by the driver.
This chapter provides information on the following subjects:
Driver Loading and Unloading
The system loads driver binary modules from the drv subdirectory of the kernel module directory for autoconfiguration (see "Copying the Driver to a Module Directory"). Once a module is read into memory with all symbols resolved, the system will call the _init(9E) entry point for that module. Upon successful completion of _init(), the driver is properly registered with the system, or "loaded." At this point, the driver is not actively managing any device; that will happen as part of device configuration.
The system unloads driver binary modules either to conserve system memory or at the explicit request of a user. Before deleting the driver code and data from memory, the _fini(9E) entry point of the driver is invoked. The driver is unloaded if and only if _fini() returns success.
Figure 5-1 illustrates a structural overview of a device driver. The shaded area of this figure highlights the driver data structures and entry points. The upper half of the shaded area contains data structures and entry points supporting driver loading and unloading; the lower half, driver configuration.
Figure 5-1 Module Loading and Autoconfiguration Entry Points
Data Structures Required for Drivers
Drivers are required to statically initialize a number of data structures to support autoconfiguration. These structures include modlinkage(9S), modldrv(9S), dev_ops(9S), and cb_ops(9S).
The data structures illustrated in Figure 5-1 must be provided and initialized correctly for the driver to load and for its routines to be called. If an operation is not supported by the driver, the address of the routine nodev(9F) can be used to fill it in. If the driver supports the entry point, but does not need to do anything except return success, the address of the routine nulldev(9F) can be used.
Note - These structures should be initialized at compile-time. They should not be accessed or changed by the driver 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 loading subsystem and 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 on how to install and uninstall the module. It should be set to &mod_driverops for driver modules. The second field is a string to be displayed by modinfo(1M). It 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, /* getinfo(9E) */ nulldev, /* identify(9E) */ xxprobe, /* probe(9E) */ xxattach, /* attach(9E) */ xxdetach, /* detach(9E) */ nodev, /* devo_reset */ &xx_cb_ops, /* devo_cb_ops */ NULL, /* devo_bus_ops */ &xxpower /* power(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 itself, and 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. The exceptions are:
If a probe(9E) routine is not needed, set the devo_probe field to nulldev(9F).
identify(9E) is obsolete and no longer required. Set this field to nulldev(9F).
Set devo_reset to nodev(9F).
Drivers for devices that provide Power Management functionality must have a power(9E) entry point. If a power(9E) routine is not needed, set this field to NULL.
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 and block operations of the device driver. Any entry points 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, and 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 this is a STREAMS-based driver. The device drivers discussed in this book (with the exception of Chapter 16, Drivers for Network Devices) are not STREAMS based. For a non-STREAMS-based driver, the streamtab field must be set to NULL.
The cb_flag member contains the following flags:
The D_MP flag indicates that the driver is safe for multi-threading. Solaris 9 operating environment only supports thread safe drivers, so, D_MP must be set.
If the driver properly handles 64-bit offsets, it should set the D_64BIT flag in the cb_flag field. This specifies that the driver will use the uio_loffset field of the uio(9S) structure.
If the driver supports the devmap(9E) entry point, it should set the D_DEVMAP flag. For information on devmap(9E), see Chapter 12, Mapping Device and Kernel Memory.
cb_rev is the cb_ops(9S) structure revision number. This field must be set to CB_REV.