Writing Device Drivers
Previous Next

Device Access (Character Drivers)

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

open() Entry Point (Character Drivers)

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:

devp

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

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.

otyp

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

credp

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);
}

close() Entry Point (Character Drivers)

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.

Previous Next