[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


8    Implementing Character Device Driver Interfaces

Character device drivers can contain the following sections:

The following sections explain how to implement or set up each of these interfaces.


[Return to Library] [Contents] [Previous Chapter] [Next Section] [Next Chapter] [Index] [Help]


8.1    Implementing the read Interface

A device driver's read interface performs the tasks necessary to read data from a device. The kernel calls the driver's read interface for character device drivers on behalf of applications that make a read system call. The code associated with a read interface resides in the read and write device section of the device driver. You specify the entry point for the driver's read interface in a dsent structure. Section 6.6.5.1 describes the dsent structure. Writing Device Drivers: Reference provides a reference page that gives additional information on the arguments and tasks associated with a read interface.

To implement a read interface you must understand the dev_t data type and the uio data structure. Section 8.1.1 and Section 8.1.2 discuss these topics. Section 8.1.1 discusses how to set up a read interface.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.1.1    Using the dev_t Data Type

The dev_t data type is a type definition of the major and minor device numbers. Device driver writers use the dev_t data type to represent a device's major and minor numbers. This data type is an abstraction of the internal representations of the major and minor numbers. Driver writers do not need to know how the system internally represents the major and minor numbers. To ensure maximum portability of the device driver, use the major interface to extract the major number portion of this internal representation and use the minor interface to extract the minor number portion of this internal representation.

The following character driver interfaces take a dev_t as the first argument: read, write, select, and mmap (memory map). The following sections describe how to use the major and minor interfaces.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.1.1.1    Getting the Device Major Number with major

To get the device major number, call the major interface. The following code fragment shows a call to this interface:


.
.
.
xxread(dev, uio, flag) dev_t dev; [1] struct uio *uio; int flag;
.
.
.
int major_number = major(dev); [2]
.
.
.

  1. Declares an argument that specifies the major and minor numbers for a specific XX device. The major and minor number is passed to the xxread interface by the kernel as a result of an application's read system call. [Return to example]

  2. Shows that the major interface takes one argument: the number of the device whose associated major device number the major interface will obtain. Upon successful completion, major returns the major number portion of the dev_t passed as the argument. In this example, the major device number is stored in the major_number variable. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.1.1.2    Getting the Device Minor Number with minor

To get the device minor number, call the minor interface. The following code fragment shows a call to this interface:

xxread(dev, uio, flag)
dev_t dev; [1]
struct uio *uio;
int flag;

.
.
.
int unit = minor(dev); [2]

.
.
.

  1. Declares an argument that specifies the major and minor numbers for a specific XX device. The major and minor number is passed to the xxread interface by the kernel as a result of an application's read system call. The minor device number is used to determine the logical unit number for the XX device on which the read operation is performed. [Return to example]

  2. Shows that the minor interface takes one argument: the number of the device whose associated minor device number the minor interface will obtain. Upon successful completion, minor returns the minor number portion of the dev_t passed as the argument. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.1.2    Using the uio Structure

The uio structure describes I/O, either single vector or multiple vectors. Typically, device drivers do not manipulate the members of this structure. However, the structure is presented here for the purpose of understanding the uiomove kernel interface, which operates on the members of the uio structure. Table 8-1 lists the members of the uio structure along with their associated data types that you might need to understand.

Table 8-1: Members of the uio Structure

Member Name Data Type
uio_iov struct iovec *
uio_iovcnt int
uio_offset off_t
uio_segflg enum uio_seg
uio_resid int
uio_rw enum uio_rw

The following character driver interfaces take a pointer to a uio structure as an argument: read and write. The following sections discuss each of these members. Section 8.2.2 shows how the /dev/none driver's write interface uses the uio structure.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.1.2.1    The uio_iov and uio_iovcnt Members

The uio_iov member specifies a pointer to the first iovec structure. The iovec structure has two members: one that specifies the address of the segment and another that specifies the size of the segment. The system allocates contiguous iovec structures for a given transfer.

The uio_iovcnt member specifies the number of iovec structures for this transfer.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.1.2.2    The uio_offset and uio_segflg Members

The uio_offset member specifies the offset within the file.

The uio_segflg member specifies the segment type. This member can be set to one of the following values: UIO_USERSPACE (the segment is from the user data space), UIO_SYSSPACE (the segment is from the system space), or UIO_USERISPACE (the segment is from the user I space).


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.1.2.3    The uio_resid and uio_rw Members

The uio_resid member specifies the number of bytes that still need to be transferred.

The uio_rw member specifies whether the transfer is a read or a write. This member is set by read and write system calls according to the corresponding field in the file descriptor. This member can be set to one of the following values: UIO_READ (read transfer), UIO_WRITE (write transfer), or UIO_AIORW (Alpha I/O read/write transfer).


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.1.3    Setting Up the read Interface

The following code shows you how to set up a read interface, using the /dev/none driver as an example:

noneread(dev, uio, flag) dev_t dev; [1] struct uio *uio; [2] int flag; [3] { return (ESUCCESS); [4] }


.
.
.

  1. Declares an argument that specifies the major and minor device numbers for a specific NONE device. The minor device number is used to determine the logical unit number for the NONE device on which the read operation is performed. [Return to example]

  2. Declares a pointer to a uio structure. This structure contains the information for transferring data to and from the address space of the user's process. You typically pass this pointer unmodified to the uiomove or physio kernel interface. [Return to example]

  3. Specifies the access mode of the device. The access modes are represented by a bit mask of flags defined in the file /usr/sys/include/sys/fcntl.h. [Return to example]

  4. Because the /dev/none driver always returns EOF (end-of-file) on read operations, the noneread interface simply returns ESUCCESS. More complicated drivers would need to copy data from the device into the address space pointed to by the uio structure. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.2    Implementing the write Interface

A device driver's write interface performs the tasks necessary to write data to a device. The kernel calls the driver's write interface for character device drivers on behalf of applications that make a write system call. The code associated with a write interface resides in the read and write device section of the device driver. You specify the entry point for the driver's write interface in a dsent structure. Section 6.6.5.1 describes the dsent structure. Writing Device Drivers: Reference provides a reference page that gives additional information on the arguments and tasks associated with a write interface.

To implement a write interface you must understand the dev_t data type and the uio data structure. Section 8.1.1 and Section 8.1.2 discuss these topics. The following list describes some typical tasks that you perform when implementing a write interface.

Your write interface will probably perform most of these tasks and, possibly, some additional ones. The following sections describe each of these tasks, using the /dev/none driver as an example.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.2.1    Setting Up the write Interface

The following code shows you how to set up a write interface, using the /dev/none driver as an example:

nonewrite(dev, uio, flag) dev_t dev; [1] struct uio *uio; [2] int flag; [3]

{


.
.
.

  1. Declares an argument that specifies the major and minor device numbers for a specific NONE device. The minor device number is used to determine the logical unit number for the device on which the write operation is performed. [Return to example]

  2. Declares a pointer to a uio structure. This structure contains the information for transferring data to and from the address space of the user's process. You typically pass this pointer unmodified to the uiomove or physio kernel interface. [Return to example]

  3. Specifies the access mode of the device. The access modes are represented by a bit mask of flags defined in the file /usr/sys/include/sys/fcntl.h. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.2.2    Copying Data to the Device

The following code shows how a driver's write interface copies data to a device, using the /dev/none driver as an example. The code shows some typical variable and structure declarations and the use of the minor and panic interfaces. The /dev/none driver's write interface (called nonewrite) copies data from the address space pointed to by the uio structure to the device. Upon a successful write, nonewrite returns the value zero (0) to the write system call.

int unit = minor(dev); [1] struct controller *ctlr = noneinfo[unit]; [2] struct none_softc *sc = &none_softc[unit]; [3] unsigned int count; [4] struct iovec *iov; [5]

while(uio->uio_resid > 0) { [6] iov = uio->uio_iov; [7] if(iov->iov_len == 0) { [8] uio->uio_iov++; uio->uio_iovcnt--; if(uio->uio_iovcnt < 0) [9] panic("none write"); continue; }

count = iov->iov_len; [10]

iov->iov_base += count; [11] iov->iov_len -= count; [12] uio->uio_offset += count; [13] uio->uio_resid -= count; [14]

sc->sc_count +=count; [15] } return (ESUCCESS); }

  1. Declares a unit variable and initializes it to the device minor number. Note the use of the minor interface to obtain the device minor number.

    The minor interface takes one argument: the number of the device for which an associated device minor number will be obtained. The minor number is encoded in the dev argument, which is of type dev_t. Section 8.1.1 describes the dev_t data type. [Return to example]

  2. Declares a pointer to a controller structure and calls it ctlr. The driver initializes ctlr to the controller structure associated with this NONE device. The minor device number, unit, is used as an index into the array of controller structures to determine which controller structure is associated with this NONE device. [Return to example]

  3. Declares a pointer to a none_softc structure and calls it sc. The driver initializes sc to the address of the none_softc structure associated with this NONE device. The minor device number, unit, is used as an index into the array of none_softc structures to determine which none_softc structure is associated with this NONE device. [Return to example]

  4. Declares a variable that stores the size of the write request. [Return to example]

  5. Declares a pointer to an iovec structure and calls it iov. [Return to example]

  6. Checks the size of the remaining logical buffer (represented by the uio_resid member) to determine if nonewrite must copy data from the address space pointed to by the uio structure to the device. The loop continues until all the bytes of data are copied to the device. [Return to example]

  7. Sets the iov pointer to the address of the current logical buffer segment (represented by the uio_iov member). [Return to example]

  8. If the remaining size of the current segment (represented by the iov_len member) is equal to zero (0), increments the address of the current logical buffer segment (represented by the uio_iov member) and decrements the number of remaining logical buffer segments (represented by the uio_iovcnt member). [Return to example]

  9. If the number of remaining logical buffer segments is less than zero (0), there is no data to write; therefore, the /dev/none driver calls the panic interface to cause a system crash and displays the message ``none write'' on the console terminal. This code represents an error condition that should never occur.

    The panic interface takes one argument: the message you want the panic interface to display on the console terminal. [Return to example]

  10. Sets the count variable to the number of bytes contained in the current segment (represented by the iov_len member). This value is the size of the write request. [Return to example]

  11. Adds the number of bytes in the write request to the address of the current byte within the logical buffer segment (represented by the iov_base member). [Return to example]

  12. Subtracts the number of bytes in the write request from the current segment (represented by the iov_len member). [Return to example]

  13. Adds the number of bytes in the write request to the current offset into the full logical buffer (represented by the uio_offset member). [Return to example]

  14. Subtracts the number of bytes in the write request from the size of the remaining logical buffer (represented by the uio_resid member). [Return to example]

  15. Adds the number of bytes in the write request to the sc_count member of the sc pointer. When there are no more bytes, nonewrite returns ESUCCESS to indicate a successful write. Otherwise, it returns an appropriate error code that identifies the problem. You obtain the error codes from the file /usr/sys/include/sys/errno.h. Because there is no physical device associated with /dev/none, nonewrite does not actually copy data anywhere. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.3    Implementing the reset Interface

A device driver's reset interface performs the tasks necessary to force a device reset to place the device in a known state after a bus reset operation. The bus adapter code calls the driver's reset interface after completion of a bus reset operation. The code associated with a reset interface resides in the reset section of the device driver. You specify the entry point for a driver's reset interface in a dsent structure. Section 6.6.5.1 describes the dsent structure. Writing Device Drivers: Reference provides a reference page that gives additional information on the arguments and tasks associated with a reset interface. The following section describes how to set up a reset inteface, using the /dev/xx driver as an example.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.3.1    Setting Up the reset Interface

The following code shows you how to set up a reset interface, using the /dev/xx driver as an example:

xxreset(busnum)
int busnum; [1]
{


.
.
.

  1. Declares an argument that specifies the logical unit number of the bus on which the bus reset occurred. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.4    Implementing the select Interface

A device driver's select interface performs the tasks necessary to determine whether data is available for reading and whether space is available for writing data. The kernel calls a driver's select interface on behalf of applications that make a select system call. The code associated with a select interface resides in the select section of the device driver. You specify the entry point for a driver's select interface in a dsent structure. Section 6.6.5.1 describes the dsent structure. Writing Device Drivers: Reference provides a reference page that gives additional information on the arguments and tasks associated with a select interface.

The following list describes some typical tasks that you perform when implementing a select interface.

Your select interface will probably perform most of these tasks and, possibly, some additional ones. The following sections describe each of these tasks, using the /dev/xx driver as an example.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.4.1    Using the sel_queue Data Structure

The sel_queue data structure provides device driver writers with a generic queue of select events. You must initialize the links member by calling the queue_init interface prior to using the select_enqueue and select_dequeue interfaces. Section 8.4.3 and Section 8.4.4 show how the /dev/xx driver's select interface calls these interfaces.

Table 8-2 lists the members of the sel_queue structure along with their associated data types.

Table 8-2: Members of the sel_queue Structure

Member Name Data Type
links struct queue_entry
event struct event *

The links member specifies a queue_entry structure. This structure contains a generic doubly linked list (queue).

The event member specifies a pointer to an event structure. This structure is an opaque structure; that is, you do not reference it in your device driver.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.4.2    Setting Up the select Interface

The following code shows you how to set up a select interface (and a typical declaration of the sel_queue structure), using the /dev/xx driver as an example.


.
.
.
struct {
.
.
.
sel_queue_t * sel_q;
.
.
.
} xx_softc[NXX]; [1] xxselect(dev, events, revents, scanning) dev_t dev; [2] short *events; [3] short *revents; [4] int scanning; [5] {

  1. Declares an array of softc structures and calls it xx_softc. One of the members of the xx_softc structure is a pointer to a sel_queue data structure. The sel_queue data structure provides device driver writers with a generic queue of select events. You must initialize the links member by calling the queue_init interface prior to using the select_enqueue and select_dequeue interfaces. [Return to example]

  2. Declares an argument that specifies the major and minor device numbers for a specific XX device. The minor device number is used to determine the logical unit number for the XX device on which the select call is to be performed. [Return to example]

  3. Declares a pointer to an argument that specifies the events to be polled. This argument is an input to the device driver. A user-level process issues a select system call. The select system call then causes the kernel to call the driver's select interface. The kernel can set this argument to the bitwise inclusive OR of one or more of the polling bit masks defined in the file /usr/sys/include/sys/poll.h: POLLNORM, POLLOUT, and POLLPRI. [Return to example]

  4. Declares a pointer to an argument that specifies the events that are ready. The driver writer sets this value in the driver's select interface. The driver writer can set this argument to the bitwise inclusive OR of one or more of the polling bit masks defined in /usr/sys/include/sys/poll.h: POLLNVAL, POLLHUP, POLLNORM, and POLLOUT. [Return to example]

  5. Declares an argument that specifies the initiation and termination of a select call. The kernel sets this argument to the value 1 to indicate initiation of a select call. A user-level process issues a select system call. The select system call then causes the kernel to call the driver's select interface. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.4.3    Using the select Interfaces to poll for Input Reads

One task that a device driver's select interface can perform is to poll for input reads. A device driver typically calls two kernel support interfaces when polling for input reads: select_enqueue and select_dequeue. In addition, a device driver calls the queue_init kernel support interface to initialize the specified queue. The minor interface is used to obtain the minor device number associated with a specific device. The following code shows you how to call these interfaces when polling for input reads, using the /dev/xx driver as an example.


.
.
.

xxcattach (ctlr)
struct controller *ctlr;

.
.
.
queue_init(&sc->sel_q.links); [1]
.
.
.
int nread; [2] register int unit = minor(dev); [3] struct xx_softc *sc = &xx_softc[unit]; [4] if (*events & POLLNORM) { [5] if (scanning) { [6] nread = xxnread(dev); [7] if (nread > 0) *revents |= POLLNORM; [8] else select_enqueue(&sc->sel_q); [9] } else select_dequeue(&sc->sel_q); [10] }


.
.
.

  1. Calls the queue_init interface. The queue_init interface initializes the specified queue. Device drivers call this interface prior to calling select_enqueue to initialize the links member of the sel_queue data structure. This member specifies a queue_entry structure. This structure contains a generic doubly linked list (queue).

    The queue_init interface takes one argument: a pointer to a queue_entry structure. This structure contains a links member that specifies a queue_entry structure. This structure contains a generic doubly linked list (queue).

    In the example, the links member passed to queue_init is referenced through the sel_q member of the softc structure. Section 8.4.2 shows the declaration of the /dev/xx driver's softc structure. [Return to example]

  2. Declares a variable to contain the number of characters available for input. [Return to example]

  3. Calls the minor interface to obtain the minor device number associated with this XX device. The minor interface takes one argument: the number of the device whose associated minor device number the minor interface will obtain. Upon successful completion, minor returns the minor number portion of the dev_t passed as the argument. [Return to example]

  4. Declares a pointer to an xx_softc structure and calls it sc. Initializes sc to the address of the xx_softc structure associated with this XX device. The minor device number, unit, is used as an index into the array of xx_softc structures to determine which xx_softc structure is associated with this XX device. [Return to example]

  5. Determines if the kernel set the read input select bit, which indicates that the caller of the driver's select interface wants to know if input data is available on this device. A user-level process issues a select system call. The select system call then causes the kernel to call the driver's select interface. [Return to example]

  6. If the kernel sets this argument to the value 1 (true), then a user-level process has initiated a select request (which causes the kernel to issue a select system call). [Return to example]

  7. For the purpose of this example, assume that the /dev/xx driver has a separate interface called xxnread, which returns the count of the number of characters available for input. [Return to example]

  8. If the count is greater than zero (0), there are characters available for input. The driver's xxcattach interface sets the read input select bit in the pointer to the revents argument. The select system call can be completed without waiting for input to be available. [Return to example]

  9. If the count is not greater than zero (0), there are no characters available for input. The driver's xxcattach interface calls the select_enqueue interface to allow the select system call to remember that the user-level process that initiated the select request should be notified when input is available to be read.

    This interface takes one argument: a pointer to a sel_queue structure. You previously initialized the links member of this structure by calling the queue_init interface. The select_enqueue interface adds the current kernel thread to the list of kernel threads waiting for a select-related event on this XX device. When input is available, the select request can be completed.

    At a different interface in the /dev/xx driver (typically, either the interrupt handler or an interface called by the interrupt handler), when new input is received on the device the driver calls the select_wakeup interface. The driver passes to it the same parameter passed to select_enqueue to notify the upper levels of the select system call that the user-level process that initiated the select request can now be notified that the driver has new input available to be read. [Return to example]

  10. Executes when the kernel sets the scanning parameter to the value zero (0). This indicates that the upper level select system call code is no longer interested in being notified when input is available on this device. The /dev/xx driver calls the select_dequeue interface to remove any instances of this XX device registered as waiting for notification of input.

    This interface takes one argument: a pointer to a sel_queue structure. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.4.4    Using the select Interfaces to poll for Output Writes

Another task that a device driver's select interface can perform is to poll for output writes. A device driver typically calls two kernel support interfaces when polling for output writes: select_enqueue and select_dequeue. (A device driver would have previously called the queue_init interface, typically in the driver's attach interface to initialize the specified queue.) The following code shows you how to call these interfaces when polling for output writes.


.
.
.

        if (*events & POLLOUT) { [1]
                if (scanning) { [2]
                        if (xxnwrite(dev)) [3]
                                *revents |= POLLOUT; [4]
                        else
                                select_enqueue(&sc->sel_q); [5]
                } else
                        select_dequeue(&sc->sel_q); [6]
        }
        return (ESUCCESS); [7]
}


.
.
.

  1. Determines if the kernel set the write output select bit, which indicates that the user-level process that initiated the select request wants to know if the device is ready to accept data to be output. Typically, this involves verifying that the device's output buffers have sufficient space to accept additional characters to be transmitted. [Return to example]

  2. If the kernel sets the scanning argument to the value 1 (true), then a user-level process has initiated a select request (which causes the kernel to issue a select system call). [Return to example]

  3. For the purpose of this example, assume that the /dev/xx driver has a separate interface called xxnwrite, which returns a nonzero value if the device is in a state where it is ready to output data. [Return to example]

  4. To indicate that this instance of the XX device is ready to accept additional output, sets the revents argument to the polling bit POLLOUT. [Return to example]

  5. The device is not ready to accept additional output. Therefore, the /dev/xx driver calls the select_enqueue interface to cause the select system call to be notified later when the device is ready to accept output.

    At a different interface in the /dev/xx driver (typically, either the interrupt handler or an interface called by the interrupt handler), after previous output transmission is completed the driver calls the select_wakeup interface. The driver passes a pointer to a sel_queue structure to notify the upper levels of the select system call that the user-level process that initiated the select request can now be notified that the driver has new output available to be written. [Return to example]

  6. If the kernel sets the scanning argument to the value zero (false), then a select system call is being terminated. This indicates that the upper level select system call code is no longer interested in being notified when the device is ready to accept output characters. The /dev/xx driver calls the select_dequeue interface to remove any instances of this XX device registered as waiting for notification of output ready status.

    This interface takes one argument: a pointer to a sel_queue structure. [Return to example]

  7. The /dev/none driver's xxselect interface returns ESUCCESS to indicate success. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.5    Implementing the stop Interface

A device driver's (specifically, a terminal device driver's) stop interface performs the tasks necessary to suspend transmission on a specified line. The code associated with a stop interface resides in the stop section of the device driver. You specify the entry point for a driver's stop interface in a dsent structure. Section 6.6.5.1 describes the dsent structure. Writing Device Drivers: Reference provides a reference page that gives additional information on the arguments and tasks associated with a stop interface. The following section describes how to set up the stop interface.


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Section] [Next Chapter] [Index] [Help]


8.5.1    Setting Up the stop Interface

The following code shows you how to set up a stop interface, using the /dev/xx driver as an example:

xxstop(tp, flag)
struct tty *tp; [1]
int flag;       [2]
{


.
.
.

  1. Declares an argument that specifies a pointer to a tty structure. This structure contains data such as state information about the hardware terminal line, input and output queues, and the line discipline number. [Return to example]

  2. Declares an argument that specifies whether the output is to be flushed or suspended. Some device drivers do not use this argument. The argument is included here for use in terminal drivers. [Return to example]


[Return to Library] [Contents] [Previous Chapter] [Previous Section] [Next Chapter] [Index] [Help]


8.6    Implementing the mmap Interface

Alpha CPUs do not support an application's use of the mmap system call. Therefore, if you are writing a device driver that operates on such CPUs, you need to use a mechanism other than the memory map interface. The memory map section applies only to character device drivers, and it contains a memory map interface. The memory map interface is invoked by the kernel as the result of an application calling the mmap system call. You specify the entry for the driver's xxmmap interface in a dsent structure. Section 6.6.5.1 describes the dsent structure. Writing Device Drivers: Reference provides a reference page that gives additional information on the arguments and tasks associated with a memory map interface.

The following code shows you how to set up a memory map interface, using the /dev/xx driver as an example.

xxmmap(dev, offset, prot)
dev_t dev;     [1]
off_t offset; [2]
int prot;     [3]
{

.
.
.
}

  1. Declares an argument that specifies the major and minor device numbers for this device. The minor device number is used to determine the logical unit number for the character device whose memory is to be mapped. [Return to example]

  2. Declares an argument that specifies the offset (in bytes) into the character device's memory. The offset must be a valid offset into device memory. [Return to example]

  3. Declares an argument that specifies the protection flag for the mapping. The protection flag is the bitwise inclusive OR of the following valid protection flag bits defined in /usr/sys/include/sys/mman.h:
    Flag Meaning
    PROT_READ Pages can be read.
    PROT_WRITE Pages can be written.

    [Return to example]