Communicating with Linux Device Drivers
The first driver we write is like a silent employee, only working according to a fixed process. However, in reality, we often need to tell this employee specific information, such as: “Use the red LED today” or “Set the motor speed to 1000 RPM”.
This article teaches you how to “communicate” (pass parameters) to the driver when loading it, allowing it to work flexibly according to our instructions.
Three Ways to “Communicate”
The Linux kernel provides three main methods to pass parameters to driver modules:
-
<span>module_param()</span>– Pass a single value (number or string) -
<span>module_param_array()</span>– Pass a set of values (array) -
<span>module_param_cb()</span>– Pass values and receive notifications when the value changes
Detailed Explanation of Core Tools
1. <span>module_param()</span> – 【Pass a Single Parameter】
This macro is used to pass a single value, such as a number or a string.
module_param(variable_name, type, file_permissions);
Example:
int my_value;char *my_name;module_param(my_value, int, 0644); // Pass an integermodule_param(my_name, charp, 0644); // Pass a string
File Permissions: This determines who can view or modify this parameter through the <span>/sys</span> filesystem after the driver is loaded.<span>0644</span> means the owner can read and write, while others can only read.
2. <span>module_param_array()</span> – 【Pass a Set of Parameters】
When multiple values of the same type need to be passed (for example, configuring the states of multiple LEDs), this macro can be used.
module_param_array(array_name, element_type, element_count_pointer, file_permissions);
Example:
int led_states[4];module_param_array(led_states, int, NULL, 0644); // Pass 4 integers
3. <span>module_param_cb()</span> – 【Parameters with “Notification Feature”】
This is the most powerful method! It not only passes parameters but also automatically calls a callback function to notify you when the parameter value is modified.
Why is this needed? Imagine: your driver controls a motor. When someone changes the speed from 1000 to 2000, you not only need to know that the value has changed, but also immediately write this new speed to the hardware register. Ordinary parameters cannot provide this “instant notification”.
// 1. First define the callback functionint speed_changed(const char *val, const struct kernel_param *kp){ int res = param_set_int(val, kp); // Update the variable value if(res == 0) { printk(KERN_INFO "Speed has changed! New value=%d\n", motor_speed); // Write the new speed to the hardware register here return 0; } return -1;}// 2. Define the operation structureconst struct kernel_param_ops my_ops = { .set = &speed_changed, // Call our function when setting the value .get = ¶m_get_int, // Use the standard function when reading the value};// 3. Register the parameter with callbackmodule_param_cb(motor_speed, &my_ops, &motor_speed, 0644);
Complete Parameter Passing Example
#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/moduleparam.h>// Define variables to receive parametersint valueETX; // Ordinary integerchar *nameETX; // Stringint arr_valueETX[4]; // Integer arrayint cb_valueETX = 0; // Integer with callback// 1. Register ordinary parametersmodule_param(valueETX, int, 0644);module_param(nameETX, charp, 0644);module_param_array(arr_valueETX, int, NULL, 0644);// 2. Callback function - called when cb_valueETX is modifiedint notify_param(const char *val, const struct kernel_param *kp){ int res = param_set_int(val, kp); // Update variable value if(res == 0) { printk(KERN_INFO "Callback function has been called!\n"); printk(KERN_INFO "New value of cb_valueETX = %d\n", cb_valueETX); return 0; } return -1;}// 3. Define callback operationsconst struct kernel_param_ops my_param_ops = { .set = ¬ify_param, // Call our function when setting .get = ¶m_get_int, // Use the standard function when reading};// 4. Register the parameter with callbackmodule_param_cb(cb_valueETX, &my_param_ops, &cb_valueETX, 0644);// Driver initialization functionstatic 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 < 4; i++) { printk(KERN_INFO "arr_valueETX[%d] = %d\n", i, arr_valueETX[i]); } printk(KERN_INFO "Kernel module loaded successfully!\n"); return 0;}// Driver exit functionstatic void __exit hello_world_exit(void){ printk(KERN_INFO "Kernel module unloaded successfully!\n");}module_init(hello_world_init);module_exit(hello_world_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Embedded Station");MODULE_DESCRIPTION("Parameter Passing Example Driver");MODULE_VERSION("1.0");
How to Use This Driver?
Compile the Driver
Use the previous Makefile and run <span>make</span> to generate the <span>.ko</span> file.
Load the Driver and Pass Parameters
sudo insmod hello_world_module.ko valueETX=14 nameETX="Embedded Station" arr_valueETX=100,102,104,106
Run Results (View via <span>dmesg</span>):
valueETX = 14cb_valueETX = 0 nameETX = Embedded Stationarr_valueETX[0] = 100arr_valueETX[1] = 102arr_valueETX[2] = 104 arr_valueETX[3] = 106Kernel module loaded successfully!
Test the Callback Functionality
# Method 1: Directly use echo sudo suecho 13 > /sys/module/hello_world_module/parameters/cb_valueETX# Method 2: Use sudo shsudo sh -c "echo 13 > /sys/module/hello_world_module/parameters/cb_valueETX"
Run Results (View via <span>dmesg</span>):
Callback function has been called!cb_valueETX's new value = 13
Summary
Through this example, we have mastered:
-
Three Parameter Passing Methods:
-
<span>module_param()</span>: Pass a single value -
<span>module_param_array()</span>: Pass an array -
<span>module_param_cb()</span>: Pass a value and receive change notifications -
Practical Application Scenarios:
-
Ordinary parameters: Used for initial configuration
-
Callback parameters: When a parameter changes and immediate action is needed (e.g., writing to hardware registers)
-
Operational Tips:
-
Pass parameters when loading:
<span>insmod module_name param1=value1 param2=value2</span> -
Modify parameters at runtime: Through the
<span>/sys/module/module_name/parameters/</span>directory
This is akin to:
-
Ordinary parameters are like giving an employee a task list (informed all at once during loading)
-
Callback parameters are like giving an employee a walkie-talkie (can issue new commands at any time and receive confirmation)
With the ability to pass parameters, our driver transforms from a “mute” to a “smart assistant” that can listen and respond!