Development Environment and Tools
This section summarizes the driver development process and provides some pointers to resources.
For more information on the development process, see Driver Development Summary in Writing Device Drivers.
Sun offers training courses in Solaris OS internals, crash dump analysis, writing device
drivers, DTrace, Sun Studio, and other topics useful to Solaris developers. See https://www.sun.com/training/
for more information.
The general steps in writing a device driver are as follows:
Write a .c source file using the interfaces and structures defined in man page sections 9E, 9F, and 9S. Most of the include files you need are in /usr/include/sys. The function and structure man pages show which include files you need.
Write a .conf hardware configuration file to define property values for your driver.
Compile and link your driver. Always use the -D_KERNEL option when you compile a driver for the Solaris OS. The default compile result is 32-bit. To get a 64-bit result on a 64-bit platform, specify the appropriate 64-bit option as described in Building a Driver.
Copy your driver binary file and your driver configuration file to the appropriate [platform]/kernel directories. See Driver Directory Organization for descriptions of driver directories.
Use the add_drv(1M) command to load your driver. When your driver is loaded, you can see your driver in /dev and /devices. You can also see an entry for your driver in the /etc/name_to_major file.
Writing a Driver
A driver consists of a C source file and a hardware configuration
file.
Writing a Driver Module
The C code for a driver is a collection of data and
functions that define a kernel module. As noted in Structural Differences Between Kernel Modules and User Programs, a driver has
no main() routine. Many of the subroutines of a driver are special functions
called entry points. See Device Drivers for information about entry points.
The function man pages provide both the function declaration that you need in
your driver and the list of header files you need to include.
Make sure you consult the correct man page. For example, the following command displays
the ioctl(2) man page. The ioctl(2) system call cannot be used in a
device driver.
% man ioctl
Use one of the following commands to display the ioctl(9E) man page. The
ioctl(9E) subroutine is a device driver entry point.
% man ioctl.9e
% man -s 9e ioctl
By convention, the names of functions and data that are unique to this
driver begin with a common prefix. The prefix is the name of
this driver or an abbreviation of the name of this driver. Use the
same prefix for all names that are specific to this driver. This practice
makes debugging much easier. Instead of seeing an error related to an ambiguous
attach() function, you see an error message about mydriver_attach() or newdriver_attach().
A 64-bit system can run both 32-bit user programs and 64-bit user programs. A
64-bit system runs 32-bit programs by converting all data needed between the
two data models. A 64-bit kernel supports both 64-bit and 32-bit user data.
Whenever a 64-bit driver copies data between kernel space and user space, the
driver must use the ddi_model_convert_from(9F) function to determine whether the data must be
converted between 32-bit and 64-bit models. For an example, see Reporting and Setting Device Size and Re-initializing the Device.
The Sun Studio IDE includes the following three source editors: GVIM, XEmacs, and
the built-in Source Editor provided by NetBeans. The IDE provides online help for
these tools. You can also run GVIM and XEmacs from the command line.
See vim(1) and xemacs(1).
For more information, see the following resources:
Writing a Configuration File
A driver that is not self-identifying might need a configuration file named node_name.conf,
where node_name is the prefix for the device. A self-identifying driver is a
driver that can obtain all the property information it needs from the DDI
property interfaces such as ddi_prop_get_int(9F) and ddi_prop_lookup(9F). The minimum information that a
configuration file must contain is the name of the device node and the
name or type of the device's parent.
On the x86 platform, device information is supplied by the booting system. Hardware
configuration files should no longer be needed, even for non-self-identifying devices.
For more information about device driver configuration files, see the driver.conf(4) man
page. For an example configuration file, see Writing the Device Configuration File.
Building a Driver
This section tells you how to compile and link a driver for different
architectures.
Make sure you have installed the Solaris OS at the Developer level
or above. Follow the instructions in Chapter 2, Installing With the Solaris Installation Program (Tasks), in Solaris Express Installation Guide: Basic Installations. Select Custom Install, and select the Developer
cluster or above.
A 64-bit kernel cannot use a 32-bit driver. A 64-bit kernel can
use only 64-bit drivers. All parts of any particular program must use the
same data model. A device driver is not a complete program. The kernel
is a complete program. A driver is a part of the kernel program.
If you want your device to work with the Solaris OS in 32-bit
mode and with the Solaris OS in 64-bit mode, then you must provide
both a 32-bit driver and a 64-bit driver.
By default, compilation on the Solaris OS yields a 32-bit result on every
architecture. To obtain a 64-bit result, use the compilation options specified in this
section for 64-bit architectures.
Use the prtconf(1M) command with the -x option to determine whether the firmware
on this system is 64-bit ready.
Compiling with Sun Studio
Use the -D_KERNEL option to indicate that this code defines a kernel module.
If you are compiling for a 64-bit SPARC architecture using Sun Studio 9, Sun Studio 10, or Sun Studio 11, use the -xarch=v9 option:
% cc -D_KERNEL -xarch=v9 -c mydriver.c
% ld -r -o mydriver mydriver.o
If you are compiling for a 64-bit SPARC architecture using Sun Studio 12, use the -m64 option:
% cc -D_KERNEL -m64 -c mydriver.c
% ld -r -o mydriver mydriver.o
If you are compiling for a 64-bit x86 architecture using Sun Studio 10 or Sun Studio 11, use both the -xarch=amd64 option and the -xmodel=kernel option:
% cc -D_KERNEL -xarch=amd64 -xmodel=kernel -c mydriver.c
% ld -r -o mydriver mydriver.o
If you are compiling for a 64-bit x86 architecture using Sun Studio 12, use the -m64 option, the -xarch=sse2a option, and the -xmodel=kernel option:
% cc -D_KERNEL -m64 -xarch=sse2a -xmodel=kernel -c mydriver.c
% ld -r -o mydriver mydriver.o
If you are compiling for a 32-bit architecture, use the following build commands:
% cc -D_KERNEL -c mydriver.c
% ld -r -o mydriver mydriver.o
Note - Sun Studio 9 does not support 64-bit x86 architectures. Use Sun Studio 10,
Sun Studio 11, or Sun Studio 12 to compile and debug drivers
for 64-bit x86 architectures.
For more information on compile and link options, see the Sun Studio Man Pages and the
Sun Studio 12: C User’s Guide. See the Sun Studio Information Center in the Sun Studio 12 Collection for Sun Studio books about
dbx, dmake, Performance Analyzer, and other software development topics. To read technical articles about
Sun Studio, see Sun Studio Technical Articles. To download Sun Studio, go to https://developers.sun.com/sunstudio/.
Compiling with the GNU C Compiler
To get the GNU C compiler, you must install the Solaris OS
at the Developer level or above. Follow the instructions in Chapter 2, Installing With the Solaris Installation Program (Tasks), in Solaris Express Installation Guide: Basic Installations. Select Custom Install,
and select the Developer cluster or above. The GNU C compiler is installed
in /usr/sfw.
Use the -D_KERNEL option to indicate that this code defines a kernel module.
These examples show options that are required for correct functionality of the result.
If you are compiling for a 64-bit SPARC architecture, use the following build commands:
% gcc -D_KERNEL -m64 -mcpu=v9 -mcmodel=medlow -fno-pic -mno-fpu -ffreestanding -nodefaultlibs -c mydriver.c
% ld -r -o mydriver mydriver.o
You might also want to use the -mtune=ultrasparc option and the -O2 option.
If you are compiling for a 64-bit x86 architecture, use the following build commands:
% gcc -D_KERNEL -m64 -mcmodel=kernel -mno-red-zone -ffreestanding -nodefaultlibs -c mydriver.c
% ld -r -o mydriver mydriver.o
You might also want to use the -mtune=opteron option and the -O2 option.
If you are compiling for a 32-bit architecture, use the following build commands:
% gcc -D_KERNEL -ffreestanding -nodefaultlibs -c mydriver.c
% ld -r -o mydriver mydriver.o
For more information on these and other options, see the gcc(1) man page.
See also the GCC web site at https://gcc.gnu.org/. More information about using
the gcc compiler with the Solaris OS is on the OpenSolaris web site
at https://opensolaris.org/os/community/tools/gcc/.
Installing a Driver
After you write and build your driver, you must install the driver
binary. To install a driver, copy the driver binary and the configuration file
to the appropriate /kernel/drv directory.
Make sure you are user root when you install a driver.
Copy the configuration file to the kernel driver area of the system.
# cp mydriver.conf /usr/kernel/drv
Install drivers in the /tmp directory until you are finished modifying and testing
the _info(), _init(), and attach() routines. See Device Driver Testing Tips for more information.
Copy the driver binary to the /tmp directory.
# cp mydriver /tmp
Link to the driver from the kernel driver directory.
On a 64-bit SPARC architecture, link to the sparcv9 directory:
# ln -s /tmp/mydriver /usr/kernel/drv/sparcv9/mydriver
On a 64-bit x86 architecture, link to the amd64 directory:
# ln -s /tmp/mydriver /usr/kernel/drv/amd64/mydriver
On a 32-bit architecture, create the link as follows:
# ln -s /tmp/mydriver /usr/kernel/drv/mydriver
When the driver is well tested, copy the driver directly to the
appropriate kernel driver area of the system.
On a 64-bit SPARC architecture, copy the driver to the sparcv9 directory:
# cp mydriver /usr/kernel/drv/sparcv9/mydriver
On a 64-bit x86 architecture, copy the driver to the amd64 directory:
# cp mydriver /usr/kernel/drv/amd64/mydriver
On a 32-bit architecture, copy the driver to the kernel driver area of the system:
# cp mydriver /usr/kernel/drv/mydriver
Adding, Updating, and Removing a Driver
Use the add_drv(1M) command to make the installed driver usable. Be sure you
are user root when you use the add_drv(1M) command.
# add_drv mydriver
The following events take place when you add a driver:
The _info(9E), _init(9E), and attach(9E) entry points are called in that order.
The driver is added to the /devices directory.
The driver is the most recent module listed by modinfo(1M).
The driver is the most recent module listed in the file /etc/name_to_major.
The file /etc/driver_aliases might be updated. The /etc/driver_aliases file shows which devices are bound
to which drivers. If a driver is not listed in the /etc/driver_aliases
file, then the Solaris OS does not load that driver or attach to
that driver. Each line of the /etc/driver_aliases file shows a driver name followed
by a device name. You can search this file to determine which driver
is managing your device.
Note - Do not edit the /etc/driver_aliases file manually. Use the add_drv(1M) command to establish
a device binding. Use the update_drv(1M) command to change a device binding.
The example drivers shown in this book manage pseudo devices. If your driver
manages real hardware, then you need to use the -c and -i options on
the add_drv(1M) command or the -i option on the update_drv(1M) command. To
specify a device class or device ID, you might find the following sites
useful. This information also is useful to search the /etc/driver_aliases file to find
out whether a device already is supported.
Use the update_drv(1M) command to notify the system about attribute changes to an
installed device driver. By default, the update_drv(1M) command reloads the hardware configuration file
for the specified driver. Use the prtconf(1M) command to review the current configuration
information for a device and driver. For example, the -D option shows which
driver manages a particular device. The -P option shows information about pseudo devices.
Use the rem_drv(1M) command to update the system driver configuration files so that
the driver is no longer usable. The rem_drv(1M) command does not physically delete
driver files. If possible, the rem_drv(1M) command unloads the driver from memory.
Loading and Unloading a Driver
A driver is loaded into memory when a device that the driver
manages is accessed. A driver might be unloaded from memory when the driver
is not being used. Normally, you do not need to load a driver
into memory manually or unload a driver from memory manually.
To manually load a loadable module into memory, use the modload(1M) command.
While you are developing your driver, you might want to manually unload the
driver and then update the driver. To manually unload a loadable module from
memory, use the modunload(1M) command.
Testing a Driver
Drivers should be thoroughly tested in the following areas:
Configuration
Functionality
Error handling
Loading, unloading, and removing
All drivers will need to be removed eventually. Make sure that your driver can be successfully removed.
Stress, performance, and interoperability
DDI/DKI compliance
Installation and packaging
For detailed information on how to test your driver and how to
avoid problems during testing, see the following references:
Additional testing is specific to the type of driver.