JavaScript is required to for searching.
Skip Navigation Links
Exit Print View
Writing Device Drivers
search filter icon
search icon

Document Information

Preface

Part I Designing Device Drivers for the Solaris Platform

1.  Overview of Solaris Device Drivers

2.  Solaris Kernel and Device Tree

3.  Multithreading

4.  Properties

5.  Managing Events and Queueing Tasks

6.  Driver Autoconfiguration

7.  Device Access: Programmed I/O

Device Memory

Managing Differences in Device and Host Endianness

Managing Data Ordering Requirements

ddi_device_acc_attr Structure

Mapping Device Memory

Mapping Setup Example

Device Access Functions

Alternate Device Access Interfaces

Memory Space Access

I/O Space Access

PCI Configuration Space Access

8.  Interrupt Handlers

9.  Direct Memory Access (DMA)

10.  Mapping Device and Kernel Memory

11.  Device Context Management

12.  Power Management

13.  Hardening 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

19.  Drivers for Network Devices

20.  USB Drivers

Part III Building a Device Driver

21.  Compiling, Loading, Packaging, and Testing Drivers

22.  Debugging, Testing, and Tuning Device Drivers

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

Index

Device Memory

Devices that support programmed I/O are assigned one or more regions of bus address space that map to addressable regions of the device. These mappings are described as pairs of values in the reg property associated with the device. Each value pair describes a segment of a bus address.

Drivers identify a particular bus address mapping by specifying the register number, or regspec, which is an index into the devices' reg property. The reg property identifies the busaddr and size for the device. Drivers pass the register number when making calls to DDI functions such as ddi_regs_map_setup(9F). Drivers can determine how many mappable regions have been assigned to the device by calling ddi_dev_nregs(9F).

Managing Differences in Device and Host Endianness

The data format of the host can have different endian characteristics than the data format of the device. In such a case, data transferred between the host and device would need to be byte-swapped to conform to the data format requirements of the destination location. Devices with the same endian characteristics of the host require no byte-swapping of the data.

Drivers specify the endian characteristics of the device by setting the appropriate flag in the ddi_device_acc_attr(9S) structure that is passed to ddi_regs_map_setup(9F). The DDI framework then performs any required byte-swapping when the driver calls a ddi_getX routine like ddi_get8(9F) or a ddi_putX routine like ddi_put16(9F) to read or write to device memory.

Managing Data Ordering Requirements

Platforms can reorder loads and stores of data to optimize performance of the platform. Because reordering might not be allowed by certain devices, the driver is required to specify the device's ordering requirements when setting up mappings to the device.

ddi_device_acc_attr Structure

This structure describes the endian and data order requirements of the device. The driver is required to initialize and pass this structure as an argument to ddi_regs_map_setup(9F).

typedef struct ddi_device_acc_attr {
    ushort_t       devacc_attr_version;
    uchar_t    devacc_attr_endian_flags;
    uchar_t    devacc_attr_dataorder;
} ddi_device_acc_attr_t;
devacc_attr_version

Specifies DDI_DEVICE_ATTR_V0

devacc_attr_endian_flags

Describes the endian characteristics of the device. Specified as a bit value whose possible values are:

  • DDI_NEVERSWAP_ACC – Never swap data

  • DDI_STRUCTURE_BE_ACC – The device data format is big-endian

  • DDI_STRUCTURE_LE_ACC – The device data format is little-endian

devacc_attr_dataorder

Describes the order in which the CPU must reference data as required by the device. Specified as an enumerated value, where data access restrictions are ordered from most strict to least strict.

  • DDI_STRICTORDER_ACC – The host must issue the references in order, as specified by the programmer. This flag is the default behavior.

  • DDI_UNORDERED_OK_ACC – The host is allowed to reorder loads and stores to device memory.

  • DDI_MERGING_OK_ACC – The host is allowed to merge individual stores to consecutive locations. This setting also implies reordering.

  • DDI_LOADCACHING_OK_ACC – The host is allowed to read data from the device until a store occurs.

  • DDI_STORECACHING_OK_ACC – The host is allowed to cache data written to the device. The host can then defer writing the data to the device until a future time.


Note - The system can access data more strictly than the driver specifies in devacc_attr_dataorder. The restriction to the host diminishes while moving from strict data ordering to cache storing in terms of data accesses by the driver.


Mapping Device Memory

Drivers typically map all regions of a device during attach(9E). The driver maps a region of device memory by calling ddi_regs_map_setup(9F), specifying the register number of the region to map, the device access attributes for the region, an offset, and size. The DDI framework sets up the mappings for the device region and returns an opaque handle to the driver. This data access handle is passed as an argument to the ddi_get8(9F) or ddi_put8(9F) family of routines when reading data from or writing data to that region of the device.

The driver verifies that the shape of the device mappings match what the driver is expecting by checking the number of mappings exported by the device. The driver calls ddi_dev_nregs(9F) and then verifies the size of each mapping by calling ddi_dev_regsize(9F).

Mapping Setup Example

The following simple example demonstrates the DDI data access interfaces. This driver is for a fictional little endian device that accepts one character at a time and generates an interrupt when ready for another character. This device implements two register sets: the first is an 8-bit CSR register, and the second is an 8-bit data register.

Example 7-1 Mapping Setup

    #define CSR_REG 0
    #define DATA_REG 1
    /*
     * Initialize the device access attributes for the register
     * mapping
     */
    dev_acc_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
    dev_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
    dev_acc_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
    /*
     * Map in the csr register (register 0)
     */
    if (ddi_regs_map_setup(dip, CSR_REG, (caddr_t *)&(pio_p->csr), 0,
      sizeof (Pio_csr), &dev_acc_attr, &pio_p->csr_handle) != DDI_SUCCESS) {
    mutex_destroy(&pio_p->mutex);
    ddi_soft_state_free(pio_softstate, instance);
    return (DDI_FAILURE);
    }
    /*
     * Map in the data register (register 1)
     */
    if (ddi_regs_map_setup(dip, DATA_REG, (caddr_t *)&(pio_p->data), 0,
      sizeof (uchar_t), &dev_acc_attr, &pio_p->data_handle) \
            != DDI_SUCCESS) {
    mutex_destroy(&pio_p->mutex);
    ddi_regs_map_free(&pio_p->csr_handle);
    ddi_soft_state_free(pio_softstate, instance);
    return (DDI_FAILURE);
    }