驱动开发时候,尽量选择对应操作系统内核的Linux系统作为上位机平台
下载源码与编译
源码的下载可以从网站:https://mirrors.edge.kernel.org/pub/linux/kernel/
找到对应的内核版本,然后下载,通过make menuconfig和make,进行编译。
没有编译过的内核,驱动开发过程中进行编译可能有错误,找不到文件等。
编写一个最简单的驱动
如下是hello.c文件的驱动程序。其中声明了证书,和模块加载后与退出时应该执行的函数。
#include<linux/module.h> #include<linux/kernel.h> MODULE_LICENSE("Dual BSD/GPL"); static int hello_init(void) { printk(KERN_ALERT "hello,world "); return 0; } static void hello_exit(void) { printk(KERN_ALERT "goodbye,world "); } module_init(hello_init); module_exit(hello_exit);
编写Makefile文件
Makefile文件的编写如下,主要是KERNELDIR,为linux源码的位置
ifeq ($(KERNELRELEASE),) KERNELDIR ?= /usr/src/linux-source-5.4.0 PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions else obj-m := hello.o endif
修改部分信息和执行
当编译的内核为系统本身的内核,但是make以后生成的ko文件无法加载,即通过insmod xxx.ko无法加载,格式不对
查看dmesg信息,参考解决:https://www.cnblogs.com/blfbuaa/p/6907027.html
正常执行后再dmesg中会有相应的加载和卸载模块的message信息。卸载命令为rmmod xxx
模块之间的依赖通信
以下为add_sub.c
#include<linux/kernel.h> #include<linux/module.h> #include"add_sub.h" long add_integer(long a,long b) { printk(KERN_ALERT "add init"); return a+b; } long sub_integer(long a,long b) { printk(KERN_ALERT "sub init"); return a-b; } EXPORT_SYMBOL(add_integer); EXPORT_SYMBOL(sub_integer); MODULE_LICENSE("Dual BSD/GPL");
以下为add_sub.h
#ifndef _ADD_SUB_H_ #define _ADD_SUB_H_ long add_integer(long a,long b); long sub_integer(long a,long b); #endif
以下为Makefile,当执行完make后,则生成了符号表文件,其为Module.symvers,该文件可以用于其他文件的函数引用
ifeq ($(KERNELRELEASE),) KERNELDIR ?= /usr/src/linux-source-5.4.0 PWD := $(shell pwd) PRINT_INC =$(PWD)/../include EXTRA_CFLAGS += -I $(PRINT_INC) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions else obj-m := add_sub.o endif
以下为test.c
#include<linux/kernel.h> #include<linux/module.h> #include"../add_sub.h" static long a = 1; static long b = 1; static int AddOrSub = 1; static int test_init(void) { long result = 0; printk(KERN_ALERT "test init "); if (1 == AddOrSub) { result = add_integer(a,b); } else { result = sub_integer(a,b); } //printk(KERN_ALERT, "the %s result is %ld", AddOrSub==1?"Add":"Sub",result); return 0; } static void test_exit(void) { printk(KERN_ALERT, "test_exit"); } module_init(test_init); module_exit(test_exit); module_param(a,long,S_IRUGO); module_param(b,long,S_IRUGO); module_param(AddOrSub,int,S_IRUGO); MODULE_LICENSE("Dual BSD/GPL");
以下为test.c的Makefile
ifeq ($(KERNELRELEASE),) KERNELDIR ?= /usr/src/linux-source-5.4.0 PWD := $(shell pwd) KBUILD_EXTRA_SYMBOLS=$(obj)/../print/Module.symvers modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions else obj-m := test.o endif
编译完后,需要先挂载add_sub模块,然后再挂载test模块。
未得到预期的效果,主要是在顺次加载后,并在加载过程添加a,b等参数,无法从dmesg中获得一个调用输出
将模块编译到内核中
如在drivers下建立add_sub_Kconfig文件夹,然后放入对应的源码文件,对应的Makefile和Kconfig文件。其中Kconfig文件用于make menuconfig的索引,其和上层的Kconfig关联
Makefile文件内容如下:
obj-$(CONFIG_ADD_SUB)+=add_sub.o obj-$(CONFIG_TEST)+=test.o
Kconfig文件内容如下:
# # add_sub configuration # menu "ADD_SUB" comment "ADD_SUB" config CONFIG_ADD_SUB tristate "ADD_SUB support" default y config CONFIG_TEST tristate "ADD_SUB test support" depends on CONFIG_ADD_SUB default y endmenu
然后修改上层的Kconfig文件,添加如下内容:
Source "drivers/add_sub_Kconfig/Kconfig"
然后修改上层的Makefile文件,添加如下内容:
obj-$(ADD_SUB) += add_sub_Kconfig/
就可以在主目录下执行make menuconfig后,在驱动下找到对应的驱动和编译信息了