Several parameters that a driver needs to know can change from system to system. For instance, the driver must know the hardware’s actual I/O addresses, or memory range (this is not a problem with well-designed bus interfaces and only applies to ISA devices). Sometimes you’ll need to pass parameters to a driver to help it in finding its own device or to enable/disable specific features.
Depending on the device, there may be other parameters in addition to the I/O address that affect the driver’s behavior, such as device brand and release number. It’s essential for the driver to know the value of these parameters in order to work correctly. Setting up the driver with the correct values (i.e., configuring it) is one of the tricky tasks that need to be performed during driver initialization.
Basically, there are two ways to obtain the correct values: either the user specifies them explicitly or the driver autodetects them. Although autodetection is undoubtedly the best approach to driver configuration, user configuration is much easier to implement. A suitable trade-off for a driver writer is to implement automatic configuration whenever possible, while allowing user configuration as an option to override autodetection. An additional advantage of this approach to configuration is that the initial development can be done without autodetection, by specifying the parameters at load time, and autodetection can be implemented later.
Many drivers also have configuration options that control other aspects of their operation. For example, drivers for SCSI adapters often have options controlling the use of tagged command queuing, and the Integrated Device Electronics (IDE) drivers allow user control of DMA operations. Thus, even if your driver relies entirely on autodetection to locate hardware, you may want to make other configuration options available to the user.
Parameter values can be assigned at load time by
insmod or
modprobe; the latter can also read
parameter assignment from a configuration file (typically
/etc/modules.conf
). The commands accept the specification of integer and string values on
the command line. Thus, if your module were to provide an integer
parameter called skull_ival
and a string
parameter skull_sval
, the parameters could be set
at module load time with an insmod command
like:
insmod skull skull_ival=666 skull_sval="the beast"
However, before insmod can change module
parameters, the module must make them available. Parameters are
declared with the MODULE_PARM
macro, which is
defined in
module.h
. MODULE_PARM
takes
two parameters: the name of the variable, and a string describing its
type. The macro should be placed outside of any function and is
typically found near the head of the source file. The two parameters
mentioned earlier could be declared with the following lines:
int skull_ival=0; char *skull_sval; MODULE_PARM (skull_ival, "i"); MODULE_PARM (skull_sval, "s");
Five types are currently supported for module parameters:
b
, one byte; h
, a short (two
bytes); i
, an integer; l
, a
long; and s
, a string. In the case of string
values, a pointer variable should be declared;
insmod will allocate the memory for the
user-supplied parameter and set the variable accordingly. An integer
value preceding the type indicates an array of a given length; two
numbers, separated by a hyphen, give a minimum and maximum number of
values. If you want to find the author’s description of this feature,
you should refer to the header file
<linux/module.h>
.
As an example, an array that must have at least two and no more than four values could be declared as:
int skull_array[4]; MODULE_PARM (skull_array, "2-4i");
There is also a macro MODULE_PARM_DESC
, which
allows the programmer to provide a description for a module
parameter. This description is stored in the object file; it can be
viewed with a tool like objdump, and can
also be displayed by automated system administration tools. An example
might be as follows:
int base_port = 0x300; MODULE_PARM (base_port, "i"); MODULE_PARM_DESC (base_port, "The base I/O port (default 0x300)");
All module parameters should be given a default value; insmod will change the value only if explicitly told to by the user. The module can check for explicit parameters by testing parameters against their default values. Automatic configuration, then, can be designed to work this way: if the configuration variables have the default value, perform autodetection; otherwise, keep the current value. In order for this technique to work, the “default” value should be one that the user would never actually want to specify at load time.
The following code shows how skull autodetects the port address of a device. In this example, autodetection is used to look for multiple devices, while manual configuration is restricted to a single device. The function skull_detect occurred earlier, in Section 2.5.1.1, while skull_init_board is in charge of device-specific initialization and thus is not shown.
/* * port ranges: the device can reside between * 0x280 and 0x300, in steps of 0x10. It uses 0x10 ports. */ #define SKULL_PORT_FLOOR 0x280 #define SKULL_PORT_CEIL 0x300 #define SKULL_PORT_RANGE 0x010 /* * the following function performs autodetection, unless a specific * value was assigned by insmod to "skull_port_base" */ static int skull_port_base=0; /* 0 forces autodetection */ MODULE_PARM (skull_port_base, "i"); MODULE_PARM_DESC (skull_port_base, "Base I/O port for skull"); static int skull_find_hw(void) /* returns the # of devices */ { /* base is either the load-time value or the first trial */ int base = skull_port_base ? skull_port_base : SKULL_PORT_FLOOR; int result = 0; /* loop one time if value assigned; try them all if autodetecting */ do { if (skull_detect(base, SKULL_PORT_RANGE) == 0) { skull_init_board(base); result++; } base += SKULL_PORT_RANGE; /* prepare for next trial */ } while (skull_port_base == 0 && base < SKULL_PORT_CEIL); return result; }
If the configuration variables are used only within the driver (they
are not published in the kernel’s symbol table), the driver writer can
make life a little easier for the user by leaving off the prefix on
the variable names (in this case,
skull_
). Prefixes usually mean little to
users except extra typing.
For completeness, there are three other macros that place documentation into the object file. They are as follows:
-
MODULE_AUTHOR (name)
-
MODULE_DESCRIPTION (desc)
-
MODULE_SUPPORTED_DEVICE (dev)
Places an entry describing what device is supported by this module. Comments in the kernel source suggest that this parameter may eventually be used to help with automated module loading, but no such use is made at this time.
Get Linux Device Drivers, Second Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.