如何将Linux内核驱动程序模块添加为Buildroot软件包?

10

我正在使用Xilinx的Zybo Board构建嵌入式Linux,使用Buildroot工具。现在,我想要添加一个用C语言编写的驱动程序,用户程序可以使用它来写入一些特定的寄存器,从而控制一些LED灯。当我查阅手册时,基本上说要做的第一件事是在新包文件夹中创建一个Config.in文件,在其中编写一些说明该驱动程序的文本。好的,我已经做到了。但现在是Makefile:我不太明白需要放什么东西在里面。它只是像这样的编译命令gcc -o ledcontrol hellofunc.c吗?除了Config.in和Makefile之外还有其他需要做的事情吗?


可能是将新的驱动程序代码添加到Linux源代码的重复问题。 - geek
3个回答

18

完全自动化的QEMU外部模块示例

这里也有一个设置版本:https://github.com/cirosantilli/linux-kernel-module-cheat/tree/753cbe68ff50bea0982e1791c8a2178a999d8377/buildroot_packages/kernel_modules,并在https://cirosantilli.com/linux-kernel-module-cheat/kernel-modules-buildroot-package上进行了说明。但下面的设置是从该存储库中完全提取出来的,并且可以独立工作。

源代码树:

  • buildroot/:Buildroot 2017.02,最好作为git子模块
  • kernel_module/:带有一些模块的外部软件包,最好在git存储库中跟踪
    • Config.in
    • external.mk
    • external.desc
    • Makefile
    • hello.c:hello world模块
    • overlay/etc/inittab:只是一个小修复,使shell工作正常,与内核模块本身无关

kernel_module/Config.in

config BR2_PACKAGE_KERNEL_MODULE
        bool "kernel_module"
        depends on BR2_LINUX_KERNEL
        help
                Linux Kernel Module Cheat.

kernel_module/external.mk

KERNEL_MODULE_VERSION = 1.0
KERNEL_MODULE_SITE = $(BR2_EXTERNAL_KERNEL_MODULE_PATH)
KERNEL_MODULE_SITE_METHOD = local
$(eval $(kernel-module))
$(eval $(generic-package))

kernel_module/external.desc

name: KERNEL_MODULE

内核模块/Makefile

obj-m += $(addsuffix .o, $(notdir $(basename $(wildcard $(BR2_EXTERNAL_KERNEL_MODULE_PATH)/*.c))))
ccflags-y := -DDEBUG -g -std=gnu99 -Wno-declaration-after-statement

.PHONY: all clean

obj-m += $(addsuffix .o, $(notdir $(basename $(wildcard $(BR2_EXTERNAL_KERNEL_MODULE_PATH)/*.c))))
ccflags-y := -DDEBUG -g -std=gnu99 -Wno-declaration-after-statement

.PHONY: all clean

all:
    $(MAKE) -C '$(LINUX_DIR)' M='$(PWD)' modules

clean:
    $(MAKE) -C '$(LINUX_DIR)' M='$(PWD)' clean

kernel_module/hello.c

#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE("GPL");

static int myinit(void)
{
    printk(KERN_INFO "hello init\n");
    return 0;
}

static void myexit(void)
{
    printk(KERN_INFO "hello exit\n");
}

module_init(myinit)
module_exit(myexit)

overlay/etc/inittab

::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mount -o remount,rw /
::sysinit:/bin/mkdir -p /dev/pts
::sysinit:/bin/mkdir -p /dev/shm
::sysinit:/bin/mount -a
::sysinit:/bin/hostname -F /etc/hostname
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::shutdown:/etc/init.d/rcK
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r

使用方法:

sudo apt install make gcc file bzip2 build-essential wget cpio python unzip rsync bc

git clone https://github.com/buildroot/buildroot
cd buildroot
git checkout 2017.2
make BR2_EXTERNAL="$(pwd)/../kernel_module" qemu_x86_64_defconfig
echo '
BR2_PACKAGE_KERNEL_MODULE=y
BR2_TARGET_ROOTFS_EXT2_EXTRA_BLOCKS=1024
BR2_ROOTFS_OVERLAY="overlay"
' >> .config
make olddefconfig
make BR2_JLEVEL="$(nproc)" all

qemu-system-x86_64 -M pc -kernel output/images/bzImage -drive file=output/images/rootfs.ext2,if=virtio,format=raw -append 'root=/dev/vda console=ttyS0' -net nic,model=virtio -nographic -serial mon:stdio -net user

QEMU打开后,然后运行:

modprobe hello
modprobe -r hello

dmesg 显示:

hello init
hello exit

关键行是$(eval $(kernel-module))external.mk中,该行为我们设置了一切,并将模块安装在/lib/modules/*/extra/hello.ko中,其中包括modules.dep以实现模块之间的依赖。详情请参见:如何从另一个模块调用导出的内核模块函数? 如何使用GDB逐步调试内核模块:请参见如何使用QEMU调试Linux内核模块? 要在启动时自动加载模块,请使用BR2_ROOTFS_OVERLAY="../rootfs_overlay"和一个执行modproberootfs_overlay/etc/init.d/S99modules文件。
示例代码: https://github.com/cirosantilli/buildroot/tree/9580078b98f885ca94e4dfc896265a8a491f6ae1 非常简洁,但同样有效。
已在Ubuntu 16.04上测试过。

6
您需要的是“内核模块基础设施”。您可以查看Buildroot手册,链接如下:
https://buildroot.org/downloads/manual/manual.html#_infrastructure_for_packages_building_kernel_modules
此外,还有很多使用Buildroot提供的内核模块基础设施的示例,可协助将内核模块添加到Buildroot中。
$ git grep "(kernel-module)" -- package/
package/amd-catalyst/amd-catalyst.mk:$(eval $(kernel-module))
package/batman-adv/batman-adv.mk:$(eval $(kernel-module))
package/cryptodev-linux/cryptodev-linux.mk:$(eval $(kernel-module))
package/emlog/emlog.mk:$(eval $(kernel-module))
package/freescale-imx/kernel-module-imx-gpu-viv/kernel-module-imx-gpu-viv.mk:$(eval $(kernel-module))
package/igh-ethercat/igh-ethercat.mk:$(eval $(kernel-module))
package/iqvlinux/iqvlinux.mk:$(eval $(kernel-module))
package/ktap/ktap.mk:$(eval $(kernel-module))
package/linux-backports/linux-backports.mk:$(eval $(kernel-module))
package/lttng-modules/lttng-modules.mk:$(eval $(kernel-module))
package/nvidia-driver/nvidia-driver.mk:$(eval $(kernel-module))
package/ocf-linux/ocf-linux.mk:$(eval $(kernel-module))
package/on2-8170-modules/on2-8170-modules.mk:$(eval $(kernel-module))
package/owl-linux/owl-linux.mk:$(eval $(kernel-module))
package/pkg-kernel-module.mk:#   $(eval $(kernel-module))
package/pkg-kernel-module.mk:#   $(eval $(kernel-module))
package/rtl8188eu/rtl8188eu.mk:$(eval $(kernel-module))
package/rtl8821au/rtl8821au.mk:$(eval $(kernel-module))
package/simicsfs/simicsfs.mk:$(eval $(kernel-module))
package/sysdig/sysdig.mk:$(eval $(kernel-module))

嗯,我想我可以写一个较长的回复,但那只是在抄袭Buildroot手册。因此,让我们尊重那些编写如此干净文档和代码的勇敢开发者(Buildroot核心非常干净,每个软件包都经过广泛审查,因此它们通常也非常好写)。


谢谢提供链接!在第一个例子中,他们将*.tar文件作为源代码。我如何使用驱动程序的源代码(用C语言编写)?我可以直接使用它作为源代码吗? - Daiz
@Daiz,SITE_METHOD=local 就是为此而设计的。请参考此处 17.5.2 泛包参考 https://buildroot.org/downloads/manual/manual.html#_infrastructure_for_packages_with_specific_build_systems - Ezequiel Garcia

2
Ciro的帖子中的所有内容都是准确的。然而,对于任何新手来说,可能不会立即意识到通过make linux-menuconfig对Linux配置进行的任何更改都需要重新构建项目或手动重新构建模块。请参见此讨论线程这些文档更新

嗨,丹,对我来说单独的答案很好,这样你就可以得到应有的修正声望。只有一件事,你确定需要这个依赖吗?根据手册,似乎这应该由 $(eval $(kernel-module)) 自动完成 https://buildroot.org/downloads/manual/manual.html#_infrastructure_for_packages_building_kernel_modules “对 Linux 的依赖性会自动添加,因此不需要在 FOO_DEPENDENCIES 中指定。” - Ciro Santilli OurBigBook.com
1
嗨Ciro,感谢你的指引。我同意文档清楚地表明这不是必需的,并且相关的文档更改和依赖添加逻辑在此处是在我参考的邮件列表发布之后才出现的。我注意到一个情况,我的模块重建失败了,但在我注意到邮件列表发布并手动添加依赖项后,问题似乎得到了解决。我需要进一步研究这个问题,以了解真正发生了什么。感谢你的帖子。 - dan
好的,如果你发现了什么,请告诉我,丹。 - Ciro Santilli OurBigBook.com

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接