-
insmod
resolves undefined symbols against the kernel's public symbols.
-
These include both functions and variables.
-
The symbol table lives in
/proc/ksyms
.
-
Global symbols in your module are added here.
-
Use
ksyms
to output them directly.
-
Module loading order can be important, particularly if they are stacked (dependent on the symbols defined in other modules).
-
An
alternative to exporting
all the global symbols of your module is to use the function
register_symtab
(register_symtab_from in kernels 2.0 and not required in kernels >2.1 -- see next example).
-
If
register_symtab
is called from the driver's initialization function, global symbols are no longer exported !
-
This is a way around using
static
everywhere, and allows multiple source files to share data through "module-only" global variables.
-
An example:
static struct symbol_table skull_syms = {
/*
use
EXPORT_SYMBOL(func_name)
for kernels > 2.1
*/
#include <linux/symtab_begin.h>
X(skull_fn1),
X(skull_fn2),
X(skull_variable),
#include <linux/symtab_end.h>
};
/*
register_symtab(...)
not needed in
init_module()
for kernels > 2.1 */
register_symtab(&skull_syms);
-
Since
register_symtab
is called after the module is loaded, it simply replaces the public exported symbols.
-
If you don't have any global symbols, use:
register_symtab(NULL);
OR EXPORT_NO_SYMBOLS macro in kernels > 2.1.
-
Of course,
rmmod
removes all kernel symbols associated with the module automatically, independent of the loading scheme.
-
init_module
registers any
facility
offered by the module.
-
This is performed by passing several arguments to a kernel function:
-
A pointer to a data structure, which embeds pointers to module functions.
-
The name of the facility being added.
-
If any errors occur during registration, e.g., out of memory, you must undo any registration performed before the error.
-
Otherwise, the kernel is left in an unstable state.
int init_module(void)
{
int err; /* Errors defined in <linux/errno.h> */
err = register_this(ptr1, "skull");
if (err) goto fail_this;
...
return 0;
...
fail_this(unregister_this(ptr1), "skull");
return err;}
-
The cleanup module also calls these unregister functions:
void cleanup_module(void)
{
...
unregister_this(ptr1, "skull");
return;
}
-
Usage Counts:
-
The system keeps usage counts on modules to determine if a module can be safely removed.
-
A module cannot be removed if it is "busy".
-
Macros, such as
MOD_INC_USE_COUNT
, are used to increment, decrement and check the status.
-
It is easy to lose track of the count during debug (e.g. a process gets destroyed because your driver references a NULL pointer).
-
Setting these macros to no-ops is useful in this case.
-
Usage Counts:
-
The file
/proc/modules
gives a list of the currently loaded modules.
-
Mine are given below:
epic100 10004 1 (autoclean)
nls_cp437 3548 1 (autoclean)
msdos 5276 1 (autoclean)
fat 30464 1 (autoclean) [msdos]
es1370 21244 0
soundcore 2372 4 [es1370]
-
The first field is the name of the module
-
The second is the number of memory pages or bytes in newer kernels.
-
The third field gives the usage count.
-
The forth field (kernels >2.1.18) lists optional flags
-
The fifth field (kernels >2.1.18) lists modules that reference this module.
-
rmmod
calls
cleanup_module
only if the usage count is zero.
-
As shown above,
cleanup_module
unregisters facilities
explicitly
while the symbol table is removed automatically.
-
A module can't accomplish its task without using system resource, such as
memory
,
I/O ports
,
interrupt lines
and
DMA channels
.
-
For memory, use the analogues to
malloc
and
free
,
kmalloc
and
kfree
.
-
kmalloc
takes a priority argument; use
GFP_KERNEL
in most cases.
-
Ports
:
-
Ports are requested so the driver can be assured exclusive access to them.
-
However, the
request
/
free
mechanism cannot prevent unauthorized access since there is no hardware to enforce it.
-
The file
/proc/ioports
contains information about registered ports.
-
A sample of mine is shown below:
0000-001f : dma1
0020-003f : pic1
0040-005f : timer
0060-006f : keyboard
-
Ports:
-
The range given in hex enclose the ports locked by a driver.
-
Other drivers should not access these ports until they are released.
-
Collision avoidance:
-
The
/proc/ioports
file is consulted to configure the jumpers on the hardware (if it is manual).
-
When the driver initializes, it can safely autodetect the hardware by
probing
.
-
Probing
involves writing and then reading the unregistered ports.
-
If the "correct" device is connected to the probed port, it will reply to queries with sensible codes.
-
If a different device is present, the response is unpredictable !
-
A compliant driver calls
check_region
to determine the lock status of the ports,
request_region
to lock them and
release_region
to free them.
-
Ports:
-
A typical registering sequence:
#include <linux/ioport.h>
#include <linux/errno.h>
static int skull_detect(unsigned int port,
unsigned int range)
{
int err;
if ((err = check_region(port, range)) < 0 )
return err; /* Busy */
if ( skull_probe_hw(port, range) != 0 )
return -ENODEV; /* Not found. */
/* Always succeeds */
request_region(port, range, "skull");
return 0;
}
-
Ports:
-
Note that
skull_probe_hw
would contain driver specific code that writes ports and looks for specific responses.
-
Typical release code:
static void skull_release(unsigned int port,
unsigned int range)
{
release_region(port, range);
}
-
Interrupt lines
:
-
A similar sequence is used to request and free them.
-
However, managing them is trickier than it is for ports (more on this later).
-
Note that the problems identified here for probing do
not
occur for PCI devices (also discussed later).
-
Several parameters required by the driver can change from system to system.
-
For example:
-
The actual I/O addresses.
-
The memory range.
-
Other driver specific parameters, such as the device model and release number.
-
The driver, of course, must be configured with the correct values at initialization time.
-
Again, most of these problems do not apply to PCI devices.
-
There are two ways to get these values:
-
The user specifies them explicitly.
-
The driver autodetects them.
-
Autodetection
is the best method but more difficult to implement.
-
Best approach is to autoconfigure while allowing the user to override.
-
insmod
accepts integer and string values as command line arguments, which override the global variables in the module.
-
For example, given the following global variables:
int skull_ival = 0;
char *skull_sval;
-
The following command can be used to assign values:
insmod skull skull_ival=656 skull_sval="a string val"
-
The values are already in effect by the time
init_module
is called.
-
Any
int
or
char *
variable, static or global, can be assigned.
-
If the configuration variables have the default value, perform autodetection.
-
Otherwise, keep the current value.
-
Of course, the default value should be some illegal value to trigger the autodetection.
/* Look for the device in port range 0x280-0x300. */
#define SKULL_PORT_FLOOR 0x280
#define SKULL_PORT_CEIL 0x300
#define SKULL_PORT_RANGE 0x010
/* Autodetect unless the user overrides.*/
static int skull_port_base = 0; /* Force autodetect */
static int skull_find_hw(void) /* Return # of devices */
{
int base = skull_port_base ? skull_port_base:
SKULL_PORT_FLOOR;
int result;
do {
if ( skull_detect(base, SKULL_PORT_RANGE) == 0)
{ skull_init_board(base);
result++;}
base += SKULL_PORT_RANGE;
} while (skull_port_base == 0 &&
base < SKULL_PORT_CEIL);
return result;
}