前言

(1)
(2)前文我们说了,驱动程序有两种运行方式,一种是以内核模块(.ko)的形式体现出来,另外一种是直接编译进入Linux内核中。上文我们已经介绍了如何以内核模块的形式体现,现在我们将介绍如何讲驱动程序编译进入内核中。新手了解即可,甚至可以不看,因为新手写的驱动文件不建议编译进入内核。后续等你熟练了再回来看也不迟。
(3)如果按照本文的思路,将自己的驱动程序编译进入内核,之后再进行驱动装载,大概率会出现loading out-of-tree module taints kernel这个警告,意思就是说你的垃圾代码污染了内核。如果你依旧使用这个内核进行操作,并且产生了问题,而且你还不知好歹的向Linux内核研发大佬提交问题。那么大佬看到这个警告之后,就不会鸟你。因为Linux内核大佬只会对源码负责。
(4)这个警告一般是不会有啥问题的,但是你如果有强迫症,可以先把原来干净的.config文件复制一份到其他地方,做完实验之后再删除自己创建的文件,同时把干净的.config文件下载进入开发板。

理论

进入内核配置界面

(1)作为Linux驱动工程师,必须具备根据公司业务需求,增删Linux内核的能力。
(2)为了方便驱动工程师对内核进行修改我们可以使用如下四种命令:
<1>make config (基于文本的最为传统的配置界面, 不推荐使用)
<2>make menuconfig (基于文本菜单的配置界面)
<3>make xconfig (要求 QT 被安装)
<4>make gconfig (要求 GTK+ 被安装)
(3)一般来说,我们都是采用的make menuconfig 命令来对内核进行裁剪。
<1>如下图,我们进入Linux内核路径,我的内核路径是/home/book/100ask_imx6ull-sdk/Linux-4.9.88因此进入这个目录,内核路径需要根据你本人下载的位置定义,每个人都可能不一样。
<2>进入内核路径之后,输入make menuconfig 等待加载即可进入下图中的配置界面。

cd /home/book/100ask_imx6ull-sdk/Linux-4.9.88
make menuconfig 
# make menuconfig' requires the ncurses libraries 报错
sudo apt-get installlibncurses5-dev

Linux驱动入门篇—— 如何将驱动编译进入Linux内核_驱动程序

(4)对于刚使用的配置界面的同学,可能会出现如下三种错误
<1>如果提示错误“make menuconfig’ requires the ncurses libraries”,请使用命令sudo apt-get installlibncurses5-dev安装ncurses。
<2>提示“Your display is too small to run Menuconfig!”这个错误是因为控制终端的窗口太小,放大窗口或者全屏操作即可。
<3>提示"make:*** No rule to make target 'menucofnig.Stop"这个错误是没有在内核源码的顶层目录输入make menuconfig。

配置界面的操作

光标移动

(1)他这个操作真的很简单,就是简单上的上,下,左,右移动光标。

Linux驱动入门篇—— 如何将驱动编译进入Linux内核_#linux_02

搜索界面

(1)如果需要搜索某一个驱动程序,只需要输入"/"键即可弹出搜索界面。
(2)menuconfig 的设计比较反人类,他不是按照从a到z顺序排列的,这个排列是乱序,所以想要找到目标,特别麻烦。
(3)不同的模块直接具有依赖关系,如果依赖条件不满足,就不会出现在menuconfig 中

Linux驱动入门篇—— 如何将驱动编译进入Linux内核_#运维_03


Linux驱动入门篇—— 如何将驱动编译进入Linux内核_#运维_04


Linux驱动入门篇—— 如何将驱动编译进入Linux内核_#运维_05

快捷键快速跳转到对应的选项

(1)上面我们说了,配置界面不是按照从a到z的顺序排列的,非常反人类。所以想要找到自己要配置的选项,是相当的麻烦。
(2)但是完全没救了吗?也没有,配置界面有一个快捷键跳转的功能。menuconfig 中的每一行的选项, 都有一个用特殊颜色标记出来的字母, 很明显,此字母, 就是该行的快捷字母。

Linux驱动入门篇—— 如何将驱动编译进入Linux内核_驱动程序_06

驱动程序的三种状态以及图标的含义

(3)驱动程序有三种状态," M “表示将驱动编译成模块,” * “表示将驱动编译进入内核,空白则表示不进行编译。按空格键可以配置驱动程序的状态。
(4)在配置界面中,我们能够看到 [],<>,()这三种图标。
<1>[]只有两种状态,”“或者空白。
<2><>可以设置三种状态,“M”、”
"或者空白。
<3>()表示用来存放字符串或者16进制数。

Linux驱动入门篇—— 如何将驱动编译进入Linux内核_#linux_07

menuconfig添加目录语法介绍

(1)配置界面的基本操作我们会了,但是我们如何将自己的驱动加入到配置界面呢?

Linux驱动入门篇—— 如何将驱动编译进入Linux内核_#服务器_08

实操

Ubuntu中进行的操作

(1)进入内核目录的/drivers/char目录
(2)创建一个文件夹helloworld的文件夹,然后在这个文件夹中写入一些帮助信息。

cd /drivers/char
mkdir helloworld
cd helloworld
#————————————————————————
vi Kconfig
#Kconfig中写入下面这些
config helloworldbool "hello world support"default yhelphelloworld
#————————————————————————
vi Makefile
#Makefile中写入下面这些
obj-$(CONFIG_helloworld)        := helloworld.o
#————————————————————————
vi hello_drv.c

hello_drv.c 中写入下面这些

/******************************************************************************** @文件名    : 驱动入门* @作者      : zhangyixu* @CSDN_ID      : qq_63922192  * @CSDN_昵称      :  风正豪* @日期      : 2023年09月01日* @TABsize   : 一个TAB为4空格* @编码格式  : UTF-8* @版权声明  : 仅供参考学习,未经允许禁止商用********************************************************************************/#include <linux/module.h>
#include <linux/init.h>//入口函数
static int hello_init(void)
{printk("hello world --- CSDN_qq_63922192\r\n");return 0;
}//出口函数
static void hello_exit(void)
{printk("good bye\r\n");
}module_init(hello_init); //确认驱动入口函数
module_exit(hello_exit); //确认驱动出口函数/*最后我们需要在驱动中加入 LICENSE 信息和作者信息,其中 LICENSE 是必须添加的,否则的话编译的时候会报错,作者信息可以添加也可以不添加*这个协议要求我们代码必须免费开源,Linux遵循GPL协议,他的源代码可以开放使用,那么你写的内核驱动程序也要遵循GPL协议才能使用内核函数*因为指定了这个协议,你的代码也需要开放给别人免费使用,同时可以根据这个协议要求很多厂商提供源代码*但是很多厂商为了规避这个协议,驱动源代码很简单,复杂的东西放在应用层
*/
MODULE_LICENSE("GPL"); //指定模块为GPL协议
MODULE_AUTHOR("CSDN:qq_63922192");  //表明作者,可以不写
MODULE_VERSION("1.0");  //驱动版本申明,可以不写

(3)退回上一级目录,并且修改Kconfig和Makefile文件。

cd ..
#————————————————————————
vi Kconfig
#Kconfig中写入下面这些,注意要写在menu "Character devices"下面,因为我们的模块是要放在"Character devices"菜单中
source "drivers/char/helloworld/Kconfig"
#————————————————————————
vi Makefile
#Makefile中写入下面这些
obj-y                           += helloworld/

Linux驱动入门篇—— 如何将驱动编译进入Linux内核_开发板_09


Linux驱动入门篇—— 如何将驱动编译进入Linux内核_驱动程序_10

(4)返回到Linux内核顶层目录,输入make menuconfig进入配置界面,确定模块的存放路径。

cd ../..
make menuconfig

Linux驱动入门篇—— 如何将驱动编译进入Linux内核_#服务器_11


Linux驱动入门篇—— 如何将驱动编译进入Linux内核_#linux_12

(5)找到路径之后,确认我们所编写的模块为*,是要编译进入内核的。

Linux驱动入门篇—— 如何将驱动编译进入Linux内核_#服务器_13

(6)退出菜单界面,然后点击Yes,表示保存加进来的模块。

Linux驱动入门篇—— 如何将驱动编译进入Linux内核_#运维_14

(7)输入vi .config,输入"/hello",如果CONFIG_helloworld=y,表示我们的模块成功被选中,最终会编译进入内核。

Linux驱动入门篇—— 如何将驱动编译进入Linux内核_#linux_15

(8)
<1>在 uboot 中,通过“make xxx_defconfig”来配
置 uboot, xxx_defconfig 就是不同板子的配置文件
。因为我这里是韦东山的i.mx6ull开发板,所以是输入的make 100ask_imx6ull_defconfig。如果是正点原子的开发板,则是输入的make mx6ull_alientek_emmc_defconfig。
<2>make zImage -j4是用于编译Linux内核镜像文件,后面的-j 用于设置主机使用多少个核编译uboot,设置的核越多,编译速度越快。因为我的这个VMware 分配了 4 个核,那么最多只能使用-j4。
<3>编译完成之后,最终将内核镜像文件拷贝到网络文件系统中

make 100ask_imx6ull_defconfig
make zImage -j4
cp arch/arm/boot/zImage ~/nfs_rootfs

开发板中进行的操作

(1)接下来的操作需要在开发板上进行,最终打印结果为hello world — CSDN_qq_63922192,表示我们的驱动程序成功加载进入了Linux内核中。

mount -t nfs -o nolock,vers=3  192.168.5.11:/home/book/nfs_rootfs  /mnt
cp /mnt/zImage /boot
reboot
dmesg | grep "hello"

Linux驱动入门篇—— 如何将驱动编译进入Linux内核_驱动程序_16