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


2    Developing a Device Driver

This chapter discusses how you develop a device driver. Included is a simple example called /dev/none, which is written by a fictitious third-party driver company called EasyDriver Incorporated. The /dev/none device driver is a simple device driver that implements many of the character device driver interfaces. Note, however, that the /dev/none driver does not control a real hardware device.

Appendix C provides worksheets to illustrate the kinds of information you need to gather to make developing the device driver easier. These worksheets are filled in for the /dev/none driver. Appendix C also provides these same worksheets for use in developing your own device drivers. If you use the worksheets for your driver development, consider organizing them in a device driver project binder. This will make them available to systems engineers who need to maintain the driver.

Specifically, the chapter describes the following tasks associated with developing any device driver:


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


2.1    Gathering Information

The first task in writing a device driver is to gather pertinent information about the host system and the device for which you are writing the driver. You need to:

The following sections describe how you would fill out the worksheets provided for each of these tasks, using the /dev/none device driver as an example.


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


2.1.1    Specifying Information About the Host System

Figure C-1 and Figure C-2 show the host system information associated with the /dev/none driver. As the worksheets show, you gather the following information about the host system:


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


2.1.1.1    Specifying the Host CPU or CPUs on Which Your Driver Operates

For example purposes, the /dev/none driver will be developed for use on a DEC 3000 Model 500 AXP Workstation. (Note, however, that the /dev/none device driver cannot run on this CPU because it does not control an actual hardware device.)

The goal should always be to write any device driver to operate on more than one CPU hardware platform, so you should be aware of any differences presented by different CPU architectures. By identifying the hardware platforms you want the driver to operate on, you can address any driver design decisions related to the CPU hardware architecture. This identification can help you determine whether one driver, with appropriate conditional compilation statements, can handle the CPU hardware platforms you want the driver to operate on. You might decide that it would be easier to write two device drivers.

Section 3.1 discusses some device driver design issues that are affected by CPU architectures.


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


2.1.1.2    Specifying the Operating System or Systems on Which Your Driver Operates

The /dev/none driver will be developed for use on the Digital UNIX operating system. This is an important consideration because data structures and kernel interfaces differ among operating systems. For example, device drivers developed for Digital UNIX systems initialize a driver structure, while drivers developed for other versions of UNIX systems would initialize a different structure. This identification can help you determine the amount of work that is involved in porting an existing device driver from some other UNIX operating system to the Digital UNIX operating system.

Section 4.1 provides information to help you understand the tasks involved in porting from ULTRIX to Digital UNIX systems.


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


2.1.1.3    Specifying the Bus or Buses to Which Your Driver Connects

You must identify the bus to which the device controller is connected. Different buses require different approaches to writing the driver. For example, a VMEbus device driver writer must know how to allocate the VMEbus address space. This task is not applicable for drivers that operate on other buses. On the other hand, you may want to write one device driver that operates on several different bus architectures if these bus architectures exhibit enough common features. For Digital-implemented buses (specifically, the EISA, ISA, PCI, and TURBOchannel), you should be able to write one device driver that operates on all of these buses by observing the design issues summarized in Section 3.2.

Figure C-2 shows that for example purposes the /dev/none driver operates on the TURBOchannel bus. Section 7.1.1 discusses multiple bus issues related to implementing a probe interface. Section 7.1.4 discusses how to set up a probe interface to handle multiple buses. Section 7.4.1 discusses multiple bus issues related to implementing a slave interface.

Note that the worksheet shown in Figure C-2 provides a space for pseudodevice drivers. A pseudodevice driver, such as the pty terminal driver, is structured like any other driver. The difference is that a pseudodevice driver does not operate on a bus and does not control a hardware device. This book does not specifically address pseudodevice drivers; however, Section B.2 provides a source listing for an example pseudodevice driver called /dev/edpseudo.


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


2.1.2    Identifying the Standards Used in Writing the Driver

Figure C-3 and Figure C-4 show the device driver conventions worksheet for the /dev/none driver. As the worksheets show:


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


2.1.2.1    Specifying a Naming Scheme

The /dev/none driver uses the name none as the prefix for device driver interface names. Device driver interfaces written for Digital UNIX can use the following naming conventions:

The /dev/none driver uses the name none as the prefix for data structures internal to the device driver. These structures include a data structure (often referred to as a softc structure) to store driver-specific information.

When using the CSR I/O access interfaces to read from and write to a device's CSR addresses, you create device register offset names. You can also use the prefix to create names for each of the device register offsets. Because the /dev/none driver uses the CSR I/O access interfaces, it uses the name none as the prefix to the device register offset name.

The /dev/none driver uses the prefix DN for device driver constant names. The prefix matches the first two characters in the driver name, /dev/none (the letters D and N). These constants can represent values or macros. For example, the constant DN_SIZE might represent the size of the device register area.

The previously described naming schemes are recommendations, not requirements. The one naming requirement you must follow concerns the name of the configure interface, which for the /dev/none driver is none_configure. This interface is the configuration entry point called as a result of configuration (both static and dynamic) requests. For the configure interface, the underscore character (_) must follow the driver's name. This underscore character in the name is a requirement of the configuration process for drivers and is the Digital UNIX convention.

Before choosing a naming scheme, you have to make sure that these names do not conflict with other driver interface and structure names. To help you determine what names are currently used by the system, run the nm command on the kernel image file. This image file is usually called /vmunix. (Note that you should not run the nm command on the boot-link /sysconfigtab kernel image file.) If you follow the device driver (kernel) kits delivery process described in Chapter 12, you should be particularly careful about choosing a naming scheme, ensuring that it does not conflict with that of other third-party driver vendors.

Third-party driver writers may need to specify controller and device information in calls to the create_controller_struct and create_device_struct interfaces. Section 6.6.4.3 shows how to call these interfaces to create the controller and device structures associated with your device drivers. If you are supporting Digital devices, you specify valid device definition keywords asssociated with these devices. These keywords represent the controllers and devices that Digital supports. See the System Administration guide (specifically, the section that discusses how to build the kernel to add support for a new device) for information on how to obtain the device definition keywords asssociated with Digital-supported devices.

If you are supporting non-Digital devices, you can select any string other than those already chosen by Digital to represent the device. However, without an exclusive naming scheme, your choices could conflict with those of other third-party driver vendors. To avoid these conflicts, you can select a string that includes the vendor and product names and, possibly, the version and release numbers. This type of naming scheme minimizes the potential for name conflicts. For example, the driver writers at EasyDriver Incorporated might specify edgd for an internally developed device.


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


2.1.2.2    Choosing an Approach for Writing Comments and Documentation

The /dev/none driver takes two approaches to supplying comments in the driver code examples used in the book. In the first approach, the /dev/none driver contains no inline comments. Instead, the following convention is used:

int unit = minor(dev); [1]

  1. A number appears after a line of code in the /dev/none device driver example. Following the example, a corresponding number appears that contains an explanation of the associated line or lines. The device driver examples contained in Part 3 use the first approach to make the source code easier to read. [Return to example]

In the second approach, the /dev/none device driver supplies appropriate inline comments. The source code listing in Appendix B uses the second approach.

In addition to providing background information and detailed explanations of the /dev/none driver, this book also offers information on device driver concepts, kernel interfaces, data structures, and so forth. Your approach to writing device driver documentation may be different.


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


2.1.3    Specifying Characteristics of the Device

Figure C-5 and Figure C-6 show the device characteristics for the NONE device associated with the /dev/none driver.

As the worksheets show, you specify the following characteristics associated with the device:


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


2.1.3.1    Specifying Whether the Device Is Capable of Block I/O

If the device is capable of block I/O, then you would write a block device driver. A block device is one that stores data on its media in a standard way. For example, most disk drives store data in disk sectors (typically 512 bytes). Tape drives sometimes store data in a standard-size tape record.

Typically, block devices are random access devices (that is, disks) because the file system does not always perform I/O to sequential disk sectors. Tape devices are typically sequential access devices and, therefore, not suitable for using as a block device.

The NONE device is not capable of handling blocks of data so the No box on the worksheet shown in Figure C-5 is marked.


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


2.1.3.2    Specifying Whether the Device Supports a File System

Most block devices can support file systems. If a block device supports a file system, it must be able to map between file system blocks and the underlying structure on the device. In Digital UNIX, this mapping is accomplished through partition tables.

The NONE device is not capable of supporting file systems, so the No box on the worksheet shown in Figure C-5 is marked.


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


2.1.3.3    Specifying Whether the Device Supports Byte Stream Access

Most devices support byte stream access. You can view this access as sequentially accessing data through the device. For example, a sequence of characters typed at a terminal constitutes a byte stream. Most block devices can also be accessed in this manner. When a block device is accessed as a stream of bytes, the access is typically called ``raw'' access. When accessed this way, the data on the block device is accessed sequentially without any underlying structure being placed on the data (for example, disk sectors).

For the NONE device, the Yes box on the worksheet shown in Figure C-5 is marked.


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


2.1.3.4    Specifying Actions to Take on Interrupts

Use this space on the worksheet in Figure C-5 to summarize what the driver interrupt handlers will do when the device generates an interrupt. For example, a terminal-type character driver's interrupt handler can receive a character that was typed on a user's keyboard. Typically, the interrupt handler must determine the source of the interrupt, respond to the interrupt (for example, by reading in the data), and perform the appropriate actions to cause the interrupt to be dismissed.

Some other issues concerning interrupts are:

Because the NONE device has no underlying physical hardware, it cannot generate interrupts. Therefore, this part of the worksheet shown in Figure C-5 is left blank.


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


2.1.3.5    Specifying How to Reset the Device

If the bus that the device controller is connected to supports the reset function, the device driver must be able to stop all current work and place the device connected to the controller in a known, quiescent state.

For example purposes, the NONE device can be connected to the TURBOchannel bus. To keep the example driver simple, the reset function will not be implemented as part of the /dev/none device driver. Thus, the worksheet shown in Figure C-6 indicates that this characteristic is of no concern to the /dev/none device driver.


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


2.1.3.6    Specifying Other Device Characteristics

Use this space to identify other characteristics of the device that might influence how you design your device driver. The worksheet shown in Figure C-6 is left blank to indicate that there are no other device characteristics associated with the NONE device.


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


2.1.4    Describing Device Usage

Figure C-7 shows the device usage information for the NONE device associated with the /dev/none driver. As the worksheet shows, you gather the following information about device usage:


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


2.1.4.1    Listing the Device Documentation

For a real device, you should have on hand the manual for the device that is supplied by the manufacturer. The NONE device is a fictitious device; therefore, this section of the worksheet shown in Figure C-7 is left blank.


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


2.1.4.2    Specifying the Number of Device Types to Reside on the System

The number of devices that can be supported has a direct effect on the design of the driver. If only one will be supported, the driver need not worry about determining which device is being accessed. If a small number (for example, 2 - 5) is supported, the driver can use simple data structures and indexing to keep track of device access. If a greater number is to be supported, the driver must use more sophisticated methods to keep track of which device is being accessed.

The /dev/none driver can accommodate more than one instance. The worksheet shown in Figure C-7 indicates that the /dev/none device driver can support a maximum of eight instances of the NONE device. However, the driver writers at EasyDriver Incorporated ship the /dev/none driver product to support a maximum of one instance. Customers of EasyDriver Incorporated can change this number by editing the none: entry in the /etc/sysconfigtab database.


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


2.1.4.3    Describing the Purpose of the Device

For this device usage item, enter a short description of the purpose of the device. For example, the purpose of most disk devices is to provide storage for user data and files. The purpose of most terminal devices is to provide a means for interacting with users of the system.

The worksheet shown in Figure C-7 indicates that this item is not applicable to the NONE device because it is a fictitious device.


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


2.1.5    Providing a Description of the Device Registers

Figure C-8 and Figure C-9 show the device register information for the /dev/none driver. As the worksheets show, you gather the following information about the device registers:


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


2.1.6    Identifying Support in Writing the Driver

Figure C-10 shows the support associated with writing the /dev/none driver. As the worksheet shows, you gather the following information about device driver support:


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


2.2    Designing the Device Driver

After you gather information about the host system and the device, your next task is to design the device driver. You need to:

The following sections describe how you would fill out the worksheets provided for each of the above tasks, using the /dev/none device driver as an example.


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


2.2.1    Specifying the Device Driver Type

Figure C-11 shows the types of device drivers you can write on Digital UNIX. As the worksheet shows, you identify your driver as one of the following:

When using the buffer cache to perform I/O on blocks of data, you use block device drivers. Otherwise, you use character device drivers. Most device drivers are character drivers. Some device drivers are both character and block drivers. The network driver is another type of device driver. This book does not discuss network device drivers.

The Character box is marked for the /dev/none device driver because it has no requirements to handle blocks of data.

The worksheet also shows that you need to determine the method or methods to use to configure the driver into the kernel. As the worksheet shows, there are two methods: static configuration and dynamic configuration. Static configuration consists of the tasks and tools necessary to link a device driver (single binary module) directly into the kernel at kernel build time. Dynamic configuration consists of the tasks and tools necessary to link a device driver (single binary module) directly into the kernel at any point in time.

Both boxes on the worksheet in Figure C-11 are marked, which means the /dev/none driver is written to accommodate both static and dynamic configuration. By designing and writing the driver to be statically and dynamically configured, you offer customers maximum flexibility. Chapter 6 shows how to implement a device driver's configure interface to accommodate static and dynamic configuration.

This book does not discuss how to write STREAMS device drivers. However, Writing Device Drivers: Reference contains reference pages for kernel interfaces that STREAMS device drivers use. See the Network Programmer's Guide for information on STREAMS programming frameworks and other information related to STREAMS.


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


2.2.2    Identifying Device Driver Entry Points

Figure C-12 shows the device driver entry points (interfaces) for the /dev/none device driver. The worksheet is divided into parts because the possible entry points vary depending on whether the driver is a block, character, or network driver. Chapter 5 describes the sections in the device driver source where these block and character driver interfaces reside. Part 3 contains chapters that describe and provide examples of the typical tasks that these block and character driver interfaces perform. These chapters also show how to set up block and character driver interfaces. Finally, these chapters explain how to implement the interfaces for the /dev/none driver shown in Figure C-12.

Because the /dev/none driver is a character driver, the worksheet shows the following entry points:

You may want to describe the goals of the interfaces for your driver in a more formal device driver development specification. Consider adding this specification along with the worksheets to the device driver project binder.


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


2.2.3    Specifying the Locking Method for SMP-Safe Device Drivers

Figure C-13 shows the methods available in Digital UNIX to make your device drivers operate safely in a symmetric multiprocessing (SMP) environment. The worksheet shows that no method is chosen for the /dev/none device driver, which means it cannot operate safely in an SMP environment.

Writing Device Drivers: Advanced Topics describes the methods available for making your drivers operate safely in an SMP environment.


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


2.3    Understanding System Data and Address Types that Device Drivers Use

When you design a device driver, you need to understand the following system data types and addresses that device drivers use:

The following sections discuss each of these system data types and addresses.


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


2.3.1    I/O Handle Data Type

To provide device driver binary compatibility across different bus architectures, different CPU architectures, and different CPU types within the same CPU architecture, Digital UNIX represents I/O bus address spaces through an I/O handle. An I/O handle is a data entity that is of type io_handle_t. Device drivers use the I/O handle to reference bus address space (either I/O space or memory space). The I/O or memory spaces contain either sparse space or dense space addresses. Section 2.3.4 and Section 2.3.5 describe sparse space and dense space addresses. Section 7.1.2 provides additional information on the I/O handle.


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


2.3.2    Kernel-Unmapped Virtual Address

A kernel-unmapped virtual address is an address that resides in kernel address space. This address is sometimes referred to as a kseg address. A kernel-unmapped virtual address makes use of the virtual memory (VM) mapping registers. You can also use a kernel-unmapped virtual address to operate on sparse space, dense space, and I/O spaces available on different Alpha CPUs.

The KSEG_TO_PHYS interface converts a kernel-unmapped virtual address to a kernel physical address. Device drivers can use this physical address in DMA operations. Prior to calling KSEG_TO_PHYS, device driver writers often call one of the following interfaces to determine whether the address passed is a virtual address in the addressed kernel segment:

The PHYS_TO_KSEG interface converts a kernel physical address to a kernel-unmapped virtual address.

Writing Device Drivers: Reference provides reference pages that give additional information on the arguments and tasks associated with the previously listed interfaces.


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


2.3.3    Kernel Physical Address

A kernel physical address is an address that resides in kernel address space but does not use the virtual memory (VM) mapping registers.


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


2.3.4    Sparse Space Address

A sparse space address is an address that resides in sparse space. Sparse space contains addresses that reside in bus address space (either I/O space or memory space). All Alpha CPUs support sparse space. As a result, all bus configuration code should supply an I/O handle that references bus address space.

Digital UNIX provides the following interfaces that allow device drivers to perform copy operations on addresses, zero blocks of memory on addresses, read from addresses, and write to addresses that reside in sparse space. All of these interfaces take one or more I/O handles that reference addresses in sparse space as arguments.

Writing Device Drivers: Reference provides reference pages that give additional information on the arguments and tasks associated with the previously listed interfaces. Section 7.1.9 shows how the /dev/none driver uses the read_io_port and write_io_port interfaces. See Section 7.1.10 to learn how to build your own macros based on the read and write macros that Digital provides. Section 18.5 provides examples on how to use the io_copyin, io_copyio, and io_copyout interfaces.


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


2.3.5    Dense Space Address

A dense space address is an address that resides in dense space. Dense space is an area of I/O space or memory space that device drivers can access as if it were memory. Not all Alpha CPUs support dense space.

Digital UNIX provides the following interfaces that allow device drivers to perform copy operations and zero blocks of memory on addresses that reside in dense space:

Writing Device Drivers: Reference provides reference pages that give additional information on the arguments and tasks associated with the previously listed interfaces.


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


2.3.6    Bus Physical Address

A bus physical address is an address that the device driver can pass to another device on the bus. A device driver can use a bus physical address to reference the I/O or memory space of other cards that reside on the same bus as the card that the device driver controls.


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


2.3.7    Memory Address

A memory address is either a sparse space or dense space address that performs a memory cycle on a specific bus.


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


2.3.8    I/O Address

An I/O address is either a sparse space or dense space address that performs an I/O cycle on a specific bus.


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


2.4    Specifying the Method for Registering Device Interrupt Handlers

An interrupt handler is a device driver routine that services hardware interrupts. Digital requires that all drivers call the handler interfaces to dynamically register interrupt handlers.

The dynamic registration of interrupt handlers involves the use of the following data structures:

Section 7.1.5.1 and Section 7.1.5.2 describe the ihandler_t and handler_intr_info structures. Section 7.1.6 shows how the /dev/none driver uses the handler interfaces to dynamically register interrupt handlers. Section 7.1.7 shows an example of how to use the handler interfaces to dynamically register shared interrupts.

All Digital-implemented buses support the dynamic registration of interrupt handlers. Although some buses (for example, the TURBOchannel bus) previously supported the identification of interrupt handlers in the target (system) configuration file, you should port existing device drivers to use the handler interfaces.


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


2.5    Specifying the Method for Identifying Bus, Controller, and Device Information

Digital UNIX provides the create_controller_struct and create_device_struct interfaces. These interfaces create the controller and device structures associated with your device driver. Section 6.6.4.3 shows you how to use these interfaces.


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


2.6    Determining the Structure Allocation Technique

Another design consideration is the technique you plan to use for allocating data structures. Generally, there are two techniques you can follow: dynamic allocation and static allocation. Dynamic allocation is the recommended method; however, static allocation is discussed in this book because many existing drivers allocate their data structures statically. Table 2-1 lists the structure allocation techniques discussed in this book along with some guidelines to help you choose the best method for your device drivers. Sections that provide examples of each follow the table.

Table 2-1: Structure Allocation Technique Guidelines

Technique Guidelines for Using
Static allocation model Use this technique if you:

  • Know that the maximum number of devices is five or less

  • Know that the driver does not use numerous data structures

Dynamic allocation Use this technique if you:

  • Know that the maximum number of devices is greater than five

  • Know that the driver uses numerous data structures


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


2.6.1    Static Allocation Model Technique

The static allocation model technique lets you statically allocate enough data structures to accommodate the maximum configuration. To make access to the data structures as easy as possible, you statically declare an array of pointers to the data structures. The static allocation model technique uses a constant that defines the maximum number of devices.

The following code fragment illustrates the static allocation model technique:

/***********************************************
 * structure declarations to illustrate the    *
 * static allocation model technique           *
 ***********************************************/


.
.
.
#define MAX_NONE 4 /* Define maximum number of */ /* devices */ [1]

/********************************************** * Use the constant to size the arrays * [2] **********************************************/ struct controller *noneinfo[MAX_NONE]; struct none_softc none_softc[MAX_NONE];


.
.
.

  1. If you knew that your device driver supported at most four instances of some device (for example, four controllers), you would first declare a constant to represent this maximum number. Thus, the example defines a MAX_NONE constant and assigns it the maximum value 4. [Return to example]

  2. The next two lines use the MAX_NONE constant to size the respective arrays. In the first line, MAX_NONE sizes the array of pointers to controller structures, and in the second line it sizes an array of none_softc structures. [Return to example]

The disadvantage of the static allocation model technique is that, if you have only one instance of the device on the system, you are wasting three of the four declared none_softc data structures. If the number of these data structures does not exceed five and if they contain no more than a reasonable number of data members, the static allocation model technique is acceptable.


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


2.6.2    Dynamic Allocation Technique

The previously described static allocation model technique is suitable for device drivers that support a small number of devices (five or less). Some device drivers, however, support more than five devices and declare a significant number of data structures that might contain a large amount of data. For example, the Digital Storage Architecture (DSA) subsystem can support up to 256 disks and at least 16 controllers. Clearly, it would not be desirable to always allocate all the required data structures because for most configurations there are not nearly that many devices present. Statically allocating the maximum number of data structures would waste too much space. The DSA subsystem and systems like it are good examples of where dynamic configuration of the required data structures would be beneficial.

In this model of dynamically allocating the data structures, you need to:


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


2.6.2.1    Determining the Maximum Configuration

The first task is to determine the maximum number of instances of the device that can exist on your system. In the example, suppose that there can be a maximum of 16 NONE devices. Thus, there is one driver that handles, at most, 16 instances of the device. In this case, you could define a constant similar to the following:

#define MAX_NONE 16


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


2.6.2.2    Statically Declaring an Array of Pointers to the Data Structures

Your next task is to statically declare an array of pointers to the data structures that the device driver will use. The example declares an array of pointers to controller and none_softc data structures as follows:

struct controller *noneinfo[MAX_NONE];
struct none_softc *none_softc[MAX_NONE];

These declarations are pointers to the data structures, not the data structures themselves. Although this approach is not required, it is chosen for the example because having the static array of pointers makes access to the data structures easy.


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


2.6.2.3    Allocating the Data Structures with MALLOC

The next task is to allocate the data structures. The following example shows how to allocate the data structures in the probe interface.

The driver's probe interface is called for each instance of the device that is actually present on the system. This makes the probe interface one place to dynamically allocate the data structures, as in the following example:

noneprobe(handle, ctlr) io_handle_t handle; /* I/O handle */ struct controller *ctlr; [1] { if (ctlr->ctlr_num > MAX_NONE) /* Is unit greater * * than 16? */ [2] return(0);

.
.
.
/*********************************************** * Allocate the softc structure associated * * with this instance * [3] ***********************************************/ if (none_softc[ctlr->ctlr_num] == (struct none_softc *)NULL) { struct none_softc *sc; MALLOC(sc, struct none_softc *, sizeof(struct none_softc), M_DEVBUF, M_NOWAIT); if (sc) == (struct none_softc *)NULL) { [4] return(0); } } return(1); [5] }

  1. The bus configuration code passes the controller structure associated with this device as a parameter to the noneprobe interface. Section 17.2 describes the controller structure. The noneprobe interface uses the ctlr_num member to tell you which instance of the controller you are referring to. [Return to example]

  2. Determines if there are more than 16 controllers. If there are, noneprobe returns the value zero (0) to indicate an error. Otherwise, it allocates the data structures. [Return to example]

  3. Now you allocate the data structures associated with this instance. The first if statement is a test to make sure that the noneprobe interface has not already been called for this unit.

    You then dynamically allocate the memory to accommodate the none_softc data structures by calling the MALLOC interface and passing to it the number of bytes to allocate. The MALLOC interface takes five arguments:

    This call uses the M_NOWAIT flag. [Return to example]

  4. Checks the return value from MALLOC. The return value is NULL if there is no memory available to be dynamically allocated for this instance of the driver. The driver will not be operational without its data structures. In this case, it returns the value zero (0) to indicate that the driver's probe interface failed. [Return to example]

  5. The noneprobe interface defines the return type of MALLOC as struct none_softc * (the second argument in the call to the MALLOC interface). In this call, MALLOC returns the address of the memory where it allocated the none_softc structure. The driver will be operational when its data structures are allocated. In this case, it returns the value 1 to the bus configuration code to indicate that the driver's probe interface succeeded. [Return to example]


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


2.6.2.4    Accessing the Members of the Dynamically Allocated Data Structures

After you dynamically allocate the data structures in the probe interface, you need to correctly access them. The declaration for dynamically allocating the data structures appears as follows:

struct none_softc *none_softc[MAX_NONE];

The example declares pointers to an array of data structures. Thus, to access the data structures, you must reference the data structures as an array of pointers. For example:


.
.
.
/*********************************************** * Accessing statically allocated data * * structures * ***********************************************/ struct controller *ctlr = noneinfo[ctlr->ctlr_num]; struct none_softc *sc = &none_softc[ctlr->ctlr_num];
.
.
.
/*********************************************** * Accessing dynamically allocated data * * structures * ***********************************************/ struct controller *ctlr = noneinfo[ctlr->ctlr_num]; /* No change * * from static * * allocation */ struct none_softc *sc = none_softc[ctlr->ctlr_num]; /* Different * * from static * * allocation */

The reference to the none_softc structures uses the address operator in the static case. The address operator is not used in the dynamic case.


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


2.6.2.5    Freeing Up the Dynamically Allocated Memory with FREE

Device drivers that are dynamically configured into the kernel have a controller unattach or a device unattach interface. When the dynamically configured driver is unloaded, these interfaces are called for each instance of the controller or device present on the system. When these interfaces are called, they should free up the dynamically allocated memory. For the /dev/none device driver, the controller unattach interface frees up memory as follows:


.
.
.
if (ctlr->ctlr_num < MAX_NONE) {

/*********************************************** * Free up the dynamically allocated memory * ***********************************************/ if (none_softc[ctlr->ctlr_num] != (struct none_softc *)NULL) { FREE(sc, M_DEVBUF); [1]
/*********************************************** * Set the array element to NULL * ***********************************************/ none_softc[ctlr->ctlr_num] = (struct none_softc *)NULL; [2] }
.
.
.
}

  1. This example of a controller unattach interface uses the ctlr_num member to refer to the specific instance of the controller that the /dev/none driver operates on. The if statement is included as a safety check. Note that the FREE interface is used to free the memory previously allocated in a call to MALLOC. The FREE interface takes two arguments:

    [Return to example]

  2. After the dynamically allocated memory has been freed, the following line sets the none_softc array to NULL. This ensures that there will be no dangling references to the memory. [Return to example]


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


2.7    Creating a Device Driver Development Environment

When you are ready to write your driver, you will probably want to design a logical directory structure to hold the different files associated with the driver. Chapter 12 provides an overview of the device driver (kernel) kits delievery process. It also provides an example driver development environment. The Guide to Preparing Product Kits describes how to create kits for third-party driver products.