Compiling, Loading, Packaging, and Testing Drivers
This chapter describes the procedure for driver development, including code layout, compilation, packaging, and testing. The chapter provides information on the following subjects:
Driver Code Layout Structure
The code for a device driver is usually divided into the following files:
Header files (.h files)
Source files (.c files)
Optional configuration file (driver.conf file)
Header Files
Data structures specific to the device, such as a structure representing the device registers
Data structures defined by the driver for maintaining state information
Defined constants, such as those representing the bits of the device registers
Macros, such as those defining the static mapping between the minor device number and the instance number
Some of the header file definitions, such as the state structure, might be needed only by the device driver. This information should go in private header files that are only included by the device driver itself.
Any information that an application might require, such as the I/O control commands, should be in public header files. These are included by the driver and any applications that need information about the device.
While there is no standard for naming private and public files, one convention is to name the private header file xximpl.h and the public header file xxio.h.
.c Files
A .c file for a device driver contains the data declarations and the code for the entry points of the driver. It contains the #include statements the driver needs, declares extern references, declares local data, sets up the cb_ops and dev_ops structures, declares and initializes the module configuration section (the modlinkage(9S) and modldrv(9S) structures), makes any other necessary declarations, and defines the driver entry points.
driver.conf Files
driver.conf files are required for devices that are not self-identifying. Entries in the driver.conf file specify possible device instances the driver will probe for existence. For more information, see the scsi(4) and driver.conf(4) man pages. Driver global properties can also be set by entries in the driver.conf file. driver.conf files are optional for self-identifying devices (SID), where the entries can be used to add properties into SID nodes. See the sbus(4) and pci(4) man pages for details.
Preparing for Driver Installation
Before the driver is installed, it must be compiled into a binary, and a configuration file created, if necessary. The driver's module name must either match the name of the device nodes, or the system must be informed through add_drv(1M) or update_drv(1M) that this driver should manage other names.
The system maintains a one-to-one association between the name of the driver module and the name of the dev_info node. For example, a dev_info node for a device named wombat is handled by a driver module called wombat in a subdirectory called drv (resulting in drv/wombat if a 32-bit kernel or drv/sparcv9/wombat if 64-bit) found in the module path.
If the driver is a STREAMS network driver, then the driver name needs to meet the following constraints:
Alphanumeric characters (a-z, A-Z, 0-9), plus the underscore ('_'), are permitted only.
Neither the first nor the last character of the name may be a digit.
The name cannot exceed 16 characters in length. Names in the range of 3-8 characters in length are preferable.
If the driver should manage dev_info nodes with different names, the add_drv(1M) utility can create aliases. The -i flag specifies the names of other dev_info nodes that the driver handles. The update_drv() function can also modify aliases for an installed device driver.
Compiling and Linking the Driver
Compile each driver source file and link the resulting object files into a driver module. The example below shows a driver called xx that has two C-language source files and generates a driver module xx. The driver created in this example is intended for the 32-bit kernel:
% cc -D_KERNEL -c xx1.c % cc -D_KERNEL -c xx2.c % ld -r -o xx xx1.o xx2.o |
The _KERNEL symbol must be defined while compiling kernel (driver) code. No other symbols (such as sun4m) should be defined, aside from driver private symbols. DEBUG can also be defined to enable any calls to assert(9F). There is no need to use the -I flag for the standard headers.
Drivers intended for the 64-bit SPARC kernel should specify the -xarch=v9 option. Use the following compile line:
% cc -D_KERNEL -xarch=v9 -c xx1.c |
After the driver is stable, optimization flags can be used to build a production quality driver. For the Sun WorkShop Compilers C, the normal -O flag, or its equivalent -xO3, can be used. Note that -xO3 is the highest level of optimization device drivers should use (see cc(1)).
The following compile line was used to create 64-bit SPARC drivers provided with the Solaris 9 operating environment:
% cc -D_KERNEL -xcg92 -xarch=v9 -xcode=abs32 -xO3 -c xx1.c |
Where -xcg92 refers to the code generator, and the use of -xcode=abs32 leads to more compact code.
Note - Running ld -r is necessary even if there is only one object module.
Module Dependencies
If the driver module depends on symbols exported by another kernel module, the dependency can be specified by the -dy and -N options of ld. If the driver depends on a symbol exported by misc/foo, the example below should be used to create the driver binary. See the ld(1) man page.
% ld -dy -r -o xx xx1.o xx2.o -N misc/foo |
Writing a Hardware Configuration File
If the device is non-self-identifying, the kernel requires a hardware configuration file for it. If the driver is called xx, the hardware configuration file for it should be called xx.conf. See the driver.conf(4), pseudo(4), sbus(4), scsi(4), and update_drv(1M) man pages for more information on hardware configuration files. On the Intel platform, device information is now supplied by the booting system. Hardware configuration files should no longer be needed, even for non-self-identifying devices.
Arbitrary properties can be defined in hardware configuration files by adding entries of the form property=value, where property is the property name, and value is its initial value. This enables devices to be configured by changing the property values.