Passing Arguments to Linux Device Driver – Linux Device Driver Tutorial Part 3

This article is a continuation of the  Series on Linux Device Driver and carries on 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.

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 used to register the callback whenever the argument (parameter) got changed. 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.

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 = &notify_param, // Use our setter ...
        .get = &param_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

 Passing Arguments to Device Driver

Loading the Driver

sudo insmod hello_world_module.ko valueETX=14 nameETX="EmbeTronicX" arr_valueETX=100,102,104,106

 Passing Arguments to Device Driver

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.

 Passing Arguments to 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.

  1. sudo sh -c "echo 13 > /sys/module/driver/parameters/cb_valueETX"
  2. Type sudo su. Then enter the password if it asks. Then do echo 13 > /sys/module/hello_world_module/parameters/cb_valueETX

 Passing Arguments to Device Driver

Now do dmesg and check.

 Passing Arguments to Device Driver

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.

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.

4.9 17 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

13 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
13
0
Would love your thoughts, please comment.x
Ads Blocker Image Powered by Code Help Pro
Ads Blocker Detected!!!

We have detected that you are using extensions to block ads. Please support us by disabling these ads blocker.

Refresh