2020-02-09
关键字:
在嵌入式 Linux 开发中,驱动程序通常都是用 C语言 来编写的,并经编译后生成为目标文件,即 '.o' 文件。随后又可在编译系统时以两种形式打包成系统镜像文件:
1、uImage
即内核的二进制文件。这种形式是直接将内核驱动程序打包进系统文件中。这种形式的驱动程序将会在内核加载时运行,即随系统启动而运行。这种形式的驱动预置在一定程度上会影响系统的开机耗时。
2、ko 文件
即 kernel object,这种形式是将驱动程序以独立的模块文件存在于系统中。这种形式的驱动程序灵活度高,可以在系统启动完成后慢慢加载运行,也可以直接在系统运行期卸载驱动。ko 形式的驱动在开发时的前提是系统内核已编译成功。
嵌入式 Linux 的驱动程序编写是有规范的,通常可以遵照如下几个步骤来编写:
1、编写头文件
#include <linux/init.h>
#include <linux/module.h>
2、驱动模块装载和卸载函数入口声明
module_init(cus_drv_init);
module_exit(cus_drv_exit);
3、实现模块装载和卸载的函数入口
static int __init cus_drv_init(void)
{
//一般在这个函数里做申请系统资源操作。
return 0;
}
static void __exit cus_drv_exit(void)
{
//一般在这里释放申请到的系统资源操作。
}
以上两个函数入口函数中的 __init 与 __exit 可以填写也可以不填写。对于初始化函数来说,它们一般在模块被装载完成以后就没有任何的作用了,这种没有任何作用的函数仍然占据着内存对于某些内存资源很紧张的嵌入式设备来说将会是一种很奢侈的行为。因此,内核会将标记为 __init 的函数在模块装载完成之后“释放掉”以让出对应的内存空间。__exit 标记与之有类似且相反的功能。
4、GPL声明
MODULE_LICENSE("GPL");
ko 形式的驱动程序在加载时可以传参。例如:insmod hello.ko myname="chorm" myvalue=27
在驱动代码中,处理这种参数的的函数声明为:
module_param(name, type, perm);
参数1 表示要传递的参数的名称,如 myname, myvalue。
参数2 表示参数的类型,如charp, int。
参数3 表示文件的权限,如0666。每个驱动程序在加载后会在 /sys/modules 下创建文件,这里指定的权限就是用于这个创建文件用的。
嵌入式 Linux 驱动模块程序的开发允许像普通C程序那样编译出“驱动支援库”来供其它 ko 程序调用。这种驱动支援库也是以 ko 存在的,它与普通的驱动程序的不同之处在于不需要实现驱动入口函数,即不需要上面四个步骤中的第2、第3步。同时在驱动支援库中将需要开放出去的接口函数以 EXPORT_SYMBOL(function_name); 或 EXPORT_SYMBOL_GPL(function_name); 的形式声明。以下是一个普通 ko 驱动调用支援ko库的示例源码:
#include <linux/init.h> #include <linux/module.h> #include "mymath.h" static int __init hello_init() { printk("module init. "); printk("add result:%d ", my_add(122, 6)); return 0; } static void __exit hello_exit() { printk("module exit. "); }
static int initInt = 0;
static char *initChar = NULL;
module_param(initInt, int, S_IRUGO);
module_param(initChar, charp, S_IRUGO);
module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL");
mymath.h 是支援ko库的接口头文件。
#include <linux/init.h> #include <linux/module.h> int my_add(int a, int b) { return a + b; } EXPORT_SYMBOL(my_add); MODULE_LICENSE("GPL");
以上两个源码文件将会编译出两个 ko 程序文件来。需要注意的是,因为第一个驱动程序需要依赖第二个驱动程序中开放的接口,因此必须先加载第二个驱动程序。
以下是可以编译出 ko 形式的驱动的 Makefile
obj-m += demo.o KDIR := /home/C3/chorm/rockchip/myrk3128/kernel PWD ?= $(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean:
KDIR 填的是你的内核源码根目录。