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.
Table of Contents
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 <embetronicx@gmail.com>");
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
sudosu. 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 Driver | RTOS
Hi, 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!
Discover more from EmbeTronicX
Subscribe to get the latest posts sent to your email.





can i able to use module_param_cb() more than once in a same module?.
Hello,
can we able to handle strings using module_param_cb macro.
yes, we can handle strings.
just it is the same as command line arguments in C.
Thank you very much for this tutorial it’s being very helpful
what are these param_set_int and param_get_int ?
This param_set_int is used to set the variable and param_get_int is used to get the variable.
In the module_param_cb(), we are passing the variable to set or get and my_param_ops which has the set, and get callback functions.
So, when the callback is called, it will write the value using param_set_int.
It compiles successfully.The part assign value with “echo 13 > /sys/module/hello_world_module/parameters/cb_valueETX” not working even if giving sudo. It gives bash: valueETX: Permission denied.
Try like below:
Instead of command line, how can i read/write the param using an application code?
Hello Poorna,
You can use system() or popen() API based on your need. Just explore that. Happy coding ????
Hi,
I think there is an error in line 32:
module_param_cb(cb_valueETX, &my_param_ops, &cb_valueETX, S_IRUGO|S_IWUSR );
^^^
I had to set the write-permission indicated above to “IWUGO”, to make the value update via “echo 13 > /sys/[…]/cb_valueETX” work. Before that change, I always got a permission error – even with “sudo”.
Hi Brend,
when you use S_WUGO, it will throw an error while compiling. Having module parameter’s writable by non-priveledged user normally is not good for security reasons. So we have updated the other way of writing the variable. Please check it.
What should be the operation if I want to update the value of valueETX after insmod command ?
Hi Dheeraj,
You can write through the sysfs command. Please use the below command to write.
sudo sh -c "echo 18 > /sys/module/driver/parameters/valueETX"But you won’t get notification like the varaible
cb_valueETX. If you want to get notification callback, then you have to use module_param_cb.Hi,
I think
.set = ¬ify_param, // Use our setter …
.get = ¶m_get_int, // .. and standard getter
here & operator is not required as it is a function and to pass a function simply we have to write the name of function. So we have to use below mentioned thing which won’t through any warning.
.set = notify_param, // Use our setter …
.get = param_get_int, // .. and standard getter
Also please check, i think this won’t work fine as after giving this below mentioned command
echo 13 > /sys/module/hello_world_module/parameters/cb_valueETX
we are getting
bash: echo: write error: Operation not permitted. So use
echo 13 > /sys/module/hello_world_module/parameters/valueETX.
This should give proper result as shown in image.
Thanks,
Hi Mohan,
For functions, we can put
&func_nameor justfunc_nameis enough to take the function’s address. We can use any one of the method. And for the write permission, we have updated the way of written the variable. Please check it out.Awesome tutorial for device driver in linux love the way you are explaning each toturial.
Can you please upload a tutorial for LED ON/OFF in Raspberry pi without using predefined API for RPi.
Thanks a ton, guys for such a great series of articles. I’ve never seen such informative and detailed contents. Thanks again!