Automatic and Manual Configuration

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)

Puts the author’s name into the object file.

MODULE_DESCRIPTION (desc)

Puts a description of the module into the object file.

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.