Android系统HAL驱动开发经典案例详解(基于Android4.0)
目的:通过学习一个LED点灯的简单功能,掌握Linux驱动程序与HAL硬件抽象层之间的调用方法,同时掌握JNI层的编写思想,学会使用Eclipse编写Android应用程序,深入体会Android HAL架构。本章内容主要参考文献:《Android深度探索(卷1) HAL与驱动开发》、《TQ210开发板Android_HAL_LED_V1.2.pdf》
目录:
一、架构分析
1.1 功能介绍
1.2 HAL架构
1.3 接口定义
二、驱动程序
2.1 驱动功能简介
2.2 驱动源码分析
2.3 利用可执行文件测试驱动
2.3.1 源文件
2.3.2 利用交叉编译器编译
2.3.3 使用原生C程序测试驱动
三、HAL硬件抽象层
3.1 HAL简介
3.2 HAL层源码与分析
3.3 HAL源码编译
四、Jni/Service服务层
4.1 直接使用JNI调用驱动程序
4.2 JNI/Service程序代码
五、APP应用程序层
5.1 activity_main.xml
5.2 LedServer.java
5.3 MainActivity.java
一、架构分析
1.1 功能介绍
Linux版本:3.0.8
Android版本:4.0
开发板:FriendlyARM smart210
功能介绍:应用程序界面如下图所示,为求方便,截图来自模拟器,实际开发板界面和下图是一样的。CheckBox复选框分别对应开发板上四个LED灯,选中复选框,按动led_hal_jni按键后,相应LED就会亮起来。功能很简单,代码中几乎不会涉及任何逻辑算法,主要是为了方便理清整个程序架构,体会Android系统通过HAL硬件抽象层与实际硬件之间交互的编程思想。
1.2 HAL架构
想要点亮一个灯当然简单,可以直接烧写ARM裸机程序,也可以在跑Linux系统的板子上通过写/dev/leds这样的设备文件来达到目的。Android系统为了解决一些调用接口和版权方面的问题提出了一个HAL架构。目前最新的HAL架构是下面这个样子的。
可以看出,Android应用程序直接调用的是JNI或者Service程序库文件(.so),程序库文件是通过一个ID来定位到相应的HAL的库文件(.so),最终和硬件打交道的是HAL模块。需要注意的是,HAL及其以上层均属于应用程序范畴,也就是都运行在了用户空间,只有Linux驱动程序运行在了Linux内核空间。
1.3 接口定义
从Android HAL架构来看,一套完整的点灯的程序应该包括四个层次。为了方便程序维护,层与层之间应该尽可能的降低耦合程度,每个层对其他层开放的仅仅是一个操作接口即可。每个层的源文件位置和其向其他层提供的关键接口表述如下,这里指的关键接口实际上就是“调用函数”。各个源文件的具体内容会在下文给出。如果你能亲自动手写完整套程序再回过头来看这部分接口定义,应该能体会得更深些。
1、底层驱动。
源文件位置:$(linux_source)/drivers/char/mini210_led.c
对应开发板上的驱动设备文件为:/dev/leds
关键接口定义:
//发送IO命令
ioctl(file_handler, cmd, arg);
//调用方法如下
./ioctl_test /dev/leds 1 2 //表示点亮第二盏灯
//发送写命令
write(fd,buf,strlen(buf));
//调用方法如下
./write_test /dev/s3c6410_leds "1111" // 点亮所有灯
这里的ioctl_test和write_test是两个可执行文件,它们分别是由ioctl_test.c和write_test.c源文件通过交叉编译器或Android原生代码编译出来的,这两个源文件是为了验证驱动程序专门写的,后文会贴出具体代码。
2、HAL层。
源文件位置:
//这个.c文件也可以放到其他目录
$(Android_source)/device/friendly-arm/mini210/libled/led_hal.c
//这个.h文件最好放到指定位置,以免包含头文件的时候发生错误
$(Android_source)/hardware/libhardware/include/hardware/led_hal.h
生成模块位置:Android_source/out/target/product/mini210/lib/hw/led_hal.default.so
关键接口定义:
int led_on(struct led_control_device_t *dev, int32_t LED_NUMBER) //表示LED_NUMBER灯亮
{
... ...
ioctl(fd,IOCTL_GPIO_ON,LED_NUMBER);
... ...
}
int led_off(struct led_control_device *dev, int32_t LED_NUMBER) //表示LED_NUMBER灯灭
{
... ...
ioctl(fd,IOCTL_GPIO_OFF,LED_NUMBER);
... ...
}
3、jni/service层
源文件位置:$(Android_source)/packages/apps/led/jni/LedHalService.cpp
生成模块位置:Android_source/out/target/product/mini210/lib/libled_hal_jni.so
关键接口定义:
jboolean Java_com_example_leds_MainActivity_ledSetOn (JNIEnv* env, jobject obj,jint number)
{
... ...
return sLedDevice->set_on(sLedDevice,number);
... ...
}
jboolean Java_com_example_leds_MainActivity_ledSetOff (JNIEnv* env, jobject obj,jint number)
{
... ...
return sLedDevice->set_off(sLedDevice,number);
... ...
}
类名定义:
//注意这里的kClassName决定了调用该JNI模块的应用程序的包名与类名。
int register_android_server_LedService(JNIEnv *env)
{
... ...
static const char* const kClassName = "com/example/leds/LedService";
... ...
}
二、驱动程序
2.1 驱动功能简介
mini210_led.c主要提供了mini210_leds_ioctl和mini210_leds_write两个接口。应用程序通过向/dev/leds发送I/O指令或写命令就可以实现控制LED灯的功能了。
2.2 驱动源码分析
<span style="font-family:KaiTi_GB2312;font-size:18px;">#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include &l