The sections that make up a Digital UNIX device driver differ, depending on whether the driver is a block, character, or network driver. Figure 3-1 shows the sections that a character device driver can contain and the possible sections that a block device driver can contain. Device drivers do not have to use all of the sections shown in the figure, and more complex drivers can use additional sections. Both character and block device drivers can contain:
The block device driver can also contain a strategy section, a psize section, and a dump section.
The character device driver contains the following sections not contained in a block device driver:
Writing Device Drivers: Tutorial discusses each of the driver sections. The remainder of this chapter describes the differences in the following driver sections as they relate to TURBOchannel device drivers: include file and autoconfiguration support (specifically, the xxprobe and xxslave interfaces).
Data structures and constant values are defined in header files that you include in the include files section of the driver source code. The number and types of header files you specify in the include files section vary, depending on such things as what structures, constants, and kernel interfaces your device driver references. You need to be familiar with:
Writing Device Drivers: Tutorial describes these files. TURBOchannel device drivers use the following header file exclusively:
#include <io/dec/tc/tc.h>
When Digital UNIX boots, the kernel determines what devices are connected to the computer. After finding a device, the kernel initializes it so that the device can be used at a later time. The probe interface determines if a particular device is present and the attach interface initializes the device. If the device is a disk controller, the slave interface determines if the device is present.
The autoconfiguration support section of a TURBOchannel device driver contains the code that implements these interfaces and the section applies to both character and block device drivers. The section can contain:
Writing Device Drivers: Tutorial discusses each of these interfaces.
The remainder of this chapter describes the differences in the xxprobe and xxslave interfaces as they apply to TURBOchannel device drivers. For convenience in referring to the names of the driver interfaces, the chapter uses the prefix xx. For example, xxprobe refers to a probe interface for some XX device.
A device driver's xxprobe interface performs tasks necessary to determine if the device exists and is functional on a given system. The xxprobe interface typically registers a driver's interrupt handlers by using the ihandler_t and handler_intr_info structures and by calling the handler_add and handler_enable interfaces.
Other tasks that the xxprobe interface performs vary, depending on whether the device driver is statically or dynamically configured into the kernel:
The xxprobe interface typically checks some device control status register (CSR) to determine whether the physical device is present. If the device is not present, the device is not initialized and not available for use. The kernel calls the xxprobe interface for each device that you defined in calls to the create_device_struct interface.
For dynamically configured drivers, the xxprobe interface is called indirectly during the driver loading process. The driver's configure interface calls the configure_driver interface to merge the driver's connectivity information into the system (hardware) configuration tree, which consists of bus, controller, and device structures. The call to configure_driver results in the system calling xxprobe for each instance of the controller present on the system.
When device drivers are dynamically configured, the bus configuration code checks for the existence of the device before calling xxprobe.
The xxprobe interface returns a nonzero value if the probe operation is successful. It returns the value zero (0) to indicate that the driver did not complete the probe operation. The following code fragment shows you how to set up a probe interface for a driver that operates on a TURBOchannel bus:
xxprobe(bus_io_handle, ctlr) io_handle_t bus_io_handle; [1] struct controller *ctlr; [2] { /* Variable and structure declarations */
.
.
.
/* Code to perform necessary checks */
.
.
.
Device drivers pass the I/O handle to the following categories of interfaces, which are discussed in Writing Device Drivers: Tutorial These interfaces can process the I/O handle to access the desired bus address space.
The CSR I/O access interfaces are read_io_port and write_io_port. These are generic interfaces that allow device drivers to read from and write to device registers. Using these interfaces to read data from and write data to a device register makes the device driver more portable across different bus architectures, different CPU architectures, and different CPU types within the same CPU architecture.
The I/O copy interfaces are io_copyin, io_copyio, io_copyout, and io_zero. These are generic interfaces that allow device drivers to perform I/O copy operations. Using these interfaces to perform the copy operation makes the device driver more portable across different CPU architectures and different CPU types within the same architecture.
The /dev/cb device driver example in Chapter 6 uses the read_io_port and write_io_port interfaces. [Return to example]
Section 6.7.1 shows an implementation of a probe interface for a /dev/cb device driver.
A device driver's xxslave interface is called only for a controller that has slave devices connected to it. This interface is called once for each slave attached to the controller. The following code fragment shows you how to set up a slave interface for a driver that operates on a TURBOchannel bus:
xxslave(dev, addr) struct device *dev; [1] io_handle_t bus_io_handle; [2] { /* Variable and structure declarations */
.
.
.
/* Code to check that the device is valid */
.
.
.
Device drivers pass the I/O handle to the following categories of interfaces, which are discussed in Writing Device Drivers: Tutorial. These interfaces can process the I/O handle to access the desired bus address space.
The CSR I/O access interfaces are read_io_port and write_io_port. These are generic interfaces that allow device drivers to read from and write to device registers. Using these interfaces to read data from and write data to a device register makes the device driver more portable across different bus architectures, different CPU architectures, and different CPU types within the same CPU architecture.
The I/O copy interfaces are io_copyin, io_copyio, io_copyout, and io_zero. These are generic interfaces that allow device drivers to perform I/O copy operations. Using these interfaces to perform the copy operation makes the device driver more portable across different CPU architectures and different CPU types within the same architecture.