近期做的工作主要有两个,一是将dvsdk_4中的video_copy项目移植到自己的板子上,在参考资料极其匮乏的情况下,本人继续发扬艰苦奋斗的作风和打不死的小强精神,终于将Omap3530中的DSP成功地跑起来了。另一个工作就是开始学习Linux设备驱动开发,为编写新设备的驱动做准备。dvsdk的内容比较庞杂,所做的工作还在整理之中,后面我会发出来。Linux设备驱动开发的资料很多,我只是将我自己所做的工作记录下来,供和我一样的初学者参考,以后再回来查看的时候也方便一些。文中不足之处还请大家多多指点~~
Linux内核源码有一半是由驱动组成的,驱动在Linux完成其强大功能中扮演重要角色,而在开发自己的系统时,有时会发现无法再现成代码中找到支持特定的硬件的驱动,这是就需要自己动手,才能“丰衣足食”了。^_^
自古以来,学习一门新编程语言的第一步就是写一个打印“hello world”的程序,在本文中,我们将用同样的方式学习编写一个简单的内核模块设备驱动程序。
首先,新建一个hello.c文件:
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk(KERN_ALERT "Hello, world\n");
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
这个模块定义了两个函数,一个在模块加载到内核时被调用(hello_init),一个在模块去除时被调用(hello_exit)。module_init和module_exit这几行使用了特别的内核宏来指出这两个函数的角色。另一个特别的宏(MODULE_LICENSE)是用来告知内核,该模块带有一个自由的许可证,没有这样的说明,在模块加载时内核会报错。
printk函数在Linux内核中定义并且对模块可用,它与标准C库函数printf的行为相似。内核需要它自己的打印函数,因为没有C库的支持。字串KERN_ALERT是消息的优先级。在此模块中指定了一个高优先级,因为使用默认优先级的消息可能不会直接显示,这依赖于运行的内核版本、klogd守护进程的版本以及配置。
为了编译模块文件,可以创建一个Makefile文件,对本例来说,非常简单,只需一行即可,命令如下:
obj-m := hello.o
obj-m指出将要编译成的内核模块列表。*.o 格式文件会自动地由相应的 *.c 文件生成(不需要显式地罗列所有源代码文件)
如果要把上述程序编译为一个运行时加载和删除的模块,则编译命令如下所示。
make -C /usr/src/kernels/2.6.25-14.fc9.i686 M=$PWD modules
这个命令首先是改变目录到用 -C 选项指定的位置(即内核源代码目录,这个参数要根据自己的情况而定)。这个 M= 选项使Makefile在构造modules目标前,返回到模块源码目录。然后,modules目标指向obj-m变量中设定的模块。这里的编译规则的意思是:在包含内核源代码位置的地方进行make,然后再编译 $PWD (当前)目录下的modules。这里允许我们使用所有定义在内核源代码树下的所有规则来编译我们的内核模块。
编译完毕之后,就会在源代码目录下生成hello.ko文件,这就是内核驱动模块了。我们使用下面的命令来加载hello模块。
insmod hello.ko
下列命令完成相反的过程,即卸载hello模块。
rmmod hello
这时,你会发现终端里什么输出也没有,不用急,因为printk是内核输出函数,要查看的话,还要执行下列指令。
dmesg | tail
这时,在终端里就会打印出内核信息了,如下图所示。
至此,一个最简单的内核模块驱动程序就完成了。^_^