Android HAL的作用及编程实例

来源:互联网 发布:腾讯 武汉软件新城 编辑:程序博客网 时间:2024/04/29 21:49

HAL介绍

Android HAL(硬件抽像层)是 Google 因应厂商「希望不公开源码」的要求下,所推出的新观念,其架构如下图。虽然 HAL 现在的「抽象程度」还不足,现阶段实作还不是全面符合 HAL 的架构规划,不过也确实给了我们很好的思考空间。

1Android HAL 架构规划

 

2Android HAL / libhardware_legacy

 

3Android HAL / libhardware

 

HAL 的未来发展?
新的 HAL 做法,倾向全面采用 JNI 的方式进行。也就是,在 Android 的架构中,修改 Android runtime 实作(即「Core Library」),在取得 HAL 模块的 operations 后再做 callback 操作。将 HAL 模块完全放在 HAL 里面。以上我想应该是针对framework开发来说的。如果仅是使用hal访问硬件,完全可以不修改core library

一、HAL使用步骤:

(1)Java AP 初始化一个 java service 然后根据需求组合调用 java service 提供的接口。

(2)Java Service 设置 Native Interface声明, 并在初始化时加载  Native Service 所在的库.Native Service 实际上是一个动态链接库, 通过JNI Java Service 交互。

(3)通过 OnLoad 方法注册与 Java Service Native Function 之间的对应 JNI table

(4)通过 HAL Module ID 获得当前实际板上对应的硬件设备的 Module 并通过此 Module HAL接口 Open 获得硬件设备的 device 实例。 通过device提供的接口组合本地函数的实现。

(5)编写 HAL stub, 对具体的硬件设备初始化对应 Module Device实例, 并实现对硬件驱动的API封装。

(6)HAL模块要以MODULE_ID.platform.so的名字存放在文件系统的/system/lib/hw/下面。

 

Android HAL层主要在hardware目录下,其中hardware/libhardware/下是同一用模块的概念来加载HAL.so库。 这里以一个简单的led小例子(假设备,不涉及硬件操作)来说明具体实现步骤。

二、HAL Stub 实现步骤(Implementation)

1. 设计自已的wrapper data structure

* 编写头文件led.h

* 定义 struct led_module_t

* 框架提供的struct hw_module_t struct hw_device_t必须放在第一个 field,并取名为 common

* 可参考 hardware/hardware.h

 

struct led_module_t {

     struct hw_module_t common;

     /* support API for LEDServices constructor */

};

 

2. led_module_t的意义

声明初始化时期(new object) supporting API、在 constructor里会使用到。

3. 定义led_control_device_t

声明控制时期的 supporting API、在 Manager API 里会使用到。

struct led_control_device_t {

   struct hw_device_t common;

   /* supporting control APIs go here */

   int (*getcount_led)(struct led_control_device_t *dev);

   int (*set_on)(struct led_control_device_t *dev);

   int (*set_off)(struct led_control_device_t *dev);

};

 

4. 每個 HAL stub 都要声明 module ID

#define LED_HARDWARE_MODULE_ID "led"

5. 声明 Stub operations 并实现 callback functions

Stub module 结构体必须取名为 HAL_MODULE_INFO_SYM,此名不可更改。

const struct led_module_t HAL_MODULE_INFO_SYM = {

    common: {

        tag: HARDWARE_MODULE_TAG,

        version_major: 1,

        version_minor: 0,

        id: LED_HARDWARE_MODULE_ID,

        name: "led HAL module",

        author: "gggggg",

        methods: &led_module_methods,

    },

    /* supporting APIs go here */

};

 

以下是具体的文件内容。

led.h文件:

#include <hardware/hardware.h>

 

#include <fcntl.h>

#include <errno.h>

 

#include <cutils/log.h>

#include <cutils/atomic.h>

 

#define LED_HARDWARE_MODULE_ID "led"

 

struct led_module_t {

     struct hw_module_t common;

     /* support API for LEDServices constructor */

};

 

struct led_control_device_t {

   struct hw_device_t common;

   /* supporting control APIs go here */

   int (*getcount_led)(struct led_control_device_t *dev);

   int (*set_on)(struct led_control_device_t *dev);

   int (*set_off)(struct led_control_device_t *dev);

};

 

struct led_control_context_t {

       struct led_control_device_t device;

};

led.c文件:

#define LOG_TAG "LedStub"

 

#include <hardware/hardware.h>

 

#include <fcntl.h>

#include <errno.h>

 

#include <cutils/log.h>

#include <cutils/atomic.h>

 

#include "../include/led.h"

 

static int led_device_close(struct hw_device_t* device)

{

       struct led_control_context_t* ctx = (struct led_control_context_t*)device;

       if (ctx) {

              free(ctx);

       }

       return 0;

}

 

static int led_getcount(struct led_control_device_t *dev)

{

       LOGI("led_getcount");

       return 4;

}

 

static int led_set_on(struct led_control_device_t *dev)

{    

       //FIXME: do system call to control gpio led

       LOGI("led_set_on");

       return 0;

}

 

static int led_set_off(struct led_control_device_t *dev)

{

       //FIXME: do system call to control gpio led

       LOGI("led_set_off");

       return 0;

}

 

static int led_device_open(const struct hw_module_t* module, const char* name,

        struct hw_device_t** device)

{

       struct led_control_context_t *context;

 

       LOGD("led_device_open");

 

       context = (struct led_control_context_t *)malloc(sizeof(*context));

       memset(context, 0, sizeof(*context));

 

       //HAL must init property

       context->device.common.tag= HARDWARE_DEVICE_TAG;

       context->device.common.version = 0;

       context->device.common.module= module;

       context->device.common.close = led_device_close;

 

       //初始化控制API

       context->device.set_on= led_set_on;

       context->device.set_off= led_set_off;

       context->device.getcount_led = led_getcount;

 

       *device= (struct hw_device_t *)&(context->device);

       return 0;

}

 

static struct hw_module_methods_t led_module_methods = {

    open: led_device_open  

};

 

const struct led_module_t HAL_MODULE_INFO_SYM = {

    common: {

        tag: HARDWARE_MODULE_TAG,

        version_major: 1,

        version_minor: 0,

        id: LED_HARDWARE_MODULE_ID,

        name: "led HAL module",

        author: "gggggg",

        methods: &led_module_methods,

    },

    /* supporting APIs go here */

};

 

Android.mk文件:

LOCAL_PATH := $(call my-dir)

 

include $(CLEAR_VARS)

 

LOCAL_PRELINK_MODULE := false

 

LOCAL_SHARED_LIBRARIES := liblog

 

LOCAL_SRC_FILES := led.c

 

LOCAL_MODULE := led.goldfish

 

include $(BUILD_SHARED_LIBRARY)

 

三、在Eclipse中建一个工程使用我们编写的led stub

1.定义两个类,源码如下:

Myhal.java文件:

package com.hello.MyHal;

 

import android.app.Activity;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.widget.Button;

 

import com.hello.LedService.LedService;;

 

public class Myhal extends Activity implements View.OnClickListener {

 

    static LedService led_srv;

 

    static Button btn;

 

    static boolean iflag = false;

 

    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

 

        Log.i("Java App", "OnCreate");

        led_srv = new LedService();

        Log.i("Java App", "Load Java Serivce");

 

        btn = (Button)this.findViewById(R.id.mybtn);

        btn.setOnClickListener(this);

    }

 

    public void onClick(View v) {

 

        Log.i("Java App", "btnOnClicked");

        String title = new String();

        if (iflag) {

            title = led_srv.set_off();

            btn.setText("Turn On");

            setTitle(title);

            iflag = false;

        } else {

            title = led_srv.set_on();

            btn.setText("Turn Off");

            setTitle(title);

            iflag = true;

        }

    }

}

 

LedService.java文件:

package com.hello.LedService;

 

import android.util.Log;

 

public final class LedService {

 

    /*

     * load native service.

     */

    static {

        Log.i("Java Service", "Load Native Serivce LIB");

        System.loadLibrary("led_runtime");

    }

 

    public LedService() {

        int icount;

        Log.i("Java Service", "do init Native Call");

        _init();

        icount = _get_count();

        Log.d("Java Service", "Init OK ");

    }

 

    /*

     * LED native methods.

     */

    public String set_on() {

        Log.i("com.hello.LedService", "LED On");

        _set_on();

        return "led on";

    }

 

    public String set_off() {

        Log.i("com.hello.LedService", "LED Off");

        _set_off();

        return "led off";

    }

 

    /*

     * declare all the native interface.

     */

    private static native boolean _init();

 

    private static native int _set_on();

 

    private static native int _set_off();

 

    private static native int _get_count();

 

}

其中LedService类通过native函数使用led stub提供的功能。

 

2.接下来我们实现通过jni接口实现native方法。

这里无需使用javah生成相应的头文件。

com_hello_LedService.cpp文件:

#define LOG_TAG "LedService"

#include "utils/Log.h"

 

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <assert.h>

#include <jni.h>

 

#include "../../led_stub/include/led.h"

 

static led_control_device_t *sLedDevice = 0;

static led_module_t* sLedModule=0;

 

static int get_count(void)

{

       return 0;

}

 

static jint led_setOn(JNIEnv* env, jobject thiz) {

    //if (sLedDevice) {

              LOGI("led_set_on");

           sLedDevice->set_on(sLedDevice);

       //}

      

       return 0;

}

 

static jint led_setOff(JNIEnv* env, jobject thiz) {

 

       //if (sLedDevice) {

              LOGI("led_set_off");

           sLedDevice->set_off(sLedDevice);

       //}

 

       return 0;

}

 

/** helper APIs */

static inline int led_control_open(const struct hw_module_t* module,

        struct led_control_device_t** device) {

 

       LOGI("led_control_ope");

    return module->methods->open(module,

            LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device);

}

 

static jint led_init(JNIEnv *env, jclass clazz)

{

       led_module_t const * module;

       LOGI("led_init");   

 

    if (hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) {

              LOGI("get Module OK");     

              sLedModule = (led_module_t *) module;

        if (led_control_open(&module->common, &sLedDevice) != 0) {

                     LOGI("led_init error");

            return -1;

        }

    }

 

       LOGI("led_init success");

    return 0;

}

 

/*

 * Array of methods.

 *

 * Each entry has three fields: the name of the method, the method

 * signature, and a pointer to the native implementation.

 */

static const JNINativeMethod gMethods[] = {

    {"_init",     "()Z",

                     (void*)led_init},

    { "_set_on",          "()I",

                        (void*)led_setOn },

    { "_set_off",          "()I",

                        (void*)led_setOff },

    { "_get_count",          "()I",

                        (void*)get_count },

      

};

 

static int registerMethods(JNIEnv* env) {

    static const char* const kClassName =

        "com/hello/LedService/LedService";

    jclass clazz;

 

    /* look up the class */

    clazz = env->FindClass(kClassName);

    if (clazz == NULL) {

        LOGE("Can't find class %s/n", kClassName);

        return -1;

    }

 

    /* register all the methods */

    if (env->RegisterNatives(clazz, gMethods,

            sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)

    {

        LOGE("Failed registering methods for %s/n", kClassName);

        return -1;

    }

 

    /* fill out the rest of the ID cache */

    return 0;

}

 

/*

 * This is called by the VM when the shared library is first loaded.

 */

jint JNI_OnLoad(JavaVM* vm, void* reserved) {

    JNIEnv* env = NULL;

    jint result = -1;

       LOGI("JNI_OnLoad");

 

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {

        LOGE("ERROR: GetEnv failed/n");

        goto fail;

    }

    assert(env != NULL);

 

    if (registerMethods(env) != 0) {

        LOGE("ERROR: PlatformLibrary native registration failed/n");

        goto fail;

    }

 

    /* success -- return valid version number */

    result = JNI_VERSION_1_4;

 

fail:

    return result;

}

 

Android.mk文件:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

 

#LOCAL_MODULE_TAGS := eng

 

# This is the target being built.

LOCAL_MODULE:= libled_runtime

 

# All of the source files that we will compile.

LOCAL_SRC_FILES:= /

       com_hello_LedService.cpp

 

# All of the shared libraries we link against.

LOCAL_SHARED_LIBRARIES := /

       libandroid_runtime /

       libnativehelper /

       libcutils /

       libutils /

       libhardware

 

# No static libraries.

LOCAL_STATIC_LIBRARIES :=

 

# Also need the JNI headers.

LOCAL_C_INCLUDES += /

       $(JNI_H_INCLUDE)

# No specia compiler flags.

LOCAL_CFLAGS +=

 

# Don't prelink this library.  For more efficient code, you may want

# to add this library to the prelink map and set this to true.

LOCAL_PRELINK_MODULE := false

 

include $(BUILD_SHARED_LIBRARY)

 

Led stubLedService相关文件放到development/my_module分别make即可生成相应的so文件。使用adb push命令安装到虚拟机上运行即可。

文件组织机构如下:

a@ubuntu:~/work/android/source_android/development/my_module$ pwd

/home/a/work/android/source_android/development/my_module

a@ubuntu:~/work/android/source_android/development/my_module$ tree hal

hal

|-- LedService

|   `-- jni

|       |-- Android.mk

|       `-- com_hello_LedService.cpp

`-- led_stub

    |-- include

    |   `-- led.h

    `-- module

        |-- Android.mk

        `-- led.c

 

6 directories, 7 files

四、编译运行中遇到的问题及解决方法:

1.usr/bin/ld: cannot find -lz

collect2: ld 返回 1

只是库命名的问题,简单的做了个软链接,一切搞定

ln -svf /lib/libz.so.1 /lib/libz.so

这个库文件与软连接的命名只差了一个.1

2.frameworks/base/tools/aidl/AST.cpp:10: error: 'fprintf' was not declared in this scope的错误

下载gcc-4.3g++-4.3

apt-get install gcc-4.3 g++-4.3

大约十多兆,然后

进入/usr/bin

cd /usr/bin

建个软连接

ln -s gcc-4.3 gcc

ln -s g++-4.3 g++

33./bin/bash: flex:找不到命令

make: *** [out/host/linux-x86/obj/EXECUTABLES/aidl_intermediates/aidl_language_l.cpp] 错误 127

a@ubuntu:~/work/android/source_android$ sudo apt-get install flex

4.JNI.so文件放到/system/lib下,而hal moudule需要放到/system/lib/hw下,且命名需符合约定,egled.goldfish.so

http://blog.chinaunix.net/u/22630/showart_2190346.html

现在的 libhardware 作法,就有「stub」的味道了。HAL stub 是一种代理人(proxy)的概念,stub 虽然仍是以 *.so 檔的形式存在,但 HAL 已经将 *.so 档隐藏起来了。Stub HAL「提供」操作函数(operations),而 runtime 则是向 HAL 取得特定模块(stub)的 operations,再 callback 这些操作函数。这种以 indirect function call 的实作架构,让 HAL stub 变成是一种「包含」关系,即 HAL 里包含了许许多多的 stub(代理人)。Runtime 只要说明「类型」,即 module ID,就可以取得操作函数。HAL的实现主要在hardware.chardware.h文件中。实质也是通过加载 *.so 档(dlopen)从而呼叫 *.so 里的符号(symbol)实现。这里所谓的代理,我感觉不过是Android统一定义了三个结构体,然后通过几个“必须”从而统一了调用接口
过去的 libhardware_legacy 作法,比较是传统的「module」方式,也就是将 *.so 档案当做「shared library」来使用,在 runtimeJNI 部份)以 direct function call 使用 HAL module。透过直接函数呼叫的方式,来操作驱动程序。
当然,应用程序也可以不需要透过 JNI 的方式进行,直接以加载 *.so 檔(dlopen)的做法呼叫 *.so 里的符号(symbol)也是一种方式。
HAL 的现实状况
这是 Patrick Brady (Google) 2008 Google I/O 所发表的演讲「Anatomy & Physiology of an Android」中,所提出的 Android HAL 架构图。从这张架构图我们知道,HAL 的目的是为了把 Android framework Linux kernel 完整「隔开」。让 Android 不至过度依赖 Linux kernel,有点像是「kernel independent」的意思,让 Android framework 的开发能在不考虑驱动程序的前提下进行发展。
Android 原始码里,HAL 主要的实作储存于以下目录:
1. libhardware_legacy/ - 过去的实作、采取链接库模块的观念进行
2. libhardware/ - 新版的实作、调整为 HAL stub 的观念
3. ril/ - Radio Interface Layer
HAL 的架构实作成熟前(即图1的规划),我们先就目前 HAL 现况做一个简单的分析。另外,目前 Android HAL 实作,仍旧散布在不同的地方,例如 CameraWiFi 等,因此上述的目录并不包含所有的 HAL 程序代码。
HAL 的过去
原创粉丝点击