1.驱动开发的准备工作
1)正常运linux系统的开发板,要求开发板中的linux的zImage必须是自己编译的,不能是别人编译的。
2)内核源码树,其实就是一个经过了配置编译之后的内核源码。
3)nfd挂载的rootfs,主机ubuntu中必须搭建一个nfs服务器。
2.驱动开发的步骤
1)进行驱动源代码编写、Makefile编写、编译。
2)insmod装载模块、测试、rmmod卸载模块
测试代码:
Makefile代码:
#ubuntu的内核源码树,如果要编译在ubuntu中安装的模块就打开这2个
KERN_VER = $(shell uname -r)
KERN_DIR = /lib/modules/$(KERN_VER)/build
# 开发板的linux内核的源码树目录
#KERN_DIR = /root/driver/kernel
obj-m += module_test.o
all:
make -C $(KERN_DIR) M=`pwd` modules
cp:
cp *.ko /root/porting_x210/rootfs/rootfs/driver_test
.PHONY: clean
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
模块代码:
#include <linux/module.h> // module_init module_exit
#include <linux/init.h> // __init __exit
// 模块安装函数
static int __init chrdev_init(void)
{
printk(KERN_INFO "chrdev_init helloworld init\n");
//printk("<7>" "chrdev_init helloworld init\n");
//printk("<7> chrdev_init helloworld init\n");
return 0;
}
// 模块下载函数
static void __exit chrdev_exit(void)
{
printk(KERN_INFO "chrdev_exit helloworld exit\n");
}
module_init(chrdev_init);
module_exit(chrdev_exit);
// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("aston"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息
3.常用的模块操作命令
1)Lsmod(list module,将模块列表显示),功能是打印出当前内核中已经安装的模块列表
2)insmod(install module,安装模块),功能是向当前内核中去安装一个模块,用法是insmod xxx.ko
3)modinfo(module information,模块信息),功能是打印出一个内核模块的自带信息。,用法是modinfo xxx.ko
4)rmmod(remove module,卸载模块),功能是从当前内核中卸载一个已经安装了的模块,用法是rmmod xxx(注意卸载模块时只需要输入模块名即可,不能加.ko后缀)
5)剩下的后面再说,暂时用不到(如modprobe、depmod等)
4.模块的安装
1)先lsmod再insmod看安装前后系统内模块记录。实践测试标明内核会将最新安装的模块放在lsmod显示的最前面。
2)insmod与module_init宏。模块源代码中用module_init宏声明了一个函数(在我们这个例子里是chrdev_init函数),作用就是指定chrdev_init这个函数和insmod命令绑定起来,也就是说当我们insmod module_test.ko时,insmod命令内部实际执行的操作就是帮我们调用chrdev_init函数。
module_init(chrdev_init);
module_exit(chrdev_exit);
照此分析,那insmod时就应该能看到chrdev_init中使用printk打印出来的一个chrdev_init字符串,但是实际没看到。原因是ubuntu中拦截了,要怎么才能看到呢?在ubuntu中使用dmesg命令就可以看到了。
3)模块安装时insmod内部除了帮我们调用module_init宏所声明的函数外,实际还做了一些别的事(譬如lsmod能看到多了一个模块也是insmod帮我们在内部做了记录),但是我们就不用管了。
4.模块的版本信息
1)使用modinfo查看模块的版本信息
// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("aston"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息
2)内核zImage中也有一个确定的版本信息
3)insmod时模块的vermagic必须和内核的相同,否则不能安装,报错信息为:insmod: ERROR: could not insert module module_test.ko: Invalid module format
4)模块的版本信息是为了保证模块和内核的兼容性,是一种安全措施
5)如何保证模块的vermagic和内核的vermagic一致?编译模块的内核源码树就是我们编译正在运行的这个内核的那个内核源码树即可。说白了就是模块和内核要同出一门。
在开发板中运行测试模块:
内核源码树的位置,也是就是编译内核的目录!编译完成之后拷贝到开发板的rootfs中、
Makefile代码:
#ubuntu的内核源码树,如果要编译在ubuntu中安装的模块就打开这2个
#KERN_VER = $(shell uname -r)
#KERN_DIR = /lib/modules/$(KERN_VER)/build
# 开发板的linux内核的源码树目录
KERN_DIR = /sambashare/kernel/kernel
obj-m += module_test.o
all:
make -C $(KERN_DIR) M=`pwd` modules //进入源码树之后,在此路径进行模块化编译
cp:
cp *.ko /sambashare/rootfs/rootfs/driver/1.module_test
.PHONY: clean
clean:
make -C $(KERN_DIR) M=`pwd` modules clean