【Linux】驱动的基本架构和编译
驱动源码
/** Silicon Integrated Co., Ltd haptic sih688x haptic driver file** Copyright (c) 2021 kugua <daokuan.zhu@si-in.com>** This program is free software; you can redistribute it and/or modify it* under the terms of the GNU General Public License version 2 as published by* the Free Software Foundation*/#include <linux/init.h> //包含宏定义的头文件
#include <linux/module.h> //包含初始化加载模块的头文件static int __init haptics_init(void)
{//内核层只能使用printk,不能使用printfprintk(KERN_EMERG "[ KERN_EMERG ] Haptics Init\n"); //输出等级为0printk("[ default ] Haptics Init\n");return 0;
}static void __exit haptics_exit(void)
{printk(KERN_EMERG "[ KERN_EMERG ] Haptics Exit\n"); //输出等级为0printk("[ default ] Haptics Exit\n");
}module_init(haptics_init);//驱动入口
module_exit(haptics_exit);//驱动出口MODULE_AUTHOR("<daokuan.zhug@si-in.com>");//声明作者信息
MODULE_DESCRIPTION("Haptic Driver V1.0.0"); //对这个模块作一个简单的描述
MODULE_LICENSE("GPL v2");//声明开源许可证// "GPL" 是指明 这是GNU General Public License的任意版本// “GPL v2” 是指明 这仅声明为GPL的第二版本
- linux驱动基本框架就是包含入口函数和出口函数,加载驱动时会运行入口函数,卸载驱动时会运行出口函数。
- 入口函数的作用是加载驱动时做一些初始化工作,比如注册设备、申请设备号、生成设备节点等,上文代码中这些都还没有实现。
- 出口函数的作用是卸载驱动时做一些善后操作,比如注销设备、注销设备号、注销类等。
- 在内核态下的打印函数是“printk”函数。KERN_EMERG是打印优先级,这里采用了最高优先级。
一般情况下,驱动源码会放在kernel/drivers
,里面会有很多文件夹存放不同的驱动代码,我们自己的驱动需要新建一个目录。
Kconfig文件
这个文件时用来对内核进行配置的,当执行make arch=ARM64 menuconfig
指令的时候,这个文件就被解析。该文件和源码文件在同一目录下,文件内容可如下:
menu "haptics driver"config HAPTICS
tristate "haptics driver"
helpjust a simplest driver.
default yendmenu
#endmenu后一定要加空行
- 第一行内容表示菜单名
- 第二行内容
config HAPTICS
,在执行配置的时候,将会生成一个变量CONFIG_HAPTICS
,而这个变量,将会在编译的时候,被Makefile
引用。 - 第三行内容
tristate
声明选项的类型,"haptics driver"
声明选项的名称 - 第四行内容表示帮助信息
- 第五行的
default y
,就表示把CONFIG_HAPTICS
的值设置成y
,从而让这个驱动被编译到内核中。 - 第六行是结束菜单
- 第七行是需要加的空行
其中第三行内容的选项支持tristate
和bool
。
tristate
支持三个选项:
- < >不编译
- <*>编译到内核
- 编译成模块
bool
支持两个选项:
- < >不编译
- <*>编译到内核
这个在菜单配置时我们会碰到。
现在,haptics
驱动中的KConfig
配置文件已经准备好了,但是还需要这个配置文件登记到 Linux
内核的整体配置文件中。也就是把它登记到kernel/drivers/Kconfig
文件的末尾:
source "drivers/haptics/Kconfig"endmenu // 加在这一句的上面
然后在kernel
目录下执行make ARCH=arm64 menuconfig
命令,
我们可以看到haptics driver
前面显示的是星号*,这表示:该驱动将会编译进内核。我们可以按下空格键切换标记。M标记意思是编译成驱动模块。
我们暂时先选择星号编译进内核,其他的选项后续再介绍。最后选择save即可。
有一点需要注意的,如果Kconfig
是在Windows系统上编辑的然后放在开发服务器中,我们会发现报警了
zhudk@vm1:/expand/zhudk/rk3588_9tripod/x3588_linux_20220620/kernel$ make ARCH=arm64 menuconfig
'rivers/haptics/Kconfig:1:warning: ignoring unsupported character '
'rivers/haptics/Kconfig:2:warning: ignoring unsupported character '
'rivers/haptics/Kconfig:3:warning: ignoring unsupported character '
'rivers/haptics/Kconfig:5:warning: ignoring unsupported character '
'rivers/haptics/Kconfig:6:warning: ignoring unsupported character '
这是因为在Windows和Linux系统中,存在换行符的差异。这个小问题可以自行解决。
Makefile文件
Makefile文件是make工具的脚本,也是和源码放在同一目录下,其内容只有一行。
obj-$(CONFIG_HAPTICS) += haptics.o
CONFIG_HAPTICS
可以看做一个变量,在编译的时候,这个变量的值可能是:y, n 或者 m。- 在刚才的
Kconfig
参数配置中,CONFIG_HAPTICS
被设置为 y,于是这句话就被翻译成:obj-y += haptics
,表示把 haptics驱动编译进内核。
现在,haptics
驱动程序的Makefile
已经创建好了,我们还要让linux
内核的编译框架知道这个文件才行。在文件kernel/drivers/Makefile
中的末尾,添加如下内容:
obj-$(CONFIG_HAPTICS) += haptics/
编译进内核
现在,整个准备工作已经做好了,只需编译即可。因为本篇教程是基于ARM开发板的,所以使用开发板中SDK的编译脚本进行内核编译。
./build.sh kernel
然后将编译的镜像文件烧录至开发板中,关于编译烧录等可参考【rk3588】环境搭建及系统编译_rk3588 arm64-CSDN博客。在启动日志中,我们可以看到如下信息:
这个正好是我们在驱动入口函数中的打印信息,所以也证明了我们的驱动程序编译进内核并且正常运行了。
编译为驱动模块
上文我们介绍的是将驱动编译进内核,如果需要编译为驱动模块,直接在Kconfig
文件中将default y
修改为default m
。然后执行
./build.sh modules
表示编译内核模块,也仅仅编译设置为模块的驱动,然后会在驱动目录下生成haptics.ko
文件。这个文件需要我们手动安装卸载,但首先得发送到开发板上。
总结
驱动的编译分为两种方式:
编译进内核:
Kconfig
文件中配置为default y
- 执行
./build.sh kernel
指令进行编译 - 上述指令是编译整个内核,编译后将镜像文件烧录至开发板,驱动会自动加载
编译为模块:
Kconfig
文件中配置为default m
- 执行
./build.sh modules
指令进行编译 - 上述指令是编译驱动模块,编译结果只有
.ko
文件,需要上传至开发板手动加载(开发板提前烧录好不含驱动的镜像)
这两种方式具体使用哪一个呢,需要视具体情况。但是好像可以不需要使用界面配置,还是直接修改配置文件比较方便。