标签:
第八章 让开发板发出声音:蜂鸣器驱动
linux驱动的代码重用有很多种方法。可以采用标准C 程序的方式。将要重用的代码放在其他的文件(在头文件中声明〉中。如果要使用某些功能, include 相应的头文件即可〈这.方式称为静态重用〉。也可以使用另外一种动态重用的方式,也就是一个Linux 驱动可以使用另外一个Linux驱动中的资源(函数、变量、宏等)。如果Linux 驱动的代码不多,将所有的代码都放到一个文件中并没有什么不妥,但对于复杂的Linux 驱动,就需要使用多个源代码文件存放不同的功能代码,这样做有利于代码分类和管理。当然,如果Linux 驱动要使用第三方的源代码, 那么就不得不编译多个源代码文件,最终便成.ko 文件或编译进Linux内核。C 或C++语言中编译多个源代码文件时,如果a.c 使用了 b.c 文件中的函数,需要在a.c 文件中使用 extern 预先定义b.c中的函数, extern 的作用就是告诉编译器该函数的函数名、参数个数、参数类型和返回值类型。这些信息对于将a.c 编译成a.o已经足够了。等到将a.o 和b.o 链接成可执行文件或程序库时,编译器再到b.c中寻找函数的具体要现。也就是说, extern 只在编译阶段起作用。除此之外,还可以使用b.h 文件定义b.c 中的函数,然后在a.c中包含b.h 文件。对于有些C/C++编译器,可以省略extern 关键字。不过为了更通用,建议还是加上extern。
代码重用分为静态和动态两种。另外一种代码共享的方式:模块依赖,也称为导出符号。如果只能用一句话解释如何利用导出符号实现代码共享,这句话就是“在一个驱动模块里使用另一个驱动模块里的被导出的符号(常量、变量、函数等)”。1.由于Linux 驱动模块的初始化函数进行了某些操作而崩溃,从而导致初始化函数无法正常返回。这种情况的表现是当前Linux 驱动模块没被任何其他的Linux驱动模块使用,但却显示已经被引用了一次了一次。2.在使用rmmod 命令卸载Linux 驱动时,系统会调用卸载函数,只有卸载函数成功返回肘, Linux 驱动才会被翻载。如果卸载函数被阻塞(可能是死循环、并发等情况引起的阻塞), rmmod 命令也会被阻塞. 也就是说永远不会执行到卸载Linux 驱动模块的代码。这种情况的表现是一执行rmmod 命令就会停在那不动了,永远也不会返回到系统的操作提示符。
首先看第1 种情况。这种情况的关键是引用计数器的值和引用者不一致。实际上引用者是不存在的,因此,只需要将当前的Linux 驱动模块的引用计数器清零即可。修改引用计数器可以使用下面两个函数。
// 使module 指向的Linu x 驱动模焕的引用计数器加l ,成功返回1 ,失败返回0。
static inl 立ne int try_m。dule_get(struct module *module)
// 使module 指向的Linux 驱动模块的引用计数器减1。
extern void modul e_put (struct module *module);
第2 种情况的问题根源就是卸载函数,因此,只要将原来的卸载函数替换成一个空的卸载函数即可。
第九章 硬件抽象层:HAL
HAL ( Hardware Abstraction Layer,硬件抽象腔,〉是建立在Linux 驱动之上的一套翻字库。这套程序库并不属于Linux 内核,而是属于Linux 内核层之上的应用层。Google 为Android 增加HAL 的主要目
的除了尽量避免应用程序直接访问Linux 驱动外,还有一个重要原因,那就是保护“私人财产”。对于那些既想发布基于Android的Linux 驱动程序,又不想将核心业务逻辑公开的企业或个人,简直就是福音。
在传统的Linux系统中Linux驱动一般有两种类型的代码:访问硬件寄存器的代码和业务逻辑代码。对于访问硬件寄存器的代码,并没有什么秘密可言,因为这都是调用的Linux 内核的标准函数(ioread32 、iowrite32 等)进行的标准操作。而Linux 驱动的业务逻辑部分对击有些企业或个人并不想将源代码公开。尽管这些Linux 驱动都是免费给用户使用的,但由于这些Linux 驱动的实现涉及一些技术专利或商业秘密,如果公开源代码会有很大麻烦。但作为Linux.驱动,又不得不公开源代码。这是由于Linux 内核采用了GPL 协议,而GPL 协议要求所有使用基于GPL 协议的源代码的程序必须开源(由于Linux 驱动属于Linux 内核的一部分,因此Linux 驱动必须开源)。
总而言之,Google 为Android加入HAL主要有如下的目的:
1.统一硬件的调用接口。由于HAL 有标准的调用接卧,所以可以利用HAL屏蔽Linux 驱动复杂、不统一的接口。
2.解决了GPL版权问题。由于Linux 内核基于GPL协议,而Android 基于Apache Licence 2.0 协议。因此Google 玩了个“穿越”将原本位于Linux驱动中的敏感代码向上移了一个层次。这样这些敏感代码就摆脱了GPL 协议的束缚 。那些不想开源的Linux驱动作者也就没必要开源了。
3.针对一些特殊的要求。对于有些硬件,可能需要访问一些用户空间的资源,或在内核空间不方便完成的工作以及特殊需求。在这种情况下,可以利用位于用户空间的HAL 代码来辅助Linux驱动完成一些工作。
Google 为了满足这些不想开源的Linux 驱动作者的要求,在Android层次结构中的系统运行库层增加了一个HAL。HAL 并不是Linux 内核的一部分,而是位于Android的系统运行库层,而Android采用了Apache Licence2.0协议发布,Apache Licence2.0协议并未要求使用基于Apache Licence2.0协议的源代码的软件也必须开源。由于HAL属于Android的一部分,自然也不必开源了。
接下来是应用LED驱动的移植来演示支持HAL的linux驱动程序,让我们初步了解其步骤。
*编写调用HAL 模缺的Service
1.由于JNI 会用到很多C++代码,因此, LedHalService.cpp 不能改成LedHalService.c。
2.LedHalService.cpp 文件中引用了leds_hat.h头文件。该文件属于HAL 模块的源代码文件,己在Android.mk 文件中指leds__hat.h 文件所在的路径( hardware/leds_hal),在Android.mk 中指定的都属于系统路径,因此在include 该文件时要用尖括号(<...〉)。
3.JNI 方法在默认情况下有一定的命名规则(Java_packagename_classname_methodname),但可以通过RegisterNatives 自定义则JNI方法。该方法的最后一个参数需要指定要注册的JNI函数的个数。
4.利用slieof(methods)/sizeof(methods[0]计算methods数组长度,由于数组的每一个元素占用的字节个数是相等的
(JNINativeMethod类型,由3个指针组成,32位系统和64位系统每个数组元素占的字节数分别为12和24)。因此用整个数组占用的
字节数除以每一个数组元素占用的字节数(一定会被整除的)就是数组的长度。
5.methods数组的元素类型是JNINativeMethod。
注意:1.HAL 模块库文件的存放路径有两个: /system/lib/hw 和/vendor/lib/hw 。hw_get_ module 函数会先从/system/lib/hw 目录根据库文件命名规则寻找库文件。如果/system/lib/hw 目录中未找到库文件, hw_get_ module 会按同样的规则在/vendor/lib/hw 目录中寻找。
2.HAL 模块库文件的命名规则是ID.suffix.so。其中TD 通过hw_get_ module 函数的id 参数指定。suffix (后缀)通过属性文件指定。
3.hw _get_module 会在Android 系统的属性文件中根据variant_keys 数组中定义的4 个key依次查找suffix 。如果未找到suffix ,使用默认的suffix (default )。
Android 系统的属性文件共有如下4个:
/default.prop ;
/system/build.prop;
/system/default.prop;
/data/local.prop .
Android 在启动时会自动装载这些属性文件。如果在多个属性文件中都定义了同一个Key 和Value,那么只用第一个Key 被获取。例如,在/default.prop 文件中定义了ro.product.board 的值为abc,而在/system/build.prop 文件中定义了ro.product.board 的值为xyz。那么hw_get_ module 函数会把/default.prop 文件中的abc 作为HAL 模块库文件的后缀,而不会再读取/system/build.prop 文件中的xyz。
在Android系统中使用Linux 驱动有两种方式。一种就是通过传统的方式直接与Linux 驱动交互。例如,直接读写设备文件的数据。另外一种是Android特有的,就是通过HAL 模块 。HAL 模块本质上就是通过Linux 共享库(.so)与Linux驱动交互,然后应用程序再访问Linux共享库。早期的HAL模块由应用程序直接按访问Linux共字库的方式调用。而高版本的Android 系统为HAL增加了Stub 。换句话说,就是为每一个HAL共享库指定一个ID, 再利用这个ID 配合一定的规则找到Linux 共字库,这样Linux 共字库更换文件名,移动位置都很方便(因为HAL 共享库的路径和文件名都不是定死的),Google也建议使用Stub 的方式编写HAL模块。
第十章 嵌入式linux的调用技术
printk 函数在控制台(也称为终端)显示消息是通过/dev/console 设备文件实现的,该设备文件只在字符界面的控制台下才起作用p 所以printk 函数只有用在字符界面的控制台上才能正常输出消息。printk 函数在前面的章节己多次使用过。该函数的用法与printf 函数类似,只不过printk函数运行在内核空间, printf函数运自行在用户空间。也就是说,像Linux 驱动这样的Linux内核程序只能使用printk函数输出调用信息。
在Linux 文件系统中, /proc 经常被用来作为内核空间与用户空间进行数据交互的工具。/proc文件系统的行为方式与设备文件系统(/dev )类似。/proc 是虚拟文件系统,也就是说了,/proc 并不
是真正的文件系统,而是内存映射。所有读写/proc操作都是对内存的读写。 所以读写/proc 文件系统的速度要远比读写/dev 文件系统的速度快。因此,/proc 文件系统也可作为Linux 驱动与用户空间程序交互的工具。有很多系统信息就是通过/proc 文件系统由内核空间的程序向外界提供的。
*在Linux 系统中提供了一类工具。通过这些工具,可以逐行跟踪程序的代码,就好像可视化开发工具的step into 和step over 一样。
这些工具包含用于调试用户空间程序的gdb、gdbserver 和调试内核空间程序的kgdb。
标签:
原文地址:http://www.cnblogs.com/ls--blog/p/5654619.html