Android Hal 分析

   轉自:

http://www.cnblogs.com/armlinux/archive/2012/01/14/2396768.html

                                                                            Android Hal 分析

                                                                                                 -------rockchip  Andy

       本文是基於android4.0.3.對應其他低版本的代碼,可能有所差異,但基本大同小異。

      Android的HAL是爲了保護一些硬件提供商的知識產權而提出的,是爲了避開linux的GPL束縛。思路是把控制硬件的動作都放到了Android HAL中,而linux driver僅僅完成一些簡單的數據交互作用,甚至把硬件寄存器空間直接映射到user space。而Android是基於Aparch的license,因此硬件廠商可以只提供二進制代碼,所以說Android只是一個開放的平臺,並不是 一個開源的平臺。也許也正是因爲Android不遵從GPL,所以Greg Kroah-Hartman纔在2.6.33內核將Andorid驅動從linux中刪除。GPL和硬件廠商目前還是有着無法彌合的裂痕。Android 想要把這個問題處理好也是不容易的。

    總結下來,Android HAL存在的原因主要有:

    1. 並不是所有的硬件設備都有標準的linux kernel的接口

    2. KERNEL DRIVER涉及到GPL的版權。某些設備製造商並不原因公開硬件驅動,所以纔去用HAL方 式繞過GPL。

    3. 針對某些硬件,An有一些特殊的需求

現有HAL架構由Patrick Brady (Google) 在2008 Google  I/O演講中提出的,如下圖:

 

 

一、       源碼位置

/hardware/libhardware_legacy/ - 舊的架構、採取鏈接庫模塊的方式

/hardware/libhardware     新架構、調整爲 HAL stub 目錄的結構如下:

/hardware/libhardware/hardware.c  編譯成libhardware.s置於/system/lib

/hardware/libhardware/include/hardware目錄下包含如下頭文件:

hardware.h                             通用硬件模塊頭文件

copybit.h                              copybit模塊頭文件

gralloc.h                              gralloc模塊頭文件

lights.h                              背光模塊頭文件

overlay.h                              overlay模塊頭文件

qemud.h                               qemud模塊頭文件

sensors.h                              傳感器模塊頭文件

/hardware/libhardware/modules  目錄下定義了很多硬件模塊

/hardware/msm7k  /hardware/qcom  /hardware/ti  /device/Samsung 

/device/moto            各個廠商平臺相關的hal

這些硬件模塊都編譯成xxx.xxx.so,目標位置爲/system/lib/hw目錄

 

二、       HAL層的實現方式

目前HAL存在兩種構架,位於libhardware_legacy目錄下的“舊HAL架構”和位於libhardware目錄下的“新HAL架構”。兩種框架如下圖所示:

 

libhardware_legacy 是將 *.so 文件當作shared library來使用,在runtime(JNI 部份)以 direct function call 使用 HAL module。通過直接函數調用的方式,來操作驅動程序。當然,應用程序也可以不需要通過 JNI 的方式進行,直接加載 *.so (dlopen)的做法調用*.so 裏的符號(symbol)也是一種方式。總而言之是沒有經過封裝,上層可以直接操作硬件。

    現在的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,可以認爲Android定義了HAL層結構框架,通過幾個接口訪問硬件從而統一了調用方式。

Android的HAL的實現需要通過JNI(Java Native Interface),JNI簡單來說就是java程序可以調用C/C++寫的動態鏈接庫,這樣的話,HAL可以使用C/C++語言編寫,效率更高。 JNI->通用硬件模塊->硬件模塊->內核驅動接口,具體一 點:JNI->libhardware.so->xxx.xxx.so->kernel,具體來說:android frameworks中JNI調用hardware.c中定義的hw_get_module函數來獲取硬件模塊,然後調用硬件模塊中的方法,硬件模塊中的 方法直接調用內核接口完成相關功能

在Android下訪問HAL大致有以下兩種方式:

   (1)Android的app可以直接通過service調用.so格式的jni

     

  (2)經過Manager調用service

    

 

上面兩種方法應該說是各有優缺點,第一種方法簡單高效,但不正規。第二種方法實現起來比較複雜,但更符合目前的Android框架。第二種方法中,LegManager和LedService(java)在兩個進程中,需要通過進程通訊的方式來通訊。

在現在的android框架中,這兩種方式都存在,比如對於lights,是直接透過LightsService調用JNI,而對於sensor,中間則是通過SensorsManager

來調用JNI的。

 

1、通用硬件模塊(libhardware.so)

    一般來說HAL moudle需要涉及的是三個關鍵結構體:

struct hw_module_t;   

struct hw_module_methods_t;

struct hw_device_t;

這三個結構體定義在hardware.h中。

(1)頭文件爲:/hardware/libhardware/include/hardware/hardware.h

頭文件中主要定義了通用硬件模塊結構體hw_module_t,聲明瞭JNI調用的接口函數hw_get_module、hw_module_t定義如下:

/**

* Every hardware module must have a data structure named HAL_MODULE_INFO_SYM

 * and the fields of this data structure must begin with hw_module_t

 * followed by module specific information.

 */

typedef struct hw_module_t {

/** tag must be initialized to HARDWARE_MODULE_TAG */

uint32_t tag;

 

/** major version number for the module */

uint16_t version_major;

 

/** minor version number of the module */
      uint16_t version_minor;

 

/** 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;

 

/** padding to 128 bytes, reserved for future use */

uint32_t reserved[32-7];

} hw_module_t;

 

如註釋所說,所有的hal模塊都要有一個以HAL_MODULE_INFO_SYM命名的結構,而且這個結構要以hw_module_t開始,即要繼承hw_module_t這個結構,比如lights,sensor:

struct sensors_module_t {

 struct hw_module_t common;

int (*get_sensors_list)(struct sensors_module_t* module,

            struct sensor_t const** list);

};

/*

 * The lights Module

 */

struct light_module_t HAL_MODULE_INFO_SYM = {

    common: {

        tag: HARDWARE_MODULE_TAG,

        version_major: 1,

        version_minor: 0,

        id: LIGHTS_HARDWARE_MODULE_ID,

        name: "Lights module",

        author: "Rockchip",

        methods: &light_module_methods,

    }

};

const struct sensors_module_t HAL_MODULE_INFO_SYM = {

    .common = {

        .tag = HARDWARE_MODULE_TAG,

        .version_major = 1,

        .version_minor = 0,

        .id = SENSORS_HARDWARE_MODULE_ID,

        .name = "Stingray SENSORS Module",

        .author = "Motorola",

        .methods = &sensors_module_methods,

    },

    .get_sensors_list = sensors__get_sensors_list

};

hw_module_t中比較重要的是硬件模塊方法結構體hw_module_methods_t定義如下:

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;

該方法在定義HAL_MODULE_INFO_SYM的時候被初始化。目前該結構中只定義了一個open方法,其中調用的設備結構體參數hw_device_t定義如下:
        /**

 * Every device data structure must begin with hw_device_t

 * followed by module specific public methods and attributes.

 */

typedef struct hw_device_t {

    /** tag must be initialized to HARDWARE_DEVICE_TAG */

    uint32_t tag;

 

    /** version number for hw_device_t */

    uint32_t version;

 

    /** reference to the module this device belongs to */

    struct hw_module_t* module;

 

    /** padding reserved for future use */

    uint32_t reserved[12];

 

    /** Close this device */

    int (*close)(struct hw_device_t* device);

 

} hw_device_t;

struct light_device_t {

    struct hw_device_t common;

    int (*set_light)(struct light_device_t* dev,

            struct light_state_t const* state);

};

/**

 * Every device data structure must begin with hw_device_t

 * followed by module specific public methods and attributes.

 */

struct sensors_poll_device_t {

    struct hw_device_t common;

    int (*activate)(struct sensors_poll_device_t *dev,

            int handle, int enabled);

    int (*setDelay)(struct sensors_poll_device_t *dev,

            int handle, int64_t ns);

    int (*poll)(struct sensors_poll_device_t *dev,

            sensors_event_t* data, int count);

};

 

亦如註釋所說,每一個設備的數據結構都必須也以hw_device_t開始。hw_get_module函數聲明如下:
    int hw_get_module(const char *id, const struct hw_module_t **module);
參數id爲模塊標識,定義在/hardware/libhardware/include/hardware錄下的硬件模塊頭文件中,參數module是 硬件模塊地址,定義在/hardware/libhardware/include/hardware/hardware.h中

下面以lights模塊爲例進行分析:

     在lights.h中定義有lights模塊的ID

     #define LIGHTS_HARDWARE_MODULE_ID "lights"

     在JNI層會通過hw_ge_module()方法獲得對應的模塊,對於lights,在

frameworks/base/services/jni/com_android_server_LightsService.cpp的init_native方法中,代碼如下:

static jint init_native(JNIEnv *env, jobject clazz)

{

    int err;

    hw_module_t* module;

    Devices* devices;

    devices = (Devices*)malloc(sizeof(Devices));

 err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);

    if (err == 0) {

        devices->lights[LIGHT_INDEX_BACKLIGHT]

                = get_device(module, LIGHT_ID_BACKLIGHT);

………………………………………….

}

 hw_get_module函數在hardware.c中實現:

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時如何實現的:

首先在hardware.c的開始有如下定義和註釋:

/** Base path of the hal modules */

#define HAL_LIBRARY_PATH1 "/system/lib/hw"

#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"

 

/**

 * There are a set of variant filename for modules. The form of the filename

 * is "<MODULE_ID>.variant.so" so for the led module the Dream variants

 * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:

 *

 * led.trout.so

 * led.msm7k.so

 * led.ARMV6.so

 * led.default.so

 */

 

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"

};

 

static const int HAL_VARIANT_KEYS_COUNT =

    (sizeof(variant_keys)/sizeof(variant_keys[0]));

 

int hw_get_module_by_class(const char *class_id, const char *inst,

                           const struct hw_module_t **module)

{

    int status;

    int i;

    const struct hw_module_t *hmi = NULL;

    char prop[PATH_MAX];

    char path[PATH_MAX];

    char name[PATH_MAX];

 

    if (inst)

        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);

    else

        strlcpy(name, class_id, PATH_MAX);

 

    /*

     * 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.

     */

 

    /* Loop through the configuration variants looking for a module */

    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {

        if (i < HAL_VARIANT_KEYS_COUNT) {

            if (property_get(variant_keys[i], prop, NULL) == 0) {

                continue;

            }

            snprintf(path, sizeof(path), "%s/%s.%s.so",

                     HAL_LIBRARY_PATH2, name, prop);

            if (access(path, R_OK) == 0) break;

 

            snprintf(path, sizeof(path), "%s/%s.%s.so",

                     HAL_LIBRARY_PATH1, name, prop);

            if (access(path, R_OK) == 0) break;

        } else {

            snprintf(path, sizeof(path), "%s/%s.default.so",

                     HAL_LIBRARY_PATH1, name);

            if (access(path, R_OK) == 0) break;

        }

    }

 

    status = -ENOENT;

    if (i < HAL_VARIANT_KEYS_COUNT+1) {

        /* load the module, if this fails, we're doomed, and we should not try

         * to load a different variant. */

        status = load(class_id, path, module);

    }

 

    return status;

}

可以看到,在hw_get_module_by_class函數中,先通過property_get獲得varient_key中定義的系統屬性, 如果系統中有定義該屬性,就會獲得一個模塊名.屬性名組成的一個so的名稱,然後去定義兩個patch(/system/lib/hw、/vendor /lib/hw)下查看,該so是否存在,如果存在,幾回調用load函數,打開.so.例如在rockchip的rk29平臺上,有定義 ro.product.board = rk29sdk,在這裏會得到lights.rk29sdk.so。

 再看load函數的實現:

  /**

 * Load the file defined by the variant and if successful

 * return the dlopen handle and the hmi.

 * @return 0 = success, !0 = failure.

 */

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();

        LOGE("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) {

        LOGE("load: couldn't find symbol %s", sym);

        status = -EINVAL;

        goto done;

    }

 

    /* Check that the id matches */

    if (strcmp(id, hmi->id) != 0) {

        LOGE("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 {

        LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",

                id, path, *pHmi, handle);

    }

 

    *pHmi = hmi;

 

    return status;

}

   在這裏會打開對應了so,比如lights.rk29sdk.so,然後獲得這個模塊中定義的hw_module_t的地址。後面JNI就能通過這個接口和hal層進行溝通了。

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章