五、指定与驱动相关的信息
虽然指定这些信息不是必须的,但一个完整的Linux驱动程序都会指定这些与驱动相关的信息。一般需要为Linux驱动程序指定如下信息。
1. 模块作者:使用MODULE_AUTHOR宏指定。
2. 模块描述:使用MODULE_DESCRIPTION宏指定。
3. 模块别名:使用MODULE_ALIAS宏指定。
4. 开源协议:使用MODULE_LICENSE宏指定。
除了这些信息外,Linux驱动模块自己还会包含一些信息。读者可以执行下面的命令查看word_count.ko的信息。
# modinfo word_count.ko
执行上面的命令后,会输出如图6-6所示的信息。其中depends表示当前驱动模块的依赖,word_count并没有依赖什么,因此该项为空。vermagic表示当前Linux驱动模块在那个Linux内核版本下编译的。
现在使用下面的代码指定上述4种信息。一般会将这些代码放在word_count.c文件的最后。
MODULE_AUTHOR("lining");
MODULE_DESCRIPTION("statistics of wordcount.");
MODULE_ALIAS("word count module.");
MODULE_LICENSE("GPL");
现在使用上一节的方法重新编译word_count.c文件。然后再执行modinfo命令,就会显示如图6-7所示的信息。从图6-7可以看出,上面的代码设置的信息都包含在了word_count.ko文件中。
六、开源协议
虽然很多个人开发者或小公司并不太考虑开源协议的限制,但对于较大的公司如果违反开源协议,可能会有被起诉的风险。所以对有一定规模和影响力的公司使用开源软件时一定要注意这些软件使用的开源协议。
为了降低发布Linux驱动的难度和安装包尺寸,很多Linux驱动都是开放源代码的。在Linux驱动源代码中使用MODULE_LICENSE宏指定开源协议。例如,word_count驱动使用了GPL协议。那么我们要编写Linux'驱动程序应采取什么协议呢?目前开源协议比较多。读者可以到下面的页面查看所有的开源协议。
http://www.opensource.org/licenses/alphabetical
下面将介绍最常用的5种开源协议的基本情况。这5种开源协议以及其他的开源协议的详细情况请参阅Open SourceInitiative组织的相关页面。
GPL协议
对于喜欢钻研技术的程序员来说应该很喜欢GPL协议。因为GPL协议强迫使用该开源协议的软件开源。例如,Linux内核就采用了GPL协议。GPL的出发点就是免费/开源。但与其他开源协议(如BSD、Apache Licence)不同的是GPL协议开源的更彻底。不仅要求采用GPL协议的软件开源/免费,还要求其衍生代码开源/免费。例如,A软件采用了GPL协议,B软件使用了A软件,那么B软件也必须免费/开源。而其B软件必须也采用GPL协议。C软件又使用了B软件,C软件也必须开源/免费,当然,C软件也必须采用GPL协议。这就是所谓的“传染性”。这也是目前有很多Linux发行版及其他使用GPL协议的软件开源的原因,
由于GPL协议严格要求使用了GPL协议的软件产品必须使用GPL协议,而且必须开源/免费。对于商业软件或者对代码有保密要求的部门就非常不适合使用GPL协议发布软件,或引用基于GPL协议的类库。为了满足商业公司及保密的需要,在GPL协议的基础上又出现了LGPL协议。
LGPL协议
LGPL主要是为类库使用设计的开源协议。与GPL要求任何使用/修改/衍生的GPL类库的的软件必须采用GPL协议不同。LGPL 允许商业软件通过类库引用(link)方式使用LGPL类库而不需要开源商业软件的代码。这使得采用LGPL协议的开源代码可以被商业软件作为类库引用并发布和销售。
但是如果修改LGPL协议的代码或者衍生,则所有修改的代码,涉及修改部分的额外代码和衍生的代码都必须采用LGPL协议。因此LGPL协议的开源代码很适合作为第三方类库被商业软件引用,但不适合希望以LGPL协议代码为基础,通过修改和衍生的方式做二次开发的商业软件采用。
BSD协议
BSD开源协议是一个给于使用者很大自由的协议。基本上使用者可以“为所欲为”,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。但“为所欲为”的前提是当你发布使用了BSD协议的代码,或则以BSD协议代码为基础做二次开发自己的产品时,需要满足如下3个条件。
1. 如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议。
2. 如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。
3. 不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。
BSD 协议鼓励代码共享,但需要尊重源代码作者的著作权。BSD由于允许使用者修改和重新发布代码,也允许使用或在BSD代码上开发商业软件发布和销售,因此是对商业集成很友好的协议。而很多的公司企业在选用开源产品的时候都首选BSD协议,因为可以完全控制这些第三方的代码,在必要的时候可以修改或者二次开发。
Apache Licence 2.0协议
Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似,同样鼓励代码共享和尊重原作者的著作权,同样允许代码修改,再发布(作为开源或商业软件)。需要满足的条件也和BSD类似。
1. 需要给代码的用户一份Apache Licence
2. 如果你修改了代码,需要在被修改的文件中说明。
3. 在延伸的代码中(修改和由源代码衍生的代码中)需要带有原来代码中的协议,商标,专利声明和其他原来作者规定需要包含的说明。
4. 如果再次发布的产品中包含一个Notice文件,则在Notice文件中需要带有Apache Licence。你可以在Notice中增加自己的许可,但不可以表现为Apache Licence。
Apache Licence也是对商业应用友好的许可。使用者也可以在需要的时候修改代码来满足需要并作为开源或商业产品发布/销售。
MIT协议
MIT是和BSD一样限制宽松的许可协议,作者只想保留版权,而无任何其他了限制.也就是说,你必须在你的发行版里包含原许可协议的声明,无论你是以二进制发布的还是以源代码发布的。
七、注册和注销设备文件
本节将为word_count驱动建立一个设备文件,该设备文件的名称是wordcount,位于/dev目录中。设备文件与普通文件不同,不能使用IO函数建立,需要使用misc_register函数建立设备文件,使用misc_deregister函数注销(移除)设备文件。这两个函数的定义如下:
extern int misc_register(struct miscdevice * misc); extern int misc_deregister(struct miscdevice*misc);
一般需要在初始化Linux驱动时建立设备文件,在卸载Linux驱动时删除设备文件。而且设备文件还需要一个结构体(miscdevice)来描述与其相关的信息。miscdevice结构体中有一个重要的成员变量fops,用于描述设备文件在各种可触发事件的函数指针。该成员变量的数据类型也是一个结构体file_operations。
本节需要修改word_count.c文件的word_count_init和word_count_exit函数,并定义一些宏和变量。修改部分的代码如下:
// 定义设备文件名 #define DEVICE_NAME "wordcount" // 描述与设备文件触发的事件对应的回调函数指针 // owner:设备事件回调函数应用于哪些驱动模块,THIS_MODULE表示应用于当前驱动模块 static struct file_operations dev_fops = { .owner = THIS_MODULE}; // 描述设备文件的信息 // minor:次设备号 MISC_DYNAMIC_MINOR,:动态生成次设备号 name:设备文件名称 // fops:file_operations结构体变量指针 static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME,.fops = &dev_fops }; // 初始化Linux驱动 static int word_count_init(void) { int ret; // 建立设备文件 ret = misc_register(&misc); // 输出日志信息 printk("word_count_init_success\n"); return ret; } // 卸载Linux驱动 static void word_count_exit(void) { // 注销(移除)设备文件 misc_deregister(&misc); // 输出日志信息 printk("word_init_exit_success\n"); }
编写上面代码需要注意如下几点:
1. 设备文件由主设备号和次设备号描述。而使用misc_register函数只能设置次设备号。主设备号统一设为10。主设备号为10的设备是Linux系统中拥有共同特性的简单字符设备。这类设备称为misc设备。如果读者实现的驱动的功能并不复杂,可以考虑使用10作为其主设备号,而次设备号可以自己指定,也可以动态生成(需要指定MISC_DYNAMIC_MINOR常量)。因为采用这样的方式可以使用misc_register和misc_deregister函数简化注册和注销设备文件的步骤。在后面的章节会详细介绍如何使用register_chrdev_region和alloc_chrdev_region函数同时指定主设备号和次设备号的方式注册和注销设备文件。
2. miscdevice.name变量的值就是设备文件的名称。在本例中设备文件名称为wordcount。
3. 虽然file_operations结构体中定义了多个回调函数指针变量,但本节并未初始化任何一个回调函数指针变量。只初始化了file_operations.owner变量。如果该变量的值为module结构体,表示file_operations可被应用在这些由module指定的驱动模块中。如果owner变量的值为THIS_MODULE,表示file_operations只应用于当前驱动模块。
4. 如果成功注册了设备文件,misc_register函数返回非0的整数,如果注册设备文件失败,返回0。
5. 可能有的读者注意到了。word_count.c中的所有函数、变量都声明成了static。这是因为在C语言中用static声明函数、变量等资源,系统会将这些函数和变量单独放在内存的某一个区域,直到程序完全退出,否则这些资源不会被释放。Linux驱动一旦装载,除非手动卸载或关机,驱动会一直驻留内存,因此这些函数和变量资源会一直在内存中。也就是说多次调用这些资源不用再进行压栈、出栈操作了。有利于提高驱动的运行效率。
现在重新编译word_count.c文件并使用如下的命令安装word_count驱动。
# insmod word_count.ko
如果word_count驱动已经被安装,应先使用下面的命令下载word_count驱动,然后再使用上面的命令安装word_count驱动。
# rmmod word_count
安装完word_count驱动后,使用下面的命令查看/dev目录中的设备。
# ls –a /dev
执行上面的命令后,会输出如图6-8所示的信息,其中多了一个wordcount文件(在白框中)。
如果想查看wordcount设备文件的主设备号和次设备号,可以使用如下的命令。
# ls –l /dev
执行上面的命令会输出如图6-9所示的信息,白框中的第一个数字是主设备号,第二个数字是从设备号。
使用下面的命令可获显示当期系统中有哪些主设备以及主设备号。
# cat /proc/devices
执行上面的命令后会输出如图6-10所示的信息,从中可以找到misc设备以及主设备编号10。
本文节选至《Android深度探索(卷1):HAL与驱动开发》,接下来几篇文章将详细阐述如何开发ARM架构的Linux驱动,并分别利用android程序、NDK、可执行文件测试Linux驱动。可在ubuntu Linux、Android模拟器和S3C6410开发板(可以选购OK6410-A开发板,需要刷Android)