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

Locking Primitives

Storage Classes of Driver Data

Mutual-Exclusion Locks

Setting Up Mutexes

Using Mutexes

Readers/Writer Locks


Thread Synchronization

Condition Variables in Thread Synchronization

Initializing Condition Variables

Waiting for the Condition

Signaling the Condition

cv_wait() and cv_timedwait() Functions

cv_wait_sig() Function

cv_timedwait_sig() Function

Choosing a Locking Scheme

Potential Locking Pitfalls

Threads Unable to Receive Signals

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


Locking Primitives

In traditional UNIX systems, every section of kernel code terminates either through an explicit call to sleep(1) to give up the processor or through a hardware interrupt. The Solaris OS operates differently. A kernel thread can be preempted at any time to run another thread. Because all kernel threads share kernel address space and often need to read and modify the same data, the kernel provides a number of locking primitives to prevent threads from corrupting shared data. These mechanisms include mutual exclusion locks, which are also known as mutexes, readers/writer locks, and semaphores.

Storage Classes of Driver Data

The storage class of data is a guide to whether the driver might need to take explicit steps to control access to the data. The three data storage classes are:

Mutual-Exclusion Locks

A mutual-exclusion lock, or mutex, is usually associated with a set of data and regulates access to that data. Mutexes provide a way to allow only one thread at a time access to that data. The mutex functions are:


Releases any associated storage.


Acquires a mutex.


Releases a mutex.


Initializes a mutex.


Tests to determine whether the mutex is held by the current thread. To be used in ASSERT(9F) only.


Acquires a mutex if available, but does not block.

Setting Up Mutexes

Device drivers usually allocate a mutex for each driver data structure. The mutex is typically a field in the structure of type kmutex_t. mutex_init(9F) is called to prepare the mutex for use. This call is usually made at attach(9E) time for per-device mutexes and _init(9E) time for global driver mutexes.

For example,

struct xxstate *xsp;
/* ... */
mutex_init(&xsp->mu, NULL, MUTEX_DRIVER, NULL);
/* ... */

For a more complete example of mutex initialization, see Chapter 6, Driver Autoconfiguration.

The driver must destroy the mutex with mutex_destroy(9F) before being unloaded. Destroying the mutex is usually done at detach(9E) time for per-device mutexes and _fini(9E) time for global driver mutexes.

Using Mutexes

Every section of the driver code that needs to read or write the shared data structure must do the following tasks:

The scope of a mutex, that is, the data the mutex protects, is entirely up to the programmer. A mutex protects a data structure only if every code path that accesses the data structure does so while holding the mutex.

Readers/Writer Locks

A readers/writer lock regulates access to a set of data. The readers/writer lock is so called because many threads can hold the lock simultaneously for reading, but only one thread can hold the lock for writing.

Most device drivers do not use readers/writer locks. These locks are slower than mutexes. The locks provide a performance gain only when they protect commonly read data that is not frequently written. In this case, contention for a mutex could become a bottleneck, so using a readers/writer lock might be more efficient. The readers/writer functions are summarized in the following table. See the rwlock(9F) man page for detailed information. The readers/writer lock functions are:


Destroys a readers/writer lock


Downgrades a readers/writer lock holder from writer to reader


Acquires a readers/writer lock


Releases a readers/writer lock


Initializes a readers/writer lock


Determines whether a readers/writer lock is held for read or write


Attempts to acquire a readers/writer lock without waiting


Attempts to upgrade readers/writer lock holder from reader to writer


Counting semaphores are available as an alternative primitive for managing threads within device drivers. See the semaphore(9F) man page for more information. The semaphore functions are:


Destroys a semaphore.


Initialize a semaphore.


Decrement semaphore and possibly block.


Decrement semaphore but do not block if signal is pending. See Threads Unable to Receive Signals.


Attempt to decrement semaphore, but do not block.


Increment semaphore and possibly unblock waiter.