Character Drivers

Character drivers are basically KLDs that create character devices. As mentioned previously, character devices provide either a character-stream-oriented I/O interface or, alternatively, an unstructured (raw) interface. These (character-device) interfaces establish the conventions for accessing a device, which include the set of procedures that can be called to do I/O operations (McKusick and Neville-Neil, 2005). In short, character drivers produce character devices, which provide device access. For example, the lpt(4) driver creates the /dev/lpt0 character device, which is used to access the parallel port printer. In FreeBSD 4.0 and later, most devices have a character-device interface.

In general, three components are common to all character drivers:

  • The d_foo functions

  • A character device switch table

  • A make_dev and destroy_dev function call

d_foo Functions

The d_foo functions, whose function prototypes are defined in the <sys/conf.h> header, are the I/O operations that a process can execute on a device. These I/O operations are mostly associated with the file I/O system calls and are accordingly named d_open, d_read, and so on. A character driver’s d_foo function is called when “foo” is done on its device. For example, d_read is called when a process reads from a device.

Table 1-1 provides a brief description of each d_foo function.

Table 1-1. d_foo Functions

Function

Description

d_open

Called to open the device in preparation for I/O operations

d_close

Called to close the device

d_read

Called to read data from the device

d_write

Called to write data to the device

d_ioctl

Called to perform an operation other than a read or a write

d_poll

Called to check the device to see whether data is available for reading or space is available for writing

d_mmap

Called to map a device offset into a memory address

d_kqfilter

Called to register the device with a kernel event list

d_strategy

Called to start a read or write operation and then immediately return

d_dump

Called to write all physical memory to the device

Note

If you don’t understand some of these operations, don’t worry; we’ll describe them in detail later when we implement them.

Character Device Switch Table

A character device switch table, struct cdevsw, specifies which d_foo functions a character driver implements. It is defined in the <sys/conf.h> header as follows:

struct cdevsw {
        int                     d_version;
        u_int                   d_flags;
        const char              *d_name;
        d_open_t                *d_open;
        d_fdopen_t              *d_fdopen;
        d_close_t               *d_close;
        d_read_t                *d_read;
        d_write_t               *d_write;
        d_ioctl_t               *d_ioctl;
        d_poll_t                *d_poll;
        d_mmap_t                *d_mmap;
        d_strategy_t            *d_strategy;
        dumper_t                *d_dump;
        d_kqfilter_t            *d_kqfilter;
        d_purge_t               *d_purge;
        d_spare2_t              *d_spare2;
        uid_t                   d_uid;
        gid_t                   d_gid;
        mode_t                  d_mode;
        const char              *d_kind;

        /* These fields should not be messed with by drivers. */
        LIST_ENTRY(cdevsw)      d_list;
        LIST_HEAD(, cdev)       d_devs;
        int                     d_spare3;
        struct cdevsw           *d_gianttrick;
};

Here is an example character device switch table for a read/write device:

static struct cdevsw echo_cdevsw = {
        .d_version =    D_VERSION,
        .d_open =       echo_open,
        .d_close =      echo_close,
        .d_read =       echo_read,
        .d_write =      echo_write,
        .d_name =       "echo"
};

As you can see, not every d_foo function or attribute needs to be defined. If a d_foo function is undefined, the corresponding operation is unsupported (for example, a character device switch table for a read-only device would not define d_write).

Unsurprisingly, d_version (which denotes the version of FreeBSD this driver supports) and d_name (which is the driver’s name) must be defined. Generally, d_version is set to D_VERSION, which is a macro substitution for whichever version of FreeBSD it’s compiled on.

make_dev and destroy_dev Functions

The make_dev function takes a character device switch table and creates a character device node under /dev. Here is its function prototype:

#include <sys/param.h>
#include <sys/conf.h>

struct cdev *
make_dev(struct cdevsw *cdevsw, int minor, uid_t uid, gid_t gid,
    int perms, const char *fmt, ...);

Conversely, the destroy_dev function takes the cdev structure returned by make_dev and destroys the character device node. Here is its function prototype:

#include <sys/param.h>
#include <sys/conf.h>

void
destroy_dev(struct cdev *dev);
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.15.94