Introduction
Previous post introduced some basic knowledge about Linux kernel modules how they are inserted and removed from the kernel and invoked functions.
This post is a continuation to this device drivers series, it will explain how arguments are used in a kernel module.
Module parameters are used to give the user possibility to supply the module with input values in the command line inserting it. There are three types of module parameters and they are implemented using following macros:
- module_param()
- module_param_array()
- module_param_cb()
module_param()
module_param() macro is used to declare parameters and their associated variables. It can be used as shown in example below:
static int variable_name=1;
module_param(variable_name, int, S_IWUSR);
module_param() takes three input parameters:
- variable name
- variable type
e.g. int - variable permission
e.g. S_IWUSR (Write permission for the owner of the file)
multiple permissions can be combined using OR operator such as “S_IWUSR | S_IRGRP”…
The parameter can be passed to the insmod command as shown in command below:
$ insmod driver_example.ko variable_name=1
Once the module is inserted, the parameters values can be accessed in their appropriate files “/sys/module/module_name/parameters/parameter_name”. They can be read and written from user space.
$ echo "1" > /sys/driver_example/parameters/variable_name
module_param_array()
module_param_array() macro is used to declare parameters in form of arrays. They’re used as shown in code below:
static int count;
static int array_name[3];
module_param(array_name, int, &count,S_IWUSR);
module_param_array() takes four input parameters:
- array variable name
- array element type
e.g. int - address of elements counter (Optional, use NULL if it’s not needed)
count counts the number of elements given in the array parameter by the user when loading the module (see final module example for a better view).
Similarly to module_param the array parameter can be passed using insmod with elements separated by commas :
insmod driver_example.ko array_name=1,2,3
module_param_cb()
As discussed above parameters can be modified from user space by the user. In some cases it’s necessary to notify the kernel module about a certain value of a parameter to execute a specific task. Here is the role of module_param_cb() macro that provides a callback to the kernel module when a parameter value is changed. It can be declared as shown in following example:
static int cb_variable_name;
module_param_cb(variable_name, &my_param_ops, &cb_variable_name, S_IWUSR|S_IRUSR );
- variable_name
It’s the variable associated to the parameter to be notified with the callback - cb_variable_name
It’s the variable name holding the value to be written to variable_name - my_param_ops
It’s a variable structure of type kernel_param_ops which is defined in linux/moduleparam.h
It’s composed of two elements that are outputs of two pointer functions, one for “set” and the other for “get” used for writing and reading the value of a variable. The pointer functions are to be declared in the module program. Below the part defining this struct in linux/moduleparam.h and the module example in the last part will show the complete code implementation.
struct kernel_param_ops {
/* How the ops should behave */
unsigned int flags;
/* Returns 0, or -errno. arg is in kp->arg. */
int (*set)(const char *val, const struct kernel_param *kp);
/* Returns length written or -errno. Buffer is 4k (ie. be short!) */
int (*get)(char *buffer, const struct kernel_param *kp);
/* Optional function to free kp->arg when module unloaded. */
void (*free)(void *arg);
};
Module example with parameters
The module below is an example using three parameters:
- parameter1 as module_param
- array1[3] as array_param
- cb_parameter2 as module_param_cb
/*******************************************************************************
* \file driver-example.c
*
* \details Simple driver example using parameters
*
* \author AJ Embedded Systems Consultant https://aj-ese.com
*
* *******************************************************************************/
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
int parameter1;
int array1[3], count;
int cb_parameter2, cb_value;
module_param(parameter1, int, S_IRUSR|S_IWUSR);
module_param_array(array1, int, &count, S_IRUSR|S_IWUSR);
/*----------------------Module_param_cb() Begin--------------------------------*/
int param_callback(const char *val, const struct kernel_param *kp)
{
int set = param_set_int(val, kp);
if(set==0) {
printk(KERN_INFO "The parameter cb_parameter2 has been changed, the new value is: %d\n", cb_value);
return 0;
}
return -1;
}
const struct kernel_param_ops param_ops =
{
.set = param_callback,
.get = param_get_int,
};
module_param_cb(cb_parameter2, param_ops, &cb_value, S_IRUSR|S_IWUSR );
/*----------------------Module_param_cb() End----------------------------------*/
/*
** Module Init function
*/
static int AJ_driver_init(void)
{
int i;
printk(KERN_INFO "Welcome to AJ-ESE \n");
printk(KERN_INFO "The AJ-driver is inserted successfully...\n");
printk(KERN_INFO "parameter1 value is: %d\n", parameter1);
printk(KERN_INFO "cb_parameter2 value is: %d\n", cb_parameter2);
for (i = 0; i < (sizeof array1 / sizeof (int)); i++) {
printk(KERN_INFO "Array value[%d] is: %d\n", i, array1[i]);
}
printk(KERN_INFO "Count= %d\n", count);
return 0;
}
/*
** Module Exit function
*/
static void AJ_driver_exit(void)
{
printk(KERN_INFO "The AJ-driver is removed successfully...\n");
}
module_init(AJ_driver_init);
module_exit(AJ_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AJ Embedded Systems Consultant https://aj-ese.com");
MODULE_DESCRIPTION("Simple driver example using parameters");
MODULE_VERSION("1:0.1");

Leave a Reply