[优化]优化说明文档
This commit is contained in:
parent
ffe2d62bef
commit
b0c7a463f7
35
README.md
35
README.md
|
@ -1,28 +1,27 @@
|
||||||
本项目是[lkmpg](https://github.com/sysprog21/lkmpg)项目的翻译副本
|
|
||||||
|
|
||||||
# Linux 内核模块编程指南
|
# Linux 内核模块编程指南
|
||||||
|
|
||||||
该项目是不断更新《Linux 内核模块编程指南》的中文译本, 但需要注意的是此项目和原版在使用的中央处理器架构上有所不同, 原版是使用的主流X86架构, 本版本会将部分涉及到架构相关的内容修改为龙架构,目前本译本已支持到 6.9.x 版本的内核, 相关[示例](示例/)。
|
本项目可以看作为[lkmpg](https://github.com/sysprog21/lkmpg)项目的中文译本, 但并添加了部分译者注, 需要注意的是此项目和原版在使用的中央处理器架构上有所不同, 原版是使用的主流X86架构, 本版本会将部分涉及到架构相关的内容修改为龙架构,目前本译本已支持到 6.9.x 版本的内核, 相关[示例](示例/)。
|
||||||
|
|
||||||
本项目原版是使用TeXLie格式编写, 在译者对此格式较为陌生, 已改为更为主流的 `markdown` 格式重写, 并且在翻译途中为了方便读者理解一些不常见的英文缩写, 添加了部分注释
|
原作者与本书的一些介绍和致谢说明请看[引言](./引言.md)
|
||||||
|
|
||||||
|
本书是由寻觅(樊旭东)翻译为中文
|
||||||
|
|
||||||
|
本项目原版是使用TeXLie格式编写, 在译者对此格式较为陌生, 已改为更为主流的 `markdown` 格式重写
|
||||||
|
|
||||||
## 开始使用
|
## 开始使用
|
||||||
|
|
||||||
在线文档: http://223.76.216.188:50201/books/linux/chapter/6c4b7
|
在线文档: http://223.76.216.188:50201/books/linux/chapter/6c4b7
|
||||||
|
|
||||||
> 目录:
|
> 目录:
|
||||||
> * [0 准备工作](文档/0-准备工作)
|
> * [0 准备工作](文档/00-准备工作.md)
|
||||||
> * [1 编写驱动](文档/1-编写驱动)
|
> * [1 编写驱动](文档/01-编写驱动.md)
|
||||||
> * [2 驱动相关文件](文档/2-驱动相关文件)
|
> * [2 驱动相关文件](文档/02-驱动相关文件.md)
|
||||||
> * [3 ioctl](文档/3-ioctl)
|
> * [3 ioctl](文档/03-ioctl.md)
|
||||||
> * [4 系统调用](文档/4-系统调用)
|
> * [4 系统调用](文档/04-系统调用.md)
|
||||||
> * [5 阻塞进程和线程](文档/5-阻塞进程和线程)
|
> * [5 阻塞进程和线程](文档/05-阻塞进程和线程.md)
|
||||||
> * [6 内核模块的锁](文档/6-内核模块的锁)
|
> * [6 内核模块的锁](文档/06-内核模块的锁.md)
|
||||||
> * [7 驱动与用户交互](文档/7-驱动与用户交互)
|
> * [7 驱动与用户交互](文档/07-驱动与用户交互.md)
|
||||||
> * [8 调度与中断](文档/8-调度与中断)
|
> * [8 调度与中断](文档/08-调度与中断.md)
|
||||||
> * [](文档/)
|
> * [9 设备驱动](文档/09-设备驱动.md)
|
||||||
|
> * [10 优化与常见问题](文档/10-优化与常见问题.md)
|
||||||
## 版权说明
|
|
||||||
|
|
||||||
本项目翻译的原项目是基于GPL-2协议开源, 为了遵守协议约束, 本项目有且只有继续使用GPL-2协议开源
|
|
||||||
|
|
||||||
|
|
29
引言.md
Normal file
29
引言.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# 引言
|
||||||
|
|
||||||
|
《Linux内核模块编程指南》是一本免费的书;你可以根据 [开放软件许可证(Open Software License)](https://opensource.org/licenses/OSL-3.0) 3.0版的条款复制和/或修改它。
|
||||||
|
|
||||||
|
本书的发行是希望它会有用, 但没有任何担保, 甚至没有对适销性或特定用途的适用性的暗示担保。
|
||||||
|
|
||||||
|
作者鼓励广泛分发本书供个人或商业使用, 前提是上述版权声明保持不变, 并且该方法符合[开放软件许可证(Open Software License)](https://opensource.org/licenses/OSL-3.0)
|
||||||
|
的规定。总之, 你可以免费复制和分发本书, 也可以赢利。以任何媒介、物理或电子形式复制本书, 无需获得作者的明确许可。
|
||||||
|
|
||||||
|
本文档的衍生作品和翻译必须置于开放软件许可证下, 并且原始版权声明必须保持不变。如果你为本书贡献了新材料, 则必须为你的修订提供材料和源代码。请直接向文档维护者Jim Huang提供修订和更新<jserv@ccns.ncku.edu.tw>。
|
||||||
|
这将允许合并更新, 并为Linux社区提供一致的修订。
|
||||||
|
|
||||||
|
如果你以商业方式出版或分发本书, 作者和[Linux Documentation Project](https://tldp.org/) (LDP)将非常感谢你的捐赠、版税和/或印刷本。以这种方式贡献表明你支持自由软件和LDP。如果你有任何问题或意见, 请联系上述地址。
|
||||||
|
|
||||||
|
# 原作者介绍
|
||||||
|
|
||||||
|
《Linux内核模块编程指南》最初由Ori Pomerantz for Linux v2.2编写。随着Linux内核的发展, Ori维护文档的可用性降低了。因此, Peter Jay Salzman承担了维护者的角色, 并更新了Linuxv2.4指南。在跟踪Linux v2.6中的开发时, Peter也受到了类似的限制, 导致Michael Burian加入为共同维护者, 以使该指南与Linux v2.6同步。Bob Mottram通过更新Linux v3.8和更高版本的示例对该指南做出了贡献。Jim Huang随后承担了更新最新Linux版本(v5.0及更高版本)指南的任务, 并修订LaTeX文档。
|
||||||
|
|
||||||
|
# 致谢
|
||||||
|
|
||||||
|
以下人员为本书提供了一些更正与良好的建议:
|
||||||
|
|
||||||
|
Amit Dhingra, Andy Shevchenko, Arush Sharma, Benno Bielmeier, Bob Lee, Brad Baker, Che-Chia Chang, Cheng-Shian Yeh, Chih-En Lin, Chih-Hsuan
|
||||||
|
Yang, Chih-Yu Chen, Ching-Hua (Vivian) Lin, Chin Yik Ming, cvvletter, Cyril Brulebois, Daniele Paolo Scarpazza, David Porter, demonsome, Dimo
|
||||||
|
Velev, Ekang Monyet, Ethan Chan, Francois Audeon, Gilad Reti, heartofrain, Horst Schirmeier, Hsin-Hsiang Peng, Ignacio Martin, I-Hsin
|
||||||
|
Cheng, Iûnn Kiàn-îng, Jian-Xing Wu, Johan Calle, keytouch, Kohei Otsuka, Kuan-Wei Chiu, manbing, Marconi Jiang, mengxinayan, Meng-Zong Tsai,
|
||||||
|
Peter Lin, Roman Lakeev, Sam Erickson, Shao-Tse Hung, Shih-Sheng Yang, Stacy Prowell, Steven Lung, Tristan Lelong, Tse-Wei Lin, Tucker Polomik,
|
||||||
|
Tyler Fanelli, VxTeemo, Wei-Hsin Yeh, Wei-Lun Tsai, Xatierlike Lee, Yen-Yu Chen, Yin-Chiuan Chen, Yi-Wei Lin, Yo-Jung Lin, Yu-Hsiang Tseng, YYGO.
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
obj-m += example_tasklet.o
|
obj-m += example_tasklet.o
|
||||||
obj-m += sched.o
|
obj-m += sched.o
|
||||||
obj-m += intrpt.o
|
obj-m += intrpt.o
|
||||||
obj-m += intrpt_old.o
|
obj-m += bottomhalf.o
|
||||||
|
obj-m += bh_thread.c
|
||||||
|
|
||||||
|
|
||||||
PWD := $(CURDIR)
|
PWD := $(CURDIR)
|
||||||
|
|
151
示例/8-调度与中断/bh_thread.c
Normal file
151
示例/8-调度与中断/bh_thread.c
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* bh_thread.c - 起始部分和后续部分中断处理
|
||||||
|
*
|
||||||
|
* 基于 Stefan Wendler(devnull@kaltpost.de)的 RPi 示例
|
||||||
|
* 来源于:
|
||||||
|
* https://github.com/wendlers/rpi-kmod-samples
|
||||||
|
*
|
||||||
|
* 按下一个按钮打开 LED,另一个按钮关闭 LED
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
|
||||||
|
static int button_irqs[] = { -1, -1 };
|
||||||
|
|
||||||
|
/* 定义 LED 的 GPIO。
|
||||||
|
* FIXME: 根据你的板子的 GPIO 号码修改这些数字。
|
||||||
|
*/
|
||||||
|
static struct gpio leds[] = { { 1, GPIOF_OUT_INIT_LOW, "LED 1" } };
|
||||||
|
|
||||||
|
/* 定义按钮的 GPIO
|
||||||
|
* FIXME: 根据你的板子的 GPIO 号码修改这些数字。
|
||||||
|
*/
|
||||||
|
static struct gpio buttons[] = {
|
||||||
|
{ 2, GPIOF_IN, "LED 1 开按钮" },
|
||||||
|
{ 3, GPIOF_IN, "LED 1 关按钮" },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 当 IRQ 触发时立即发生 */
|
||||||
|
static irqreturn_t button_top_half(int irq, void *ident)
|
||||||
|
{
|
||||||
|
return IRQ_WAKE_THREAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 可以在空闲时处理,释放 IRQ 给其他高优先级任务 */
|
||||||
|
static irqreturn_t button_bottom_half(int irq, void *ident)
|
||||||
|
{
|
||||||
|
pr_info("后续部分任务开始\n");
|
||||||
|
mdelay(500); /* 执行一些耗时的操作 */
|
||||||
|
pr_info("后续部分任务结束\n");
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init bottomhalf_init(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pr_info("%s\n", __func__);
|
||||||
|
|
||||||
|
/* 注册 LED GPIO */
|
||||||
|
ret = gpio_request_array(leds, ARRAY_SIZE(leds));
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
pr_err("无法请求 LED 的 GPIOs: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 注册按钮 GPIO */
|
||||||
|
ret = gpio_request_array(buttons, ARRAY_SIZE(buttons));
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
pr_err("无法请求 BUTTON 的 GPIOs: %d\n", ret);
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("当前 button1 的值: %d\n", gpio_get_value(buttons[0].gpio));
|
||||||
|
|
||||||
|
ret = gpio_to_irq(buttons[0].gpio);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("无法请求 IRQ: %d\n", ret);
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
button_irqs[0] = ret;
|
||||||
|
|
||||||
|
pr_info("成功请求 BUTTON1 IRQ # %d\n", button_irqs[0]);
|
||||||
|
|
||||||
|
ret = request_threaded_irq(button_irqs[0], button_top_half,
|
||||||
|
button_bottom_half,
|
||||||
|
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||||
|
"gpiomod#button1", &buttons[0]);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
pr_err("无法请求 IRQ: %d\n", ret);
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpio_to_irq(buttons[1].gpio);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("无法请求 IRQ: %d\n", ret);
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
button_irqs[1] = ret;
|
||||||
|
|
||||||
|
pr_info("成功请求 BUTTON2 IRQ # %d\n", button_irqs[1]);
|
||||||
|
|
||||||
|
ret = request_threaded_irq(button_irqs[1], button_top_half,
|
||||||
|
button_bottom_half,
|
||||||
|
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||||
|
"gpiomod#button2", &buttons[1]);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
pr_err("无法请求 IRQ: %d\n", ret);
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* 清理已设置的部分 */
|
||||||
|
fail3:
|
||||||
|
free_irq(button_irqs[0], NULL);
|
||||||
|
|
||||||
|
fail2:
|
||||||
|
gpio_free_array(buttons, ARRAY_SIZE(leds));
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
gpio_free_array(leds, ARRAY_SIZE(leds));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit bottomhalf_exit(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pr_info("%s\n", __func__);
|
||||||
|
|
||||||
|
/* 释放 IRQ */
|
||||||
|
free_irq(button_irqs[0], NULL);
|
||||||
|
free_irq(button_irqs[1], NULL);
|
||||||
|
|
||||||
|
/* 关闭所有 LED */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(leds); i++)
|
||||||
|
gpio_set_value(leds[i].gpio, 0);
|
||||||
|
|
||||||
|
/* 注销 */
|
||||||
|
gpio_free_array(leds, ARRAY_SIZE(leds));
|
||||||
|
gpio_free_array(buttons, ARRAY_SIZE(buttons));
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(bottomhalf_init);
|
||||||
|
module_exit(bottomhalf_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("起始部分和后续部分中断处理");
|
171
示例/8-调度与中断/bottomhalf.c
Normal file
171
示例/8-调度与中断/bottomhalf.c
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
* bottomhalf.c - 起始与后续部分中断处理
|
||||||
|
*
|
||||||
|
* 基于 Stefan Wendler (devnull@kaltpost.de) 提供的 RPi 示例
|
||||||
|
* 来自:
|
||||||
|
* https://github.com/wendlers/rpi-kmod-samples
|
||||||
|
*
|
||||||
|
* 按下一个按钮以点亮 LED,按下另一个按钮以熄灭它
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
|
||||||
|
/* 宏 DECLARE_TASKLET_OLD 的存在是为了确保兼容
|
||||||
|
* 详细信息: https://lwn.net/Articles/830964/
|
||||||
|
*/
|
||||||
|
#ifndef DECLARE_TASKLET_OLD
|
||||||
|
#define DECLARE_TASKLET_OLD(arg1, arg2) DECLARE_TASKLET(arg1, arg2, 0L)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int button_irqs[] = { -1, -1 };
|
||||||
|
|
||||||
|
/* 定义 LED 的 GPIO。
|
||||||
|
* TODO: 根据你的板子更改 GPIO 编号。
|
||||||
|
*/
|
||||||
|
static struct gpio leds[] = { { 1, GPIOF_OUT_INIT_LOW, "LED 1" } };
|
||||||
|
|
||||||
|
/* 定义按钮的 GPIO。
|
||||||
|
* TODO: 根据你的板子更改 GPIO 编号。
|
||||||
|
*/
|
||||||
|
static struct gpio buttons[] = { { 2, GPIOF_IN, "LED 1 打开按钮" },
|
||||||
|
{ 3, GPIOF_IN, "LED 1 关闭按钮" } };
|
||||||
|
|
||||||
|
/* 包含一些复杂处理的任务队列函数 */
|
||||||
|
static void bottomhalf_tasklet_fn(unsigned long data)
|
||||||
|
{
|
||||||
|
pr_info("底半部任务队列开始\n");
|
||||||
|
/* 执行一些耗时的操作 */
|
||||||
|
mdelay(500);
|
||||||
|
pr_info("底半部任务队列结束\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static DECLARE_TASKLET_OLD(buttontask, bottomhalf_tasklet_fn);
|
||||||
|
|
||||||
|
/* 按钮按下时触发的中断函数 */
|
||||||
|
static irqreturn_t button_isr(int irq, void *data)
|
||||||
|
{
|
||||||
|
/* 立即执行一些操作 */
|
||||||
|
if (irq == button_irqs[0] && !gpio_get_value(leds[0].gpio))
|
||||||
|
gpio_set_value(leds[0].gpio, 1); // 点亮 LED
|
||||||
|
else if (irq == button_irqs[1] && gpio_get_value(leds[0].gpio))
|
||||||
|
gpio_set_value(leds[0].gpio, 0); // 熄灭 LED
|
||||||
|
|
||||||
|
/* 将剩余工作交给调度程序处理 */
|
||||||
|
tasklet_schedule(&buttontask);
|
||||||
|
|
||||||
|
return IRQ_HANDLED; // 表示中断已经处理完毕
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init bottomhalf_init(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pr_info("%s\n", __func__);
|
||||||
|
|
||||||
|
/* 请求 LED 的 GPIO */
|
||||||
|
ret = gpio_request_array(leds, ARRAY_SIZE(leds));
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
pr_err("无法请求 LED 的 GPIOs: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 请求按钮的 GPIO */
|
||||||
|
ret = gpio_request_array(buttons, ARRAY_SIZE(buttons));
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
pr_err("无法请求按钮的 GPIOs: %d\n", ret);
|
||||||
|
goto fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("当前按钮1的值: %d\n", gpio_get_value(buttons[0].gpio));
|
||||||
|
|
||||||
|
/* 获取按钮 1 的中断号 */
|
||||||
|
ret = gpio_to_irq(buttons[0].gpio);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("无法请求中断: %d\n", ret);
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
button_irqs[0] = ret;
|
||||||
|
|
||||||
|
pr_info("成功请求了按钮1的中断号 # %d\n", button_irqs[0]);
|
||||||
|
|
||||||
|
/* 注册按钮 1 的中断 */
|
||||||
|
ret = request_irq(button_irqs[0], button_isr,
|
||||||
|
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||||
|
"gpio模块#按钮1", NULL);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
pr_err("无法请求中断: %d\n", ret);
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 获取按钮 2 的中断号 */
|
||||||
|
ret = gpio_to_irq(buttons[1].gpio);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("无法请求中断: %d\n", ret);
|
||||||
|
goto fail2;
|
||||||
|
}
|
||||||
|
|
||||||
|
button_irqs[1] = ret;
|
||||||
|
|
||||||
|
pr_info("成功请求了按钮2的中断号 # %d\n", button_irqs[1]);
|
||||||
|
|
||||||
|
/* 注册按钮 2 的中断 */
|
||||||
|
ret = request_irq(button_irqs[1], button_isr,
|
||||||
|
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||||
|
"gpio模块#按钮2", NULL);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
pr_err("无法请求中断: %d\n", ret);
|
||||||
|
goto fail3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* 清理已经设置的资源 */
|
||||||
|
fail3:
|
||||||
|
free_irq(button_irqs[0], NULL);
|
||||||
|
|
||||||
|
fail2:
|
||||||
|
gpio_free_array(buttons, ARRAY_SIZE(buttons));
|
||||||
|
|
||||||
|
fail1:
|
||||||
|
gpio_free_array(leds, ARRAY_SIZE(leds));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit bottomhalf_exit(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pr_info("%s\n", __func__);
|
||||||
|
|
||||||
|
/* 释放中断 */
|
||||||
|
free_irq(button_irqs[0], NULL);
|
||||||
|
free_irq(button_irqs[1], NULL);
|
||||||
|
|
||||||
|
/* 关闭所有 LED */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(leds); i++)
|
||||||
|
gpio_set_value(leds[i].gpio, 0);
|
||||||
|
|
||||||
|
/* 注销 GPIO */
|
||||||
|
gpio_free_array(leds, ARRAY_SIZE(leds));
|
||||||
|
gpio_free_array(buttons, ARRAY_SIZE(buttons));
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(bottomhalf_init);
|
||||||
|
module_exit(bottomhalf_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("起始与后续部分中断处理");
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kernel.h> /* 用于 ARRAY_SIZE() */
|
#include <linux/kernel.h> /* 引入 ARRAY_SIZE() */
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/printk.h>
|
#include <linux/printk.h>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user