Linux内核模块中的LED闪烁

大家好 在本文中,我想分享创建简单Linux内核模块的经验。 对于那些想了解如何编写内核模块但又不知道从哪里开始的人来说,本文将是有用的。

我一直想了解这个主题,但是直到最近我才知道该如何处理它。 我希望该模块足够简单,但要比消息“ Hello world!”更复杂。 在日志文件中输出。 最后,我决定尝试使LED闪烁。 另一个目标是推导引起sysfs闪烁频率的参数。

在实验中,我使用了Orange Pi One开发板和Ubuntu Linux(内核版本3.4.113)。 为了构建内核模块,您需要gcc编译器,make实用程序和内核头文件。 要安装头文件,请运行以下命令:

sudo apt-get install linux-headers-$(uname -r) 

接下来,我将分析模块中最有趣的部分。 为了节省空间,我不会在这里给出所有代码,它与make文件一起在github上可用。

在模块中,我使用了头文件:

 #include <linux/kernel.h> #include <linux/module.h> #include <linux/gpio.h> #include <linux/hrtimer.h> #include <linux/moduleparam.h> 

编写内核模块时,应始终启用kernel.h和module.h,gpio.h实际上负责使用GPIO,hrtimer.h(高分辨率计时器)是计时器的头文件,需要moduleparam.h来显示sysfs中的参数。

为了不使它们的变量和功能在系统内核中发光,它们都应描述为静态的。 以防万一,我注意到内核是用C和静态语言编写的,这与C ++不同,这意味着该对象仅在可执行文件内部可用。

入口点是:

 static int blink_module_init(void) 

在这里,我将初始化以后将使用的变量,包括:

 gpio_timer_interval = ktime_set(gpio_blink_interval_s, 0); 

ktime_set通过为其提供所需的秒数(gpio_blink_interval_s)和纳秒(0)来初始化ktime_t数据类型。 将来,计时器将使用此变量。

以下是使用GPIO的请求:

 err = gpio_request(BLINK_PIN_NR, "blink_led"); 

如果成功,此函数将返回0,因此以后我会检查它是否返回。 接下来,必须将选定的引脚设置为信号输出并指定默认值。

 err = gpio_direction_output(BLINK_PIN_NR, GPIOF_INIT_LOW); 

如果没有错误,则初始化并启动计时器

 hrtimer_init(&gpio_blink_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); gpio_blink_timer.function = &gpio_blink_timer_callback; hrtimer_start(&gpio_blink_timer, gpio_timer_interval, HRTIMER_MODE_REL); 

计时器回调函数将为gpio_blink_timer_callback。 在此功能中,我将引脚值更改为相反的值

 gpio_value ^= 0x01; gpio_set_value(BLINK_PIN_NR, gpio_value); 

我问定时器什么时候下一次工作

 hrtimer_forward_now(&gpio_blink_timer, gpio_timer_interval); 

并返回HRTIMER_RESTART。

现在,让我们看一下如何在sysfs中显示某种变量。 为此,我使用一个宏

 module_param_cb(gpio_blink_interval_s, &kp_ops, &gpio_blink_interval_s, 0660); 

该宏的第一个参数是sysfs中的文件名。 第二个是包含回调函数的数据结构。 第三个参数是指向实变量的指针,并且是sysfs中的第四个文件权限。

当用户更改sysfs文件的值或读取其值时,将调用kp_ops中的函数。 这是我初始化它们的方式:

 static const struct kernel_param_ops kp_ops = { .set = &set_blink_interval, .get = &get_blink_interval }; 

在这种情况下,由于set会设置一个新值gpio_timer_interval,因此很有趣。

 gpio_timer_interval = ktime_set(gpio_blink_interval_s, 0); 

在出口处,我清除所有使用的资源

 static void blink_module_exit(void) { hrtimer_cancel(&gpio_blink_timer); gpio_set_value(BLINK_PIN_NR, 0); gpio_free(BLINK_PIN_NR); printk(KERN_ALERT "Blink module unloaded\n"); } 

入口和出口点必须在相应的宏中指示

 module_init(blink_module_init); module_exit(blink_module_exit); 

它似乎描述了所有要点。 如果读者有任何疑问,我将很乐意在评论中回答。

Source: https://habr.com/ru/post/zh-CN411721/


All Articles