In the previous post , I covered the basics of kernel development with a simple example of loadable kernel module that has only init and exit functions. In this post we will add a parameters to the module. Using the parameters, you can access the module global variables while loading the module and on runtime while the module is already loaded.

Loading a module with parameters

When you load a module using insmod command you can supply the parameters as key=value pairs for example:

# insmod ./mymod.ko irq=20 name=mydev debug=1 address=0x1000,0x2000,0x3000 1 # insmod ./mymod.ko irq=20 name=mydev debug=1 address=0x1000,0x2000,0x3000

The parameter can be a number, a string or an array (of numbers or strings)

To declare a simple parameter in a module, declare a global variable and use module_param macro for example:

#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/kernel.h> static int irq=10; module_param(irq,int,0660); static int debug=0; module_param(debug,int,0660); static char *devname = "simpdev"; module_param(devname,charp,0660); static int simple_init(void) { printk(KERN_WARNING "hello... irq=%d name=%s debug=%d

",irq,devname,debug); return 0; } static void simple_cleanup(void) { printk(KERN_WARNING "bye... irq=%d name=%s debug=%d

",irq,devname,debug); } module_init(simple_init); module_exit(simple_cleanup); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/kernel.h> static int irq = 10 ; module_param ( irq , int , 0660 ) ; static int debug = 0 ; module_param ( debug , int , 0660 ) ; static char * devname = "simpdev" ; module_param ( devname , charp , 0660 ) ; static int simple_init ( void ) { printk ( KERN _ WARNING "hello... irq=%d name=%s debug=%d

" , irq , devname , debug ) ; return 0 ; } static void simple_cleanup ( void ) { printk ( KERN _ WARNING "bye... irq=%d name=%s debug=%d

" , irq , devname , debug ) ; } module_init ( simple_init ) ; module_exit ( simple_cleanup ) ;

Now you can compile and load the module with irq, debug and devname parameters:

# insmod ./simple.ko irq=44 devname=simpdev debug=0 1 # insmod ./simple.ko irq=44 devname=simpdev debug=0

The global variable will be initialized before the init function called use dmesg to see the output:

# dmesg ... [ 144.526050] hello... irq=44 name=simpdev debug=0 1 2 3 # dmesg . . . [ 144.526050 ] hello . . . irq = 44 name = simpdev debug = 0

module_param macro

The macro prototype is

<span class="cp"> module_param(name, type, perm)</span> 1 < span class = "cp" > module_param ( name , type , perm ) < / span >

name – name of an already defined variable

type – parameter type(int,long,charp,…)

perm – permissions for /sys/module/<module_name>/parameters/<param> (or 0 for no such module parameter value file)

Reading and Changing the parameter value

The file in /sys/module/<module_name>/parameters is used to read/write the parameter value while the module is loaded. For example if we loaded a module with debug=0 and we want to turn on debugging messages without reloading the module we can use the file:

# echo "1">/sys/module/simple/parameters/debug 1 # echo "1">/sys/module/simple/parameters/debug

You can see the debug variable changed while you unload the module

# rmmod simple # dmesg ... [ 362.125050] bye... irq=44 name=simpdev debug=0 1 2 3 4 # rmmod simple # dmesg . . . [ 362.125050 ] bye . . . irq = 44 name = simpdev debug = 0

Validating the parameter value

Using the file in /sys , if we have permission, we can set any value we want. If for example the irq can only be in the range 1 – 32 , there is no way to validate it because the user access the variable directly. To solve this problem we can implement an interface for setting an getting parameter values – kernel_param_ops

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); }; 1 2 3 4 5 6 7 8 9 10 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 ) ; } ;

To implement the interface we need to define a variable , implement the set/get methods and register it using module_param_cb:

static int my_set(const char *val, const struct kernel_param *kp) { int n = 0, ret; ret = kstrtoint(val, 10, &n); if (ret != 0 || n < 1 || n > 32) return -EINVAL; return param_set_int(val, kp); } static const struct kernel_param_ops param_ops = { .set = my_set, .get = param_get_int, }; static int num; module_param_cb(simpcb, ¶m_ops, &num, 0664); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static int my_set ( const char * val , const struct kernel_param * kp ) { int n = 0 , ret ; ret = kstrtoint ( val , 10 , & n ) ; if ( ret != 0 || n < 1 || n > 32 ) return - EINVAL ; return param_set_int ( val , kp ) ; } static const struct kernel_param_ops param_ops = { . set = my_set , . get = param_get_int , } ; static int num ; module_param_cb ( simpcb , & param_ops , & num , 0664 ) ;

The actual parameter value is saved in num , the set function get the user input as a string and convert it to int , then we check if the value is correct (1 – 32) and set it using the param_set_int function provided by the kernel. To return the parameter value we use the provided param_get_int function

If we try to load the module with wrong value or if we try to set a wrong value using the /sys file beget an error:

$ sudo insmod ./simp.ko irq=9 devname=mydev debug=0 simpcb=200 insmod: ERROR: could not insert module ./simp.ko: Invalid parameters # echo "70">/sys/module/simp/parameters/simpcb bash: echo: write error: Invalid argument 1 2 3 4 $ sudo insmod . / simp . ko irq = 9 devname = mydev debug = 0 simpcb = 200 insmod : ERROR : could not insert module . / simp . ko : Invalid parameters # echo "70">/sys/module/simp/parameters/simpcb bash : echo : write error : Invalid argument

We can also define a custom function for get but in this example its not necessary. It will be useful if you want the parameter value provided using a string but saved as a number

For example if we want a parameter for interrupt mode to be one from level, edge, polling but we want to save it as a number (1 – level, 2-edge, 3-polling) we will define the set function to convert from string to int and the get function to convert from int to string

For Example:

enum irq_type { IRQ_TYPE_LEVEL, IRQ_TYPE_EDGE, IRQ_TYPE_POLLING }; static int irq_type = IRQ_TYPE_LEVEL; // default static int irqtype_op_write_handler(const char *val, const struct kernel_param *kp) { char valcp[16]; char *s; strncpy(valcp, val, 16); valcp[15] = '\0'; s = strstrip(valcp); if (strcmp(s, "level") == 0) irq_type = IRQ_TYPE_LEVEL; else if (strcmp(s, "edge") == 0) irq_type = IRQ_TYPE_EDGE; else if (strcmp(s, "polling") == 0) irq_type = IRQ_TYPE_POLLING; else return -EINVAL; return 0; } static int irqtype_op_read_handler(char *buffer, const struct kernel_param *kp) { switch (irq_type) { case IRQ_TYPE_LEVEL: strcpy(buffer, "Level"); break; case IRQ_TYPE_EDGE: strcpy(buffer, "Edge"); break; case IRQ_TYPE_POLLING: strcpy(buffer, "Polling"); break; default: strcpy(buffer, "error"); break; } return strlen(buffer); } static const struct kernel_param_ops irqtype_op_ops = { .set = irqtype_op_write_handler, .get = irqtype_op_read_handler }; module_param_cb(irqtype, &irqtype_op_ops, NULL, 0660); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 enum irq_type { IRQ_TYPE_LEVEL , IRQ_TYPE_EDGE , IRQ_TYPE _ POLLING } ; static int irq_type = IRQ_TYPE_LEVEL ; // default static int irqtype_op_write_handler ( const char * val , const struct kernel_param * kp ) { char valcp [ 16 ] ; char * s ; strncpy ( valcp , val , 16 ) ; valcp [ 15 ] = '\0' ; s = strstrip ( valcp ) ; if ( strcmp ( s , "level" ) == 0 ) irq_type = IRQ_TYPE_LEVEL ; else if ( strcmp ( s , "edge" ) == 0 ) irq_type = IRQ_TYPE_EDGE ; else if ( strcmp ( s , "polling" ) == 0 ) irq_type = IRQ_TYPE_POLLING ; else return - EINVAL ; return 0 ; } static int irqtype_op_read_handler ( char * buffer , const struct kernel_param * kp ) { switch ( irq_type ) { case IRQ_TYPE_LEVEL : strcpy ( buffer , "Level" ) ; break ; case IRQ_TYPE_EDGE : strcpy ( buffer , "Edge" ) ; break ; case IRQ_TYPE_POLLING : strcpy ( buffer , "Polling" ) ; break ; default : strcpy ( buffer , "error" ) ; break ; } return strlen ( buffer ) ; } static const struct kernel_param_ops irqtype_op_ops = { . set = irqtype_op_write_handler , . get = irqtype_op_read _ handler } ; module_param_cb ( irqtype , & irqtype_op_ops , NULL , 0660 ) ;

To load the kernel module or change the parameter value, use a string:

# sudo insmod ./simp.ko irq=9 devname=mydev debug=0 simpcb=20 irqtype=edge # cat /sys/module/simp/parameters/irqtype Edge 1 2 3 # sudo insmod ./simp.ko irq=9 devname=mydev debug=0 simpcb=20 irqtype=edge # cat /sys/module/simp/parameters/irqtype Edge

Declare a module parameter array

You can declare a parameter as array of numbers or string using module_param_array. For example:

static int addr[SIZE]; static int count; module_param_array(addr, int, &count, 0660); 1 2 3 static int addr [ SIZE ] ; static int count ; module_param_array ( addr , int , & count , 0660 ) ;

While loading the module, use comma separated list to provide the values:

# insmod ./simp.ko addr=0x1000,0x2000,0x3000 1 # insmod ./simp.ko addr=0x1000,0x2000,0x3000

count is an output parameter and updated with the number of element that provided by the user (in this example 3)

Some notes about module parameters:

If you load a module using modprobe, you can set the parameters in /etc/modprobe.conf (or other file – depends on your distribution)

If you compile the module with the kernel (static), you can provide the parameters using the kernel command line on boot time

There are more functions – look at the source code (moduleparam.h)

You can find the full source code here