1. 程式人生 > >Android驅動(一)硬體訪問服務學習之(三)Android加入HAL層訪問硬體

Android驅動(一)硬體訪問服務學習之(三)Android加入HAL層訪問硬體

  • 硬體平臺:tiny4412
  • 系統:Android  5.0.2
  • 編譯器: arm-linux-gcc-4.5.1

當時我們把對硬體的操作放在了JNI層,但是Android並不是這樣,google提出HAL層,即硬體封裝層

這一節我們把硬體的操作封裝裝HAL層。


android HAL是什麼?為什麼有它?來看一下網上的說法:

硬體抽象層是介於android核心kernel和上層之間的抽象出來的一層結構。他是對linux驅動的一個封裝,對上層提供統一介面,上層應用不必知道下層硬體具體怎麼實現工作的,它遮蔽了底層的實現細節。

它在整個android架構中的位置如下圖所示:


傳統的linux對硬體的操作基本上在核心空間的linux驅動程式中實現了,那麼現在為什麼那麼多此一舉把對硬體的操作分為兩部分,hal和linux驅動呢?

而且hal屬於使用者空間,linux驅動屬於核心空間。其實並不多餘。那麼為什麼要高出這麼個東西,理由是很多的:

1.谷歌搭好了hal的框架,為上層framework打通過jni呼叫hal提供了統一的api,硬體開發商或者移植人員只需要按照框架開發即可,無需話費精力在與上層的互動上的實現上,將精力放在hal層本身的實現上即可。

2.從商業角度,許多硬體廠商不願意將自己硬體相關一些核心的東西開源出去,假如將對自己硬體的驅動程式全部放入核心空間驅動程式實現,那麼必須遵循GPL協議,是必需開源的。有了HAL層之後,他們可以把一些核心的演算法之類的東西的實現放在HAL層,而hal層位於使用者空間,不屬於linux核心,和android原始碼一樣遵循的是appache協議,這個是可以開源或者不開的。

接下來我們學習一下HAL,即硬體抽象層

一、HAL的思路

1、整體思路

(1)應用程式不會直接訪問硬體,硬體訪問由SystemServer訪問,SystemServer中由JNI訪問HAL層。

(2)JNI 向上提供本地函式, 向下載入HAL檔案並呼叫HAL的函式

(3)HAL 負責訪問驅動程式執行硬體操作(更加保密安全,方便,避開GPL協議)

(4)JNI載入HAL的實質就是如何使用dlopen載入動態庫。

(5)android對dlopen進行封裝,使用hw_get_module。

2、詳細分析

(1)搜尋:hw_get_module開啟如下

燈光系統的JNI檔案:
com_android_server_lights_LightsService.cpp(frameworks\base\services\core\jni)


(2)找到hw_get_module


(3)雙擊進去hw_get_module,最終呼叫hw_get_module_by_class("led",NULL)

int hw_get_module(const char *id, const struct hw_module_t **module)
{
    return hw_get_module_by_class(id, NULL, module);
}

點選hw_get_module_by_class

hw_get_module_by_class("led",NULL)

--》strlcpy(name, class_id, PATH_MAX);所以name= "led"

int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module)
{
    int i;
    char prop[PATH_MAX];
    char path[PATH_MAX];
    char name[PATH_MAX];
    char prop_name[PATH_MAX];

    if (inst)
        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
    else
        strlcpy(name, class_id, PATH_MAX);                                                   //name=led

    /*
     * Here we rely on the fact that calling dlopen multiple times on
     * the same .so will simply increment a refcount (and not load
     * a new copy of the library).
     * We also assume that dlopen() is thread-safe.
     */

    /* First try a property specific to the class and possibly instance */
    snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
    if (property_get(prop_name, prop, NULL) > 0) {
        if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
            goto found;
        }
    }

    /* Loop through the configuration variants looking for a module */
    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
        if (property_get(variant_keys[i], prop, NULL) == 0) {
            continue;
        }
        if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
            goto found;
        }
    }

    /* Nothing found, try the default */
    if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
        goto found;
    }

    return -ENOENT;

found:
    /* load the module, if this fails, we're doomed, and we should not try
     * to load a different variant. */
    return load(class_id, path, module);
}

(4)主要用到了兩個函式,property_gethw_module_exists

hw_module_exists:用來判斷 "name"."subname".so在三個固定的目錄中是否存在。

name是上層hw_get_module_by_class傳進來的name=led,即判斷led."subname".so是否存在。subname也是上層函式中提供的prop,即獲取到的屬性值。這裡暫且認為判斷某個SO是否存在。

a.獲取環境變數路徑:char *hal_library_path =getenv("HAL_LIBRARY_PATH");        

得到環境變數下的決對路徑:snprintf(path, path_len, "%s/%s.%s.so",

                 hal_library_path, name, subname);

 判斷是否存在if (access(path, R_OK) == 0)
            return 0;

b.#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"

同理判斷 /vendor/lib/hw/led."subname".so是否存在

c.#define HAL_LIBRARY_PATH1 "/system/lib/hw"

同理判斷 /system/lib/hw/led."subname".so是否存在

static int hw_module_exists(char *path, size_t path_len, const char *name,
                            const char *subname)
{
    char *hal_library_path = getenv("HAL_LIBRARY_PATH");
    if (hal_library_path) {
        snprintf(path, path_len, "%s/%s.%s.so",
                 hal_library_path, name, subname);
        if (access(path, R_OK) == 0)
            return 0;
    }

    snprintf(path, path_len, "%s/%s.%s.so",
             HAL_LIBRARY_PATH2, name, subname);
    if (access(path, R_OK) == 0)
        return 0;

    snprintf(path, path_len, "%s/%s.%s.so",
             HAL_LIBRARY_PATH1, name, subname);
    if (access(path, R_OK) == 0)
        return 0;

    return -ENOENT;
}


顯然4412只會去/system/lib/hw下查詢"name"."subname".so檔案是否存在。

property_get:android的屬性系統。屬性就是鍵值對,根據名字獲取一個值。

property_get(prop_name, prop, NULL):prop_name:鍵      prop:獲取的值

hw_get_module_by_class中property_get被多次呼叫。

第一次prop_name="ro.hardware.%s", name

之後:prop_name=

static const char *variant_keys[] = {
    "ro.hardware",  /* This goes first so that it can pick up a different
                       file on the emulator. */
    "ro.product.board",
    "ro.board.platform",
    "ro.arch"
};

軟屬性存在,在判斷led.prop.so是否在三個目錄存在。

如果都沒有存在,則判斷:led.default.so是否存在。

通過最後的Android.mk編譯檔案,我們知道,我們的檔案會被編譯成led.default.so,且放在 /system/lib/hw目錄下。

(5)hw_get_module_by_class最終會呼叫load載入C庫
found:
    /* load the module, if this fails, we're doomed, and we should not try
     * to load a different variant. */
    return load(class_id, path, module);

(6)dlopen+dlsym獲取hw_module_t結構體。

handle = dlopen(path, RTLD_NOW);//載入C庫

const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_module_t *)dlsym(handle, sym);// 載入後用dlsym獲取HMI符號,
這個符號轉換為hw_module_t結構體。從So檔案中,獲取名為HMI的結構體,判斷結構體名和"led"是否一致,一致則找到了這個模組,最後把模組賦給    *pHmi = hmi;變數 pHmi是呼叫者傳進來的。

static int load(const char *id,
        const char *path,
        const struct hw_module_t **pHmi)
{
    int status;
    void *handle;
    struct hw_module_t *hmi;

    /*
     * load the symbols resolving undefined symbols before
     * dlopen returns. Since RTLD_GLOBAL is not or'd in with
     * RTLD_NOW the external symbols will not be global
     */
    handle = dlopen(path, RTLD_NOW);
    if (handle == NULL) {
        char const *err_str = dlerror();
        ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
        status = -EINVAL;
        goto done;
    }

    /* Get the address of the struct hal_module_info. */
    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
    hmi = (struct hw_module_t *)dlsym(handle, sym);
    if (hmi == NULL) {
        ALOGE("load: couldn't find symbol %s", sym);
        status = -EINVAL;
        goto done;
    }

    /* Check that the id matches */
    if (strcmp(id, hmi->id) != 0) {
        ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
        status = -EINVAL;
        goto done;
    }

    hmi->dso = handle;

    /* success */
    status = 0;

    done:
    if (status != 0) {
        hmi = NULL;
        if (handle != NULL) {
            dlclose(handle);
            handle = NULL;
        }
    } else {
        ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
                id, path, *pHmi, handle);
    }

    *pHmi = hmi;

    return status;
}
(7)JNI 怎麼使用 HAL

a.    JNI 呼叫hw_get_module通過模組的名字"led"開啟某個so檔案獲得一個hw_module_t結構體

b. 之後通過get_device(module,LIGHT_ID_BACKLIGHT);傳入一個裝置的名字,從hw_get_module中獲取這個裝置hw_device_t

呼叫module->methods->open(module, device_name, &device)獲得一個hw_device_t結構體,並且把hw_device_t結構體轉換為裝置自定義的結構體light_device_t。

light_device_t第一個成員是hw_device_t,可以型別轉換。

static light_device_t* get_device(hw_module_t* module, char const* name)
{
    int err;
    hw_device_t* device;
    err = module->methods->open(module, name, &device);
    if (err == 0) {
        return (light_device_t*)device;
    } else {
        return NULL;
    }
}

(8)HAL 怎麼寫

a. 實現一個名為HMI的hw_module_t結構體

b. 實現一個open函式, 它會根據device_name返回一個裝置自定義的結構體

c.實現一個裝置結構體,這個裝置自定義的結構體的第1個成員是 hw_device_t結構體,還可以定義裝置相關的成員。如:

struct led_device_t {
    struct hw_device_t common;
	int (*led_open)(struct led_device_t* dev);
	int (*led_ctrl)(struct led_device_t* dev, int which, int status);
};
(9)說了半天模組的結構體,下面來看一下HAL層重要的幾個結構體

hw_module_t結構體、hw_module_methods_t結構體、hw_device_t結構體

hw_module_t:

表示硬體模組,它主要包含了一些硬體模組的資訊,結構體的定義:

裡面hw_module_methods_t,這個指標methods它指向的是與本硬體模組相關的方法的結構體

typedef struct hw_module_t {
    /** tag must be initialized to HARDWARE_MODULE_TAG */
    uint32_t tag;

    /**
     * The API version of the implemented module. The module owner is
     * responsible for updating the version when a module interface has
     * changed.
     *
     * The derived modules such as gralloc and audio own and manage this field.
     * The module user must interpret the version field to decide whether or
     * not to inter-operate with the supplied module implementation.
     * For example, SurfaceFlinger is responsible for making sure that
     * it knows how to manage different versions of the gralloc-module API,
     * and AudioFlinger must know how to do the same for audio-module API.
     *
     * The module API version should include a major and a minor component.
     * For example, version 1.0 could be represented as 0x0100. This format
     * implies that versions 0x0100-0x01ff are all API-compatible.
     *
     * In the future, libhardware will expose a hw_get_module_version()
     * (or equivalent) function that will take minimum/maximum supported
     * versions as arguments and would be able to reject modules with
     * versions outside of the supplied range.
     */
    uint16_t module_api_version;
#define version_major module_api_version
    /**
     * version_major/version_minor defines are supplied here for temporary
     * source code compatibility. They will be removed in the next version.
     * ALL clients must convert to the new version format.
     */

    /**
     * The API version of the HAL module interface. This is meant to
     * version the hw_module_t, hw_module_methods_t, and hw_device_t
     * structures and definitions.
     *
     * The HAL interface owns this field. Module users/implementations
     * must NOT rely on this value for version information.
     *
     * Presently, 0 is the only valid value.
     */
    uint16_t hal_api_version;
#define version_minor hal_api_version

    /** Identifier of module */
    const char *id;

    /** Name of this module */
    const char *name;

    /** Author/owner/implementor of the module */
    const char *author;

    /** Modules methods */
    struct hw_module_methods_t* methods;

    /** module's dso */
    void* dso;

#ifdef __LP64__
    uint64_t reserved[32-7];
#else
    /** padding to 128 bytes, reserved for future use */
    uint32_t reserved[32-7];
#endif

} hw_module_t;

hw_module_methods_t:

函式指標open它是開啟硬體模組中硬體裝置的函式

typedef struct hw_module_methods_t {
    /** Open a specific device */
    int (*open)(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device);

} hw_module_methods_t;

hw_device_t:

來描述模組中硬體裝置的屬性資訊。一個硬體模組可能有多個硬體裝置。

比如說,感測器模組,sensor_module,是一個硬體模組,但是手機中的感測器就對應的有好多種,比如加速度acc_sensor,磁感測器M_sensor等,那麼他們都屬於sensor_module,但是他們有都有自己的hw_device_t結構體來描述。

module指向的是這個裝置歸屬的硬體模組結構體。
函式指標close指向的是關閉裝置的函式。

typedef struct hw_device_t {
    /** tag must be initialized to HARDWARE_DEVICE_TAG */
    uint32_t tag;

    /**
     * Version of the module-specific device API. This value is used by
     * the derived-module user to manage different device implementations.
     *
     * The module user is responsible for checking the module_api_version
     * and device version fields to ensure that the user is capable of
     * communicating with the specific module implementation.
     *
     * One module can support multiple devices with different versions. This
     * can be useful when a device interface changes in an incompatible way
     * but it is still necessary to support older implementations at the same
     * time. One such example is the Camera 2.0 API.
     *
     * This field is interpreted by the module user and is ignored by the
     * HAL interface itself.
     */
    uint32_t version;

    /** reference to the module this device belongs to */
    struct hw_module_t* module;

    /** padding reserved for future use */
#ifdef __LP64__
    uint64_t reserved[12];
#else
    uint32_t reserved[12];
#endif

    /** Close this device */
    int (*close)(struct hw_device_t* device);

} hw_device_t;

二、程式碼實現:

(1)JNI檔案實現(frameworks/base/services/core/jni/com_android_server_LedService.cpp

#define LOG_TAG "LedService"

#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"

#include <utils/misc.h>
#include <utils/Log.h>

#include <stdio.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <hardware/led_hal.h>


namespace android
{

static led_device_t* led_device;

jint ledOpen(JNIEnv *env, jobject cls)
{
	jint err;
    hw_module_t* module;
	hw_device_t* device;

	ALOGI("native ledOpen ...");

	/* 1. hw_get_module */
    err = hw_get_module("led", (hw_module_t const**)&module);
    if (err == 0) {
		/* 2. get device : module->methods->open */
	    err = module->methods->open(module, NULL, &device);
	    if (err == 0) {
			/* 3. call led_open */
	        led_device = (led_device_t *)device;
			return led_device->led_open(led_device);
	    } else {
	        return -1;
    	}
    }
	
    return -1;	
}

void ledClose(JNIEnv *env, jobject cls)
{
	//ALOGI("native ledClose ...");
	//close(fd);
}


jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status)
{
	ALOGI("native ledCtrl %d, %d", which, status);
	return led_device->led_ctrl(led_device, which, status);
}


static const JNINativeMethod methods[] = {
	{"native_ledOpen", "()I", (void *)ledOpen},
	{"native_ledClose", "()V", (void *)ledClose},
	{"native_ledCtrl", "(II)I", (void *)ledCtrl},
};
	

int register_android_server_LedService(JNIEnv *env)
{
    return jniRegisterNativeMethods(env, "com/android/server/LedService",
            methods, NELEM(methods));
}

}
(2)HAL程式碼實現

hardware/libhardware/include/hardware/led_hal.h

hardware/libhardware/modules/led/led_hal.c

hardware/libhardware/modules/led/Android.mk

led_hal.c:

把硬體檔案加入工程,參考振動器


拷貝標頭檔案


/* 1. 實現一個名為HMI的hw_module_t結構體 */

/* 2. 實現一個open函式, 它返回led_device_t結構體*/

/* 3. 實現led_device_t結構體 */

/* 參考hardware\libhardware\modules\vibrator\vibrator.c

 */


static struct hw_module_methods_tled_module_methods = {

   .open = led_device_open,

};

struct hw_module_t HAL_MODULE_INFO_SYM = {

   .id = "led",

    .methods = &led_module_methods,

};


#define LOG_TAG "LedHal"


/* 1. 實現一個名為HMI的hw_module_t結構體 */

/* 2. 實現一個open函式, 它返回led_device_t結構體 */

/* 3. 實現led_device_t結構體 */

/* 參考 hardware\libhardware\modules\vibrator\vibrator.c
 */

#include <hardware/vibrator.h>
#include <hardware/hardware.h>

#include <cutils/log.h>

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <hardware/led_hal.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <utils/Log.h>


static int fd;


/** Close this device */
static int led_close(struct hw_device_t* device)
{
	close(fd);
	return 0;
}

static int led_open(struct led_device_t* dev)
{
	fd = open("/dev/leds", O_RDWR);
	ALOGI("led_open : %d", fd);
	if (fd >= 0)
		return 0;
	else
		return -1;
}

static int led_ctrl(struct led_device_t* dev, int which, int status)
{
	int ret = ioctl(fd, status, which);
	ALOGI("led_ctrl : %d, %d, %d", which, status, ret);
	return ret;
}




static struct led_device_t led_dev = {
	.common = {
		.tag   = HARDWARE_DEVICE_TAG,
		.close = led_close,
	},
	.led_open  = led_open,
	.led_ctrl  = led_ctrl,
};

static int led_device_open(const struct hw_module_t* module, const char* id,
        struct hw_device_t** device)
{
	*device = &led_dev;
	return 0;
}


static struct hw_module_methods_t led_module_methods = {
    .open = led_device_open,
};

struct hw_module_t HAL_MODULE_INFO_SYM = {
	.tag = HARDWARE_MODULE_TAG,
    .id = "led",
    .methods = &led_module_methods,
};

led_hal.h:
#ifndef ANDROID_LED_INTERFACE_H
#define ANDROID_LED_INTERFACE_H

#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>

#include <hardware/hardware.h>

__BEGIN_DECLS

struct led_device_t {
    struct hw_device_t common;

	int (*led_open)(struct led_device_t* dev);
	int (*led_ctrl)(struct led_device_t* dev, int which, int status);
};


__END_DECLS

#endif  // ANDROID_LED_INTERFACE_H


Android.mk
# Copyright (C) 2012 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := led.default

# HAL module implementation stored in
# hw/<VIBRATOR_HARDWARE_MODULE_ID>.default.so
LOCAL_MODULE_RELATIVE_PATH := hw            // /system/lib/hw
LOCAL_C_INCLUDES := hardware/libhardware    //標頭檔案位置
LOCAL_SRC_FILES := led_hal.c                //對應的C檔案
LOCAL_SHARED_LIBRARIES := liblog            //用的這個庫來列印除錯資訊
LOCAL_MODULE_TAGS := eng                    //開發版本

include $(BUILD_SHARED_LIBRARY)

三、上傳編譯


JNI: 重新上傳

frameworks/base/services/core/jni/com_android_server_LedService.cpp

HAL: led_hal.h

        led_hal.c

把新檔案上傳到伺服器, 所在目錄:

hardware/libhardware/include/hardware/led_hal.h

hardware/libhardware/modules/led/led_hal.c

hardware/libhardware/modules/led/Android.mk


編譯:

$ mmm frameworks/base/services

$ mmm hardware/libhardware/modules/led

$ make snod

$ ./gen-img.sh

四、程式碼下載:

以上涉及程式碼下載地址:

基於HAL的Android硬體訪問服務操作LED基於tiny4412