1.1 使用IOCTL测试LED
前面的实验中,我们通过sys文件系统,在sys/devices/platform/x210-led目录下建立了四个对应led的文件,通过改变这四个文件的状态控制LED灯。本章节我们重新编写驱动,另外再编写测试应用程序,通过IOCTL控制LED。
1.1.1 LED驱动
在kernel/drivers/char目录下建议led2目录,在led2目录下建立四个文件:
leddriver.c
leddriver.h
Makefile
Kconfig
注意,android4.0采用linux 3.0.8内核,该内核已经去掉了IOCTL的方法,替代方案是使用unlocked_ioctl,相比ioctl,它的参数少了inode,因此我们在编写驱动时,要做相应的调整。
详细的驱动源码请从光盘或从论坛下载。
修改kernel/drivers/char/Kconfig文件,添加如下代码:
source "drivers/char/led2/Kconfig"
修改kernel/drivers/char/Makefile文件,添加如下代码:
obj-y += led2/
再在android根目录重新编译内核:
./mk -ki
使用fastboot或SD卡更新内核,下面我们再编写测试应用程序。
1.1.2 LED测试应用程序
在android文件系统的external目录下新建led目录,并在led目录下建立两个文件:
Android.mk
x210_led_test.c
Android.mk内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= x210_led_test.c
LOCAL_MODULE := x210_led_test
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
Android.mk文件类似于linux中的makefile,用来向编译系统描述源代码。具体来说:该文件是GNU Makefile的一小部分,会被编译系统解析一次或多次。我们可以在每一个Android.mk文件中定义一个或多个模块,也可以在几个模块中使用同一个源代码文件。
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。
学习android开发,我们很有必要熟悉Android.mk的编写规范。这里是一个非常简单的Android.mk范例,我们一起来研究每一行的作用。
LOCAL_PATH :=$(call my-dir),一个Android.mk文件首先必须定义好LOCAL_PATH变量。它用于在开发树中查找源文件。在这个例子中,宏函数‘my-dir’,由编译系统提供,用于返回当前路径(即包含Android.mk文件的目录)。
include $(CLEAR_VARS),CLEAR_VARS由编译系统提供,指定让GNU MAKEFILE为你清除除LOCAL_PATH之外的所有LOCAL_XXX变量。这是必要的,因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的。
LOCAL_SRC_FILES:= x210_led_test.c,LOCAL_SRC_FILES变量必须包含将要编译打包进模块中的C或C++源代码文件。注意,我们不用在这里列出头文件和包含文件,因为编译系统会自动找出依赖型的文件。
LOCAL_MODULE := x210_led_test,LOCAL_MODULE变量必须定义,以标识在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'foo'的共享库模块,将会生成'libfoo.so'文件。这里表示我们编译出来的模块文件名称为x210_led_test。
LOCAL_MODULE_TAGS:= optional,LOCAL_MODULE_TAGS表示模块编译选项,具体可以定义为user,eng,tests,optional四种,分别对应如下:
user: 指该模块只在user版本下才编译
eng: 指该模块只在eng版本下才编译
tests: 指该模块只在tests版本下才编译
optional:指该模块在所有版本下都编译
这里所说的版本,对应我们lunch之后选择的版本,我们可以打开device/samsung/x210/vendorsetup.sh,里面就是我们可以选择的版本信息:
add_lunch_combo full_x210-userdebug
add_lunch_combo full_x210-eng
默认我们会使用full_x210-eng编译,因此这里我们可以将LOCAL_MODULE_TAGS定义为eng或是optional。
include $(BUILD_EXECUTABLE),表示以一个可执行程序的方式进行编译。当然我们也可以编译出一个静态库或动态库,这时BUILD_EXECUTABLE需要被替换为BUILD_STATIC_LIBRARY或BUILD_SHARED_LIBRARY。
include $(BUILD_STATIC_LIBRARY):表示编译成静态库
include $(BUILD_SHARED_LIBRARY):表示编译成动态库
include $(BUILD_EXECUTABLE):表示编译成可执行程序
以上三者的生成结果如下,generic依具体target会变:
out/target/product/generic/obj/EXECUTABLE
out/target/product/generic/obj/STATIC_LIBRARY
out/target/product/generic/obj/SHARED_LIBRARY
每个模块的目标文件夹分别为:
可执行程序:XXX_intermediates
静态库:XXX_static_intermediates
动态库:XXX_shared_intermediates
x210_led_test.c源码如下:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>// contact theopen(),close(),read(),write() and so on!
#define DEVICE_NAME "/dev/vib" //device point
#define LED_ON 0x11
#define LED_OFF 0x22
int main(int argc,char **argv)
{
int fd;
int ret;
char *i;
printf("\n x210 led test \r\n");
fd =open(DEVICE_NAME,O_RDWR);//Open device ,get the handle
printf("fd = %d \n",fd);
if(fd ==-1) //open fail
{
printf("open device %s error\n",DEVICE_NAME);
}
else
{
while(1)
{
ioctl(fd,LED_OFF); //call the output function to off LEDs
sleep(1);//wait 1 second
ioctl(fd,LED_ON);
sleep(1);
}
ret =close(fd); //close device
}
return 0;
}
这里DEVICE_NAME为LED驱动对应的设备节点,LED_ON和LED_OFF对应IOCTL的码值。
在main函数中,首先调用open函数打开LED驱动,然后通过IOCTL函数调用驱动中对应的IOCTL,从而实现LED的控制。
在android源码根目录下,执行如下指令编译LED测试应用程序:
make PRODUCT-full_x210-eng x210_led_test
这时,x210_led_test映像文件会生成到如下目录:
out/target/product/x210/obj/EXECUTABLES/x210_led_test_intermediates
同时,映像也会被拷贝到下面的目录:
out/target/product/x210/system/bin
1.1.3 运行测试程序
将映像文件x210_led_test拷贝到SD卡,再将SD卡插到开发板的SD2卡槽,通过指令将映像复制到/data目录并运行:
cp /sdcard/x210_led_test /data
cd /data
./x210_led_test
这时,LED灯就会不断闪烁。