This article is a continuation of the Series on Linux Device Driver and carries the discussion on character drivers and their implementation. The aim of this series is to provide easy and practical examples that anyone can understand. This is the tutorial about Passing Arguments to Linux Device Driver – Linux Device Driver Tutorial Part 3.
In our previous tutorial, we have written a simple device driver. Now we will focus on how to pass the information to the kernel modules during the loading time.
You can find a video explanation of this tutorial here. You can also find all the Linux device driver’s video playlists here.
Passing Arguments to Linux Device Driver
We can pass the arguments to any other functions in the same program. But Is it possible to pass any arguments to any program? I think Probably yes. Right? Well, we can. In C Programming we can pass the arguments to the program. For that, we need to add argc
and argv
in the main function definition. I hope everyone knows that. Now come to our topic. Is it possible to pass the argument to the Linux Device Driver? Fine. In this tutorial, we are going to see that topic.
Module Parameters Macros
module_param()
module_param_array()
module_param_cb()
Before discussing these macros we have to know about the permissions of the variable.
There are several types of permissions:
- S_IWUSR
- S_IRUSR
- S_IXUSR
- S_IRGRP
- S_IWGRP
- S_IXGRP
In this S_I is a common header.
R = read ,W =write ,X= Execute.
USR =user ,GRP =Group
Using OR ‘|’ (or operation) we can set multiple permissions at a time.
module_param()
This macro is used to initialize the arguments. module_param
takes three parameters: the name of the variable, its type, and a permissions mask to be used for an accompanying sysfs entry. The macro should be placed outside of any function and is typically found near the head of the source file. module_param()
macro, defined in linux/moduleparam.h
.
module_param(name, type, perm);
module_param() macro creates the sub-directory under /sys/module. For example,
module_param(valueETX, int, S_IWUSR|S_IRUSR);
This will create a sysfs entry. (/sys/module/hello_world_module/parameters/valueETX
)
Numerous types are supported for module parameters:
- bool
- invbool
A boolean (true or false) value (the associated variable should be of type int). The invbool
type inverts the value, so that true values become false and vice versa.
- charp
A char pointer value. Memory is allocated for user-provided strings, and the pointer is set accordingly.
- int
- long
- short
- uint
- ulong
- ushort
Basic integer values of various lengths. The versions starting with u are for unsigned values.
module_param_array()
This macro is used to send the array as an argument to the Linux device driver. Array parameters, where the values are supplied as a comma-separated list, are also supported by the module loader. To declare an array parameter, use:
module_param_array(name,type,num,perm);
Where,
name
is the name of your array (and of the parameter),
type
is the type of the array elements,
num
is an integer variable (optional) otherwise NULL,
perm
is the usual permissions value.
module_param_cb()
This macro is used to register the callback. Whenever the argument (parameter) got changed, this callback function will be called. I think you don’t understand. Let me explain it properly.
For Example,
I have created one parameter by using module_param()
.
module_param(valueETX, int, S_IWUSR|S_IRUSR);
This will create a sysfs entry. (/sys/module/hello_world_module/parameters/valueETX
)
You can change the value of valueETX
from the command line by
sudo su echo 1 > /sys/module/hello_world_module/parameters/valueETX
This will update the valueETX
variable. But there is no way to notify your module that “valueETX
” has changed.
By using this module_param_cb()
macro, we can get a notification.
If you want to get a notification whenever the value got to change, we need to register our handler function to its file operation structure first.
struct kernel_param_ops { int (*set)(const char *val, const struct kernel_param *kp); int (*get)(char *buffer, const struct kernel_param *kp); void (*free)(void *arg); };
For further explanation, please refer to the below program.
When we will need this notification?
I will tell you about the practical scenario. Whenever the value is set to 1, you have to write something into a hardware register. How can you do this if the change of value variable is not notified to you? Got it? I think you have understood. If you didn’t understand, just see the explanation posted below.
Passing Arguments to Linux Device Driver – Programming
In this example, i explained all (module_param, module_param_array, module_param_cb).
For module_param()
, I have created two variables. One is integer (valueETX
) and another one is a string (nameETX
).
For module_param_array()
, I have created one integer array variable (arr_valueETX
).
For module_param_cb()
, I have created one integer variable (cb_valueETX
).
You can change all variable using their sysfs entry which is under /sys/module/hello_world_module/parameters/
But you won’t get any notification when they got to change, except the variable which is created by module_param_cb()
macro.
[Get the Linux Device Driver source code from the GitHub]
/***************************************************************************//** * \file hello_world.c * * \details Simple hello world driver * * \author EmbeTronicX * * *******************************************************************************/ #include<linux/kernel.h> #include<linux/init.h> #include<linux/module.h> #include<linux/moduleparam.h> int valueETX, arr_valueETX[4]; char *nameETX; int cb_valueETX = 0; module_param(valueETX, int, S_IRUSR|S_IWUSR); //integer value module_param(nameETX, charp, S_IRUSR|S_IWUSR); //String module_param_array(arr_valueETX, int, NULL, S_IRUSR|S_IWUSR); //Array of integers /*----------------------Module_param_cb()--------------------------------*/ int notify_param(const char *val, const struct kernel_param *kp) { int res = param_set_int(val, kp); // Use helper for write variable if(res==0) { printk(KERN_INFO "Call back function called...\n"); printk(KERN_INFO "New value of cb_valueETX = %d\n", cb_valueETX); return 0; } return -1; } const struct kernel_param_ops my_param_ops = { .set = ¬ify_param, // Use our setter ... .get = ¶m_get_int, // .. and standard getter }; module_param_cb(cb_valueETX, &my_param_ops, &cb_valueETX, S_IRUGO|S_IWUSR ); /*-------------------------------------------------------------------------*/ /* ** Module init function */ static int __init hello_world_init(void) { int i; printk(KERN_INFO "ValueETX = %d \n", valueETX); printk(KERN_INFO "cb_valueETX = %d \n", cb_valueETX); printk(KERN_INFO "NameETX = %s \n", nameETX); for (i = 0; i < (sizeof arr_valueETX / sizeof (int)); i++) { printk(KERN_INFO "Arr_value[%d] = %d\n", i, arr_valueETX[i]); } printk(KERN_INFO "Kernel Module Inserted Successfully...\n"); return 0; } /* ** Module Exit function */ static void __exit hello_world_exit(void) { printk(KERN_INFO "Kernel Module Removed Successfully...\n"); } module_init(hello_world_init); module_exit(hello_world_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("EmbeTronicX <[email protected]>"); MODULE_DESCRIPTION("A simple hello world driver"); MODULE_VERSION("1.0");
Compiling
This is the code of Makefile
.
obj-m += hello_world_module.o KDIR = /lib/modules/$(shell uname -r)/build all: make -C $(KDIR) M=$(shell pwd) modules clean: make -C $(KDIR) M=$(shell pwd) clean
In the terminal, enter the command sudo make
Loading the Driver
sudo insmod hello_world_module.ko valueETX=14 nameETX="EmbeTronicX" arr_valueETX=100,102,104,106
Verify the parameters by using dmesg
Now our module got loaded. Now check dmesg
. In the below picture, every value got passed to our device driver.
Now I’m going to check module_param_cb()
is whether calling that handler function or not. For that, I need to change the variable in sysfs. You can write that variable in two ways.
sudo sh -c "echo 13 > /sys/module/hello_world_module/parameters/cb_valueETX"
- Type
sudo
su
. Then enter the password if it asks. Then doecho 13 > /sys/module/hello_world_module/parameters/cb_valueETX
Now do dmesg
and check.
See the above result. So Our callback function got called. But if you change the value of other variables, you won’t get the notification.
Unloading the Driver
Finally unload the driver by using sudo rmmod hello_world_module
.
Video Explanation
You can check the video explanation of this tutorial below.
I hope you have understood. If you have any doubt, please comment below. In our next tutorial, we will see the major and minor numbers in the Linux device driver. Please find the other Linux device driver tutorials here.
You can also read the below tutorials.

Embedded Software | Firmware | Linux Devic Deriver | RTOS
Hi, I’m SLR. I am a tech blogger and an Embedded Engineer. I am always eager to learn and explore tech-related concepts. And also, I wanted to share my knowledge with everyone in a more straightforward way with easy practical examples. I strongly believe that learning by doing is more powerful than just learning by reading. I love to do experiments. If you want to help or support me on my journey, consider sharing my articles, or Buy me a Coffee! Thank you for reading my blog! Happy learning!