驱动的加载有两种方式,一种是写进操作系统内核,跟随操作系统启动和关闭的静态加载方式,另一种是灵活多变,在操作系统运行过程中临时插入驱动的动态加载方式。
在本章中,我们将在arm32开发板上进行驱动加载,并实现两种不同的驱动加载方式。
静态加载驱动
在静态加载方式下,可以将新功能与内核源码一起编译进uImage内,由于我们上一章是从字符设备驱动开始进行驱动架构的学习的,所以本小节继续沿用之前的驱动代码作为示例。
进入Linux内核目录并将驱动代码复制到drivers/char目录下
cd /home/workspace/linux-5.10.186
cp ../drivers/myhello.c ./drivers/char/
编辑drivers/char/Kconfig文件
vim drivers/char/Kconfig
# 在Kconfig文件内添加以下内容
config MY_HELLO
tristate "This is a Hello World test!"
help
This is a test for loading module of Hello World!
编辑drivers/char/Makefile文件
vim dirvers/char/Makefile
# 在Makefile文件中添加以下内容
obj-$(CONFIG_MY_HELLO) += myhello.o
保存退出后,运行automake_arm32.sh,进入menuconfig界面
./automake_arm32.sh
进入Device Drivers > Chatacter devices菜单栏内,将我们添加的驱动选中
保存退出menuconfig,脚本继续自动编译,完成后如下图所示
进入objects目录,运行Qemu查看运行效果
cd /home/workspace/objects
./qemu-start-uboot-arm32.sh
可以看到,在内核启动过程中,新添加的驱动成功加载。
动态加载驱动
驱动的动态加载分为两种方式,一种是在内核源码中将之编译成模块,而后通过make modules_install将模块导入文件系统内,等内核启动完毕后手动加载;另一种方式则是直接将驱动源码放在内核源码之外进行编译,同样可以通过make modules_install将模块导入根文件系统。两种方式从原理上看是一致的,不过第二种方式仅适用于实验和学习内核驱动阶段,因为这种方式会导致驱动没有注册进内核信息Kconfig树中,导致内核报“loading out-of-tree module taints kernel.”警告,正式上产品线的驱动代码还是推荐使用第一种方式进行加载。
内部驱动模块加载
在前一小节静态驱动加载的基础上,运行automake_arm32.sh进入menuconfig界面
cd /home/workspace/linux-5.10.186
./automake_arm32.sh
进入Device Drivers > Chatacter devices菜单栏内,将我们添加的驱动改为<M>模式
运行make modules_install将模块导入根文件系统
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- modules INSTALL_MOD_PATH=/sync/rootfs modules_install O=../objects/vexpress-v2p-ca9
运行 ls /sync/rootfs/lib/modules/5.10.186/build/drivers/char/ 可以看到myhello.ko已加入根文件系统
接下来同样运行Qemu进行验证
cd /home/workspace/objects
./qemu-start-uboot-arm32.sh
出现以上提示说明内核驱动模块加载(卸载)成功。
外部驱动模块加载
进入外部驱动模块目录并进行编译
cd /home/workspace/drivers
sudo make ARCH=arm modules_install
完成后运行ls /sync/rootfs/lib/modules/5.10.186/extra/ -l 可以看到myhello.ko已加入根文件系统
接下来同样运行Qemu进行验证
cd /home/workspace/objects
./qemu-start-uboot-arm32.sh
出现以上提示说明内核驱动模块加载(卸载)成功。