Linux驱动测试
来源:互联网 发布:淘宝什么时候成立的 编辑:程序博客网 时间:2024/05/18 14:27
转:http://www.cnblogs.com/nokiaguy/archive/2013/03/14/2960408.html
在上一篇文章中已经实现了一个简单的Linux驱动程序,该驱动的功能是统计给定字符串中的单词数。并且在最后已经将该Linux驱动的源代码成功编译成动态Linux驱动模块word_count.ko。下一步就是测试该模块。测试的方法很多,最常用的就是直接在Ubuntu Linux、Android模拟器中测试。当然,这对于本章实现的Linux驱动是没问题的,但是对于需要直接访问硬件的驱动在Ubuntu Linux上测试就不太方便。在这种情况下就需要在相应的硬件上进行测试。
对于一个Linux驱动程序,一开始可以在Ubuntu Linux上做前期开发和测试。对于访问硬件的部分也可以在Ubuntu Linux用软件进行模拟。当基本开发完成后,就需要在开发板或工程样机上使用真实的硬件进行测试。当然,最后还需要在最终销售的手机上进行测试。最终测 试通过,Linux驱动才能算真正开发完成。在开发Linux驱动的过程中一个重要的步骤就是测试。本节将结合实际的开发流程介绍在不同平台上测试Linux驱动程序。这些测试平台包括UbuntuLinux、Android模拟器和S3C6410开发板。
一、使用Ubuntu Linux测试Linux驱动
本节将介绍如何在Ubuntu Linux下测试驱动程序。由于上一节编写的Linux驱动程序通过4个字节从设备文件(/dev/wordcount)返回单词数,所以不能使用cat命令测试驱动程序(cat命令不会将这4个字节还原成int类型的值显示)。但可以使用如下命令从日志中查看单词数。
执行上面的命令后,如果输出如图6-13所示白框中的信息,说明驱动程序成功统计了单词数。
虽然使用echo和dmesg命令可以测试Linux驱动程序,但这种方式并不是真正的测试。为了使测试效果更接近真实环境,一般需要编写专门用于测试的程序。本节将为word_count驱动编写一个专门的测试程序(test_word_count.c)。test_word_count.c通过直接操作/dev/wordcount设备文件与word_count驱动进行交互。测试程序的代码如下:
执行上面的命令后,如果输出如图6-14所示的信息(假设word_count以前统计过一个含有4个单词的字符串),表示word_count驱动成功测试。
二、在Android模拟器上通过原生(Native)C程序测试Linux驱动
虽说我们开发的是Linux驱动,但本书主要介绍的是Android版的Linux内核,因此,Linux驱动只在Ubuntu Linux上测试成功还不能保证在Android设备上一定能正常工作,所以必须在Android设备上进行测试。Android设备有很多种类,如安装了Android的开发板,运行Android系统的手机或平板电脑等。但离我们最近的并不是这些硬件设备,而是Android模拟器。Android模拟器可以模拟绝大多数真实的环境,所以可以利用Android模拟器测试Linux内核。
在Android模拟器上测试Linux驱动首先应该想到的,也是最先应该做的就是将word_count.ko驱动模块安装在模拟器上。可能读者使用过adb shell命令。如果进入Android模拟器的命令提示符为“#”,说明通过命令行方式进入Android模拟器直接就是root权限(命令提示符为 “$”,表示非root权限),因此从理论上可以使用insmod命令将word_count.ko驱动模块直接安装在Android模拟器中。现在我们来测试一下,看看是否可以将word_count.ko安装在Android模拟器上。现在执行build.sh脚本,并选择“Android模拟器”,脚本会自动将word_count.ko文件上传到Android模拟器的/data/local目录,并进行安装。
如果读者选择的是S3C6410开发板,在安装word_count.ko时就会输出如下的错误信息,表示编译Linux驱动的Linux内核版本与当前Android模拟器的版本不相同,无法安装。所以在编译Linux驱动时,必须选择与当前运行的Linux内核版本相同的Linux内核进行编译,否则就无法安装Linux驱动。
注意:建议上传文件到Android模拟器或开发板时,将文件放到/data/local目录,系统很多其他的目录,如/system/bin,都是只读的,除非将word_count.ko文件打包进system.img,否则无法向这些目录写数据,即使有root权限也不行。
用于Android模拟器的goldfish内核默认不允许动态装载Linux驱动模块,因此需要在编译Linux内核之前执行如下命令配置Linux内核。
执行上面的命令后,会出现如图6-15所示的设置界面。按“空格”键将第二项“Enable loadable module support”选中(前面是[*]),然后按“回车”进入子菜单,选中前3项,如图6-16所示。否则Linux驱动模块仍然无法安装和卸载。当退出设 置菜单时保持设置。最后按4.2.3节的方法重新编译Linux内核,
成功编译内核后,Android模拟器可以使用新生成的zImage内核文件动态装载Linux驱动模块。
现在执行build.sh脚本文件完成对word_count驱动的编译、上传和安装的工作,然后进入Android模拟器的终端,使用echo和dmesg命令可以测试word_count驱动和查看测试结果,方法与上一节相同。
注意:编译可在Android模拟器上运行的Linux驱动模块要使用goldfish内核,使用其他的内核编译word_count.c,安装时会出现如下错误。
在Android模拟器上不仅可以使用Linux命令测试驱动,也可以像Ubuntu Linux一样使用本地C/C++程序进行测试。可能有的读者要问,Android不是只能运行由Java编写的APK程序吗?顶多是在APK程序中嵌入NDK代码。还能直接运行普通的Linux程序吗?答案是肯定的。不过要满足如下两个条件。
1. Android模拟器、开发板或手机需要有root权限。
2. 可执行文件需要使用交叉编译器进行编译,以便支持ARM处理器。
现在使用交叉编译器来编译在上一节编写的test_word_count.c文件。为了使编译步骤尽可能简单,我们使用Android.mk设置编译参 数,并使用make命令进行编译。首先在/root/drivers/ch06/word_count目录中建立一个Android.mk文件,并输入如下的内容。
Android.mk文件中有如下两个地方需要说明一下。
LOCAL_MODULE_TAGS
表示当前工程(Android.mk文件所在的目录)在什么模式下编译。如果设为optional,表示不考虑模式,也就是说在任何模式下都会编译。该变量可以设置的值有user、userdebug、eng、optional。其中eng是默认值。
1. user:限制用户对Android系统的访问,适合于发布产品。
2. userdebug:类似于user模式,但拥有root访问权限,并且可以从日志中获取大量的调试信息。
3. eng:一般在开发的过程中设置该模式。除了拥有userdebug的全部功能外,还会带有大量的调试工具。
LOCAL_MODULE_TAGS的值与TARGET_BUILD_VARIANT变量有关。TARGET_BUILD_VARIANT变量用于设置当前的编译模式,可设置的值包括 user、userdebug和eng。如果想改变编译模式,可以在编译Android源代码之前执行如下命令。
# export TARGET_BUILD_VARIANT = user
或使用lunch命令设置编译模式。
# lunch full-eng
其中full表示建立的目标,除了full目标(为所有的平台建立)外,还有专门为x86建立的full-x86。详细的建立目标执行lunch命令后就会列出。在图4-8已经显示了Android4支持的建立目标的编译模式。读者可以到第4章查看该图。
include $(BUILD_EXECUTABLE)
BUILD_EXECUTABLE表示建立可执行的文件。可执行文件路径是<Android源代码目录>/ out/target/product/generic/system/bin/test_word_count。如果想编译成动态库(.so)文件,可以使用include $(BUILD_SHARED_LIBRARY)。动态库的路径是<Android源代码目录>/ out/target/product/generic/system/lib/test_word_count.so。如果想编译成静态库(.a)文件,可以使用include $(BUILD_STATIC_LIBRARY)。静态库的路径是<Android源代码目录>/ out/target/product/generic/obj/STATIC_LIBRARIES/test_word_count_intermediates/test_word_count.
为了将test_word_count.c文件编译成可在Android模拟器上运行的可执行程序,可以将word_count目录复制到<Android源代码目录>的某个子目录,也可以在<Android源代码目录>目录中为word_count目录建立一个符号链接。为了方便,我们采用如下命令为word_count目录在<Android源代码目录>/development目录建立一个符号链接(假设Android源代码的目录是/sources/android/android4/development/word_count)。
# ln -s /root/drivers/ch06/word_count /sources/android/android4/development/word_count
现在进入/sources/android/android4目录,执行下面的命令初始化编译命令。
# source ./build/envsetup.sh
可以使用下面两种方法编译test_word_count.c。
1. 进入/sources/android/android4/development/word_count目录,并执行如下的命令。
# mm
2. 在/sources/android/android4目录下执行如下的命令。
# mmm development/word_count
成功编译后可以在<Android源代码目录>/out/target/product/generic/system/bin目录中找到test_word_count文件。在随书光盘和模拟环境中已经带了编译好的test_word_count程序(包括Emulator版本和 Ubuntu Linux版本),可执行程序一般不需要考虑Linux内核的版本,用交叉编译器编译的支持ARM处理器的程序即可以在Android模拟器上运行,也可 以在S3C6410开发板或其他有root权限的手机中运行。
Emulator版本的路径
随书光盘:<光盘根目录>/sources/ch06/word_count/emulator/test_word_count
模拟环境:/root/drivers/ch06/word_count/emulator/test_word_count
Ubuntu Linux版本的路径
随书光盘:<光盘根目录>/sources/ch06/word_count/ubuntu/test_word_count
模拟环境:/root/drivers/ch06/word_count/ubuntu/test_word_count
现在执行下面的命令将test_word_count文件上传到Android模拟器。
# adb push ./emulator/test_word_count /data/local
然后进入Android模拟器的终端,并执行下面的命令测试word_count驱动(需要先使用chmod命令设置test_word_count的可执行权限)。
# chmod 777 /data/local/test_word_count
# /data/local/test_word_count
# /data/local/test_word_count 'a bb ccc ddd eee'
执行上面的命令后,如果输出的单词个数是5,表示程序测试成功。
三、使用Android NDK测试Linux驱动
在Android系统中Linux驱动主要的使用者是APK程序。因此,Linux驱动做完后必须要用APK程序进行测试才能说明Linux驱动可以正常使用。由于上一节在Android虚拟机上使用C语言编写的可执行程序测试了Linux驱动,因此很容易想到可以利用Android NDK来测试Linux驱动,
由于Android NDK也使用C/C++来编写程序,因此可以利用上一节的C语言代码,当然,还得加上一些AndroidNDK特有的代码。在使用Android NDK测试Linux驱动之前需要做如下两件事。
1. 由于Linux驱动模块不会随Android系统启动而装载,因此必须执行build.sh脚本文件安装word_count驱动。
2. 不能使用默认方式启动Android模拟器,而要使用我们自己编译的Linux内核启动Android模拟器,启动模拟器的命令如下:
# emulator-avd myavd -kernel /root/kernel/goldfish/arch/arm/boot/zImage
为了方便,读者也可以在随书光盘带的Ubuntu Linux虚拟环境中直接执行如下的命令来异步启动Android模拟器。其中emulator.sh文件在/root/drivers目录中。
# sh emulator.sh &
本节的例子已经包含在随书光盘和虚拟环境中,路径如下:
随书光盘:<光盘根目录>/sources/ch06/word_count/word_count_ndk
虚拟环境:/root/drivers/ch06/word_count/word_count_ndk
word_count_ndk工程的代码部分由WordCountNDKTestMain.java和ndk_test_word_count.c文件组成。工程结构如图6-17所示。
ndk_test_word_count.c文件用于访问word_count驱动。该文件包含两个供Java访问的函数,分别用来读取/dev/wordcount设备文件中的单词数和向/dev/wordcount设备文件写入字符串。下面先看看ndk_test_word_count.c文件的完整代码。
#include <string.h> #include <jni.h> #include <fcntl.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdlib.h> // JNI函数:readWordCountFromDev // 用于从/dev/wordcount设备文件读取单词数 jint Java_mobile_android_word_count_ndk_WordCountNDKTestMain_readWordCountFromDev( JNIEnv* env, jobject thiz) { int dev; // open函数打开/dev/wordcount设备文件后返回的句柄,打开失败返回-1 jint wordcount = 0; // 单词数 unsigned char buf[4]; // 以4个字节形式存储的单词数 // 以只读方式打开/dev/wordcount设备文件 dev = open("/dev/wordcount", O_RDONLY); // 从dev/wordcount设备文件中读取单词数 read(dev, buf, 4); int n = 0; // 存储单词数的int类型变量 // 将由4个字节表示的单词数转换成int类型的值 n = ((int) buf[0]) << 24 | ((int) buf[1]) << 16 | ((int) buf[2]) << 8 | ((int) buf[3]); // 将int类型的单词数转换成jint类型的单词数 wordcount = (jint) n; // 关闭/dev/wordcount设备文件 close(dev); // 返回单词数 return wordcount; } // 将jstring类型的值转换成char *类型的值 char* jstring_to_pchar(JNIEnv* env, jstring str) { char* pstr = NULL; // 下面的代码会调用Java中的String.getBytes方法获取字符串的字节数 // 获取java.lang.String类 jclass clsstring = (*env)->FindClass(env, "java/lang/String"); // 将字符串“utf-8”转换成jstring类型的值 jstring strencode = (*env)->NewStringUTF(env, "utf-8"); // 获取java.lang.String.getBytes方法 jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B"); // 调用String.getBytes方法将str变量的值转换成jbytearray类型的值 jbyteArray byteArray = (jbyteArray)( (*env)->CallObjectMethod(env, str, mid, strencode)); // 获取字节长度 jsize size = (*env)->GetArrayLength(env, byteArray); // 将jbytearray类型的值转换成jbyte*类型的值 jbyte* pbyte = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE); if (size > 0) { // 为char*类型变量pstr分配空间 pstr = (char*) malloc(size); // 将pbyte变量中的值复制到pstr变量中 memcpy(pstr, pbyte, size); } // 返回转换后的值 return pstr; } // JNI函数:writeStringToDev // 用于向/dev/wordcount设备文件写入字符串 void Java_mobile_android_word_count_ndk_WordCountNDKTestMain_writeStringToDev( JNIEnv* env, jobject thiz, jstring str) { int dev; // open函数打开/dev/wordcount设备文件后返回的句柄,打开失败返回-1 // 以只写方式打开/dev/wordcount设备文件 dev = open("/dev/wordcount", O_WRONLY); // 将jstring类型字符串转换成char* 类型的值 char* pstr = jstring_to_pchar(env, str); if (pstr != NULL) { // 向/dev/wordcount设备文件写入字符串 write(dev,pstr, strlen(pstr)); } // 关闭/dev/wordcount设备文件 close(dev); }
编写上面的代码有一个重点就是jstring_to_pchar函数。该函数可以将jstring类型的数据转换成char*类型的数据。转换的基本思想就是调用Java方法String.getBytes,获取字符串对应的字节数组(jbyteArray)。由于write函数需要的是char *类型的数据,因此,还必须将jbyteArray类型的数据转换成char *类型的数据。采用的方法是先将jbyteArray类型的数据转换成jbyte类型的数据,然后调用memcpy函数将jbyte类型的数据复制到使用malloc函数分配的char*指针空间中。在jstring_to_pchar函数中有如下的一行代码。
jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B"];
看到getMethodID方法最后一个参数的值是"(Ljava/lang/String;)[B",可能Android NDK初学者会对此感到困惑,以为是写错了。实际上这是JNI(Android NDK程序实际上就是遵循JNI规则的程序)对方法参数和返回类型的描述。在JNI程序中为了方便描述Java数据类型,将简单类型使用了一个大写英文字 母表示,如表6-1所示。
除了表6-1所示的Java简单类型外,还有一些数据类型需要在JNI代码中与其对应。表6-2是这些数据类型在JNI中的描述符。
从表6-2所示的数据类型对照关系很容易想到本例中的"(Ljava/lang/String;)[B"是什么意思。jstring_to_pchar函数调用的是如下的getBytes方法的重载形式。
public byte[] getBytes(String charsetName) throwsUnsupportedEncodingException
在JNI中调用Java方法需要指定方法参数和返回值的数据类型。在JNI中的格式如下:
"(参数类型)返回值类型"
getBytes方法的参数类型是String,根据表6-2的描述,String类型中JNI在的描述符是"Ljava/lang/String; "。getBytes方法的返回值类型是byte[]。这里就涉及到一个数组的表示法。在JNI中数组使用左中括号([]表示,后面是数组中元素的类型。 每一维需要使用一个“[”。byte[]是一维字节数组,所以使用"[B"表示。如果是byte[][][],应使用"[[[B"表示。如果Java方法 未返回任何值(返回值类型是void),则用V表示。如void mymethod(int value)的参数和返回值类型可表示为"(I)V"。
Android NDK程序还需要一个Android.mk文件,代码如下:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := ndk_test_word_count LOCAL_SRC_FILES := ndk_test_word_count.c include $(BUILD_SHARED_LIBRARY)
在编写Java代码调用JNI函数之前,先看一下本例的界面,如图6-18所示。
读者需要先在PC上运行build.sh脚本文件安装word_count驱动。然后单击“从/dev/wordcount读取单词数”按钮,会在按钮下方输出当前/dev/wordcount设备文件中统计出的单词数。读者也可以在输入框中输入一个由空格分隔的字符串,然后单击“向/dev /wordcount写入字符串”按钮,再单击“从/dev/wordcount读取单词数”按钮,就会统计出字符串中包含的单词数,效果如图6-19所示。
下面看一下本例中Java部分(WordCountNDKTestMain.java)的完整代码。
如果Android拥有root权限,完全可以直接使用Java代码操作/dev/wordcount设备文件(没有root权限,Linux驱动模块是无法安装的)。本节将介绍如何使用Java代码来测试Linux驱动(测试程序不使用一行C/C++代码)。本节示例的路径如下:
随书光盘:<光盘根目录>/sources/ch06/word_count/word_count_java
虚拟环境:/root/drivers/ch06/word_count/word_count_java
word_count_java工程中只有一个源代码文件WordCountJavaTestMain.java。该文件的内容如下:
- Linux驱动:LED驱动测试
- Linux驱动:LED驱动测试
- Linux驱动测试
- linux 驱动 信号量 测试
- linux设备驱动之LED驱动测试
- Linux设备驱动之plat_led驱动测试
- linux 串口驱动测试代码
- Linux驱动:内核延时测试
- Linux驱动:信号量同步测试
- Linux驱动:互斥锁mutex测试
- Linux驱动:内核延时测试
- Linux驱动:信号量同步测试
- linux内核编译 驱动测试
- Linux触摸屏驱动测试程序
- Linux: 输入设备驱动测试程序
- Linux USB鼠标驱动注解及测试
- Linux驱动:阻塞式读写测试
- linux下初步测试binder驱动
- 大部分网站都容易被入侵吗?
- 黑马程序员01_基础知识和Java开发环境搭建
- 数据库的建立与链接问题
- Ubuntu 64位下解决“bash ./没有那个文件或目录”的问题
- 黑马高新技术 反射篇
- Linux驱动测试
- IOS开发百度地图API
- java SQL批处理
- MFC 控制窗口最大最小尺寸
- OPENVPN开启用户密码认证
- ASP.net,Jquery弹出一个子页面
- struts2注解下的拦截器配置
- 面向对象六大原则(五):迪米特法则
- NAT&Port Forwarding&Port Triggering