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

Document Information


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

8.  Interrupt Handlers

9.  Direct Memory Access (DMA)

10.  Mapping Device and Kernel Memory

Memory Mapping Overview

Exporting the Mapping

The segmap(9E) Entry Point

The devmap(9E) Entry Point

Associating Device Memory With User Mappings

Associating Kernel Memory With User Mappings

Allocating Kernel Memory for User Access

Exporting Kernel Memory to Applications

Freeing Kernel Memory Exported for User Access

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


Exporting the Mapping

This section describes how to use the segmap(9E) and devmap(9E) entry points.

The segmap(9E) Entry Point

The segmap(9E) entry point is responsible for setting up a memory mapping requested by an mmap(2) system call. Drivers for many memory-mapped devices use ddi_devmap_segmap(9F) as the entry point rather than defining their own segmap(9E) routine. By providing a segmap() entry point, a driver can take care of general tasks before or after creating the mapping. For example, the driver can check mapping permissions and allocate private mapping resources. The driver can also make adjustments to the mapping to accommodate non-page-aligned device buffers. The segmap() entry point must call the ddi_devmap_segmap(9F) function before returning. The ddi_devmap_segmap() function calls the driver's devmap(9E) entry point to perform the actual mapping.

The segmap() function has the following syntax:

int segmap(dev_t dev, off_t off, struct as *asp, caddr_t *addrp,
     off_t len, unsigned int prot, unsigned int maxprot,
     unsigned int flags, cred_t *credp);



Device whose memory is to be mapped.


Offset within device memory at which mapping begins.


Pointer to the address space into which the device memory should be mapped.

Note that this argument can be either a struct as *, as shown in Example 10-1, or a ddi_as_handle_t, as shown in Example 10-2. This is because ddidevmap.h includes the following declaration:

typedef struct as *ddi_as_handle_t

Pointer to the address in the address space to which the device memory should be mapped.


Length (in bytes) of the memory being mapped.


A bit field that specifies the protections. Possible settings are PROT_READ, PROT_WRITE, PROT_EXEC, PROT_USER, and PROT_ALL. See the man page for details.


Maximum protection flag possible for attempted mapping. The PROT_WRITE bit can be masked out if the user opened the special file read-only.


Flags that indicate the type of mapping. Possible values include MAP_SHARED and MAP_PRIVATE.


Pointer to the user credentials structure.

In the following example, the driver controls a frame buffer that allows write-only mappings. The driver returns EINVAL if the application tries to gain read access and then calls ddi_devmap_segmap(9F) to set up the user mapping.

Example 10-1 segmap(9E) Routine

static int
xxsegmap(dev_t dev, off_t off, struct as *asp, caddr_t *addrp,
    off_t len, unsigned int prot, unsigned int maxprot,
    unsigned int flags, cred_t *credp)
    if (prot & PROT_READ)
        return (EINVAL);
    return (ddi_devmap_segmap(dev, off, as, addrp,
        len, prot, maxprot, flags, cred));

The following example shows how to handle a device that has a buffer that is not page-aligned in its register space. This example maps a buffer that starts at offset 0x800, so that mmap(2) returns an address that corresponds to the start of the buffer. The devmap_devmem_setup(9F) function maps entire pages, requires the mapping to be page aligned, and returns an address to the start of a page. If this address is passed through segmap(9E), or if no segmap() entry point is defined, mmap() returns the address that corresponds to the start of the page, not the address that corresponds to the start of the buffer. In this example, the buffer offset is added to the page-aligned address that was returned by devmap_devmem_setup so that the resulting address returned is the desired start of the buffer.

Example 10-2 Using the segmap() Function to Change the Address Returned by the mmap() Call

#define    BUFFER_OFFSET 0x800

xx_segmap(dev_t dev, off_t off, ddi_as_handle_t as, caddr_t *addrp, off_t len,
    uint_t prot, uint_t maxprot, uint_t flags, cred_t *credp)
        int rval;
        unsigned long pagemask = ptob(1L) - 1L;

        if ((rval = ddi_devmap_segmap(dev, off, as, addrp, len, prot, maxprot,
            flags, credp)) == DDI_SUCCESS) {
                  * The address returned by ddi_devmap_segmap is the start of the page
                  * that contains the buffer.  Add the offset of the buffer to get the
                  * final address.
                *addrp += BUFFER_OFFSET & pagemask);
        return (rval);

The devmap(9E) Entry Point

The devmap(9E) entry point is called from the ddi_devmap_segmap(9F) function inside the segmap(9E) entry point.

The devmap(9E) entry point is called as a result of the mmap(2) system call. The devmap(9E) function is called to export device memory or kernel memory to user applications. The devmap() function is used for the following operations:

The devmap() function has the following syntax:

int devmap(dev_t dev, devmap_cookie_t handle, offset_t off,
     size_t len, size_t *maplen, uint_t model);



Device whose memory is to be mapped.


Device-mapping handle that the system creates and uses to describe a mapping to contiguous memory in the device or kernel.


Logical offset within the application mapping that has to be translated by the driver to the corresponding offset within the device or kernel memory.


Length (in bytes) of the memory being mapped.


Enables driver to associate different kernel memory regions or multiple physically discontiguous memory regions with one contiguous user application mapping.


Data model type of the current thread.

The system creates multiple mapping handles in one mmap(2) system call. For example, the mapping might contain multiple physically discontiguous memory regions.

Initially, devmap(9E) is called with the parameters off and len. These parameters are passed by the application to mmap(2). devmap(9E) sets *maplen to the length from off to the end of a contiguous memory region. The *maplen value must be rounded up to a multiple of a page size. The *maplen value can be set to less than the original mapping length len. If so, the system uses a new mapping handle with adjusted off and len parameters to call devmap(9E) repeatedly until the initial mapping length is satisfied.

If a driver supports multiple application data models, model must be passed to ddi_model_convert_from(9F). The ddi_model_convert_from() function determines whether a data model mismatch exists between the current thread and the device driver. The device driver might have to adjust the shape of data structures before exporting the structures to a user thread that supports a different data model. See Appendix C, Making a Device Driver 64-Bit Ready page for more details.

The devmap(9E) entry point must return -1 if the logical offset, off, is out of the range of memory exported by the driver.