Skip Navigation Links | |
Exit Print View | |
![]() |
Writing Device Drivers Oracle Solaris 10 1/13 Information Library |
Part I Designing Device Drivers for the Oracle Solaris Platform
1. Overview of Oracle Solaris Device Drivers
2. Oracle Solaris Kernel and Device Tree
5. Managing Events and Queueing Tasks
7. Device Access: Programmed I/O
10. Mapping Device and Kernel Memory
13. Hardening Oracle Solaris Drivers
14. Layered Driver Interface (LDI)
Part II Designing Specific Kinds of Device Drivers
15. Drivers for Character Devices
Overview of the Character Driver Structure
Character Device Autoconfiguration
Differences Between Synchronous and Asynchronous I/O
Multiplexing I/O on File Descriptors
ioctl() Entry Point (Character Drivers)
I/O Control Support for 64-Bit Capable Device Drivers
32-bit and 64-bit Data Structure Macros
How Do the Structure Macros Work?
Declaring and Initializing Structure Handles
Operations on Structure Handles
18. SCSI Host Bus Adapter Drivers
19. Drivers for Network Devices
Part III Building a Device Driver
22. Compiling, Loading, Packaging, and Testing Drivers
23. Debugging, Testing, and Tuning Device Drivers
24. Recommended Coding Practices
B. Summary of Solaris DDI/DKI Services
C. Making a Device Driver 64-Bit Ready
Access to a device by one or more application programs is controlled through the open(9E) and close(9E) entry points. An open(2) system call to a special file that represents a character device always causes a call to the open(9E) routine for the driver. For a particular minor device, open(9E) can be called many times. The close(9E) routine is called only when the final reference to a device is removed. If the device is accessed through file descriptors, the final call to close(9E) can occur as a result of a close(2) or exit(2) system call. If the device is accessed through memory mapping, the final call to close(9E) can occur as a result of a munmap(2) system call.
The primary function of open() is to verify that the open request is allowed. The syntax for open(9E) is as follows:
int xxopen(dev_t *devp, int flag, int otyp, cred_t *credp);
where:
Pointer to a device number. The open() routine is passed a pointer so that the driver can change the minor number. With this pointer, drivers can dynamically create minor instances of the device. An example would be a pseudo terminal driver that creates a new pseudo-terminal whenever the driver is opened. A driver that dynamically chooses the minor number normally creates only one minor device node in attach(9E) with ddi_create_minor_node(9F), then changes the minor number component of *devp using makedevice(9F) and getmajor(9F):
*devp = makedevice(getmajor(*devp), new_minor);
You do not have to call ddi_create_minor_node(9F) for the new minor. A driver must not change the major number of *devp. The driver must keep track of available minor numbers internally.
Flag with bits to indicate whether the device is opened for reading (FREAD), writing (FWRITE), or both. User threads issuing the open(2) system call can also request exclusive access to the device (FEXCL) or specify that the open should not block for any reason (FNDELAY), but the driver must enforce both cases. A driver for a write-only device such as a printer might consider an open(9E) for reading invalid.
Integer that indicates how open() was called. The driver must check that the value of otyp is appropriate for the device. For character drivers, otyp should be OTYP_CHR (see the open(9E) man page).
Pointer to a credential structure containing information about the caller, such as the user ID and group IDs. Drivers should not examine the structure directly, but should instead use drv_priv(9F) to check for the common case of root privileges. In this example, only root or a user with the PRIV_SYS_DEVICES privilege is allowed to open the device for writing.
The following example shows a character driver open(9E) routine.
Example 15-2 Character Driver open(9E) Routine
static int xxopen(dev_t *devp, int flag, int otyp, cred_t *credp) { minor_t instance; if (getminor(*devp) /* if device pointer is invalid */ return (EINVAL); instance = getminor(*devp); /* one-to-one example mapping */ /* Is the instance attached? */ if (ddi_get_soft_state(statep, instance) == NULL) return (ENXIO); /* verify that otyp is appropriate */ if (otyp != OTYP_CHR) return (EINVAL); if ((flag & FWRITE) && drv_priv(credp) == EPERM) return (EPERM); return (0); }
The syntax for close(9E) is as follows:
int xxclose(dev_t dev, int flag, int otyp, cred_t *credp);
close() should perform any cleanup necessary to finish using the minor device, and prepare the device (and driver) to be opened again. For example, the open routine might have been invoked with the exclusive access (FEXCL) flag. A call to close(9E) would allow additional open routines to continue. Other functions that close(9E) might perform are:
Waiting for I/O to drain from output buffers before returning
Rewinding a tape (tape device)
Hanging up the phone line (modem device)
A driver that waits for I/O to drain could wait forever if draining stalls due to external conditions such as flow control. See Threads Unable to Receive Signals for information about how to avoid this problem.