Chinaunix首页 | 论坛 | 博客
  • 博客访问: 165965
  • 博文数量: 47
  • 博客积分: 1032
  • 博客等级: 少尉
  • 技术积分: 759
  • 用 户 组: 普通用户
  • 注册时间: 2011-01-19 15:47
文章分类
文章存档

2012年(26)

2011年(21)

分类: 嵌入式

2012-04-01 16:31:54

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. 针对某些硬件,Android有一些特殊的需求.

2 与接口相关的几个结构体

首先来看三个与HAL对上层接口有关的几个结构体:

  1. struct hw_module_t;              //模块类型  
  2. struct hw_module_methods_t;      //模块方法  
  3. struct hw_device_t;              //设备类型  
复制代码
这几个数据结构是在Android工作目录/hardware/libhardware/include/hardware/hardware.h文件中定义.3 解释

一般来说,在写HAL相关代码时都得包含这个hardware.h头文件,所以有必要先了解一下这个头文件中的内容.

  1. /* 
  2. * Copyright (C) 2008 The Android Open Source Project 

  3. * Licensed under the Apache License, Version 2.0 (the "License"); 
  4. * you may not use this file except in compliance with the License. 
  5. * You may obtain a copy of the License at 

  6. *      

  7. * Unless required by applicable law or agreed to in writing, software 
  8. * distributed under the License is distributed on an "AS IS" BASIS, 
  9. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  10. * See the License for the specific language governing permissions and 
  11. * limitations under the License. 
  12. */  
  13.   
  14. #ifndef ANDROID_INCLUDE_HARDWARE_HARDWARE_H  
  15. #define ANDROID_INCLUDE_HARDWARE_HARDWARE_H  
  16.   
  17. #include   
  18. #include   
  19.   
  20. #include   
  21. #include   
  22.   
  23. __BEGIN_DECLS  
  24.   
  25. /* 
  26. * Value for the hw_module_t.tag field 
  27. */  
  28.   
  29. #define MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D))  
  30.   
  31. #define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T')  
  32. #define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T')  
  33.   
  34. struct hw_module_t;  
  35. struct hw_module_methods_t;  
  36. struct hw_device_t;  
  37.   
  38. /** 
  39. * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM 
  40. * and the fields of this data structure must begin with hw_module_t 
  41. * followed by module specific information. 
  42. */  
  43. //每一个硬件模块都每必须有一个名为HAL_MODULE_INFO_SYM的数据结构变量,它的第一个成员的类型必须为hw_module_t  
  44. typedef struct hw_module_t {  
  45.     /** tag must be initialized to HARDWARE_MODULE_TAG */  
  46.     uint32_t tag;  
  47.   
  48.     /** major version number for the module */  
  49.     uint16_t version_major;  
  50.   
  51.     /** minor version number of the module */  
  52.     uint16_t version_minor;  
  53.   
  54.     /** Identifier of module */  
  55.     const char *id;  
  56.   
  57.     /** Name of this module */  
  58.     const char *name;  
  59.   
  60.     /** Author/owner/implementor of the module */  
  61.     const char *author;  
  62.   
  63.     /** Modules methods */  
  64.     //模块方法列表,指向hw_module_methods_t*  
  65.     struct hw_module_methods_t* methods;  
  66.   
  67.     /** module's dso */  
  68.     void* dso;  
  69.   
  70.     /** padding to 128 bytes, reserved for future use */  
  71.     uint32_t reserved[32-7];  
  72.   
  73. } hw_module_t;  
  74.   
  75. typedef struct hw_module_methods_t {                 //硬件模块方法列表的定义,这里只定义了一个open函数  
  76.     /** Open a specific device */  
  77.     int (*open)(const struct hw_module_t* module, const char* id, //注意这个open函数明确指出第三个参数的类型为struct hw_device_t**  
  78.             struct hw_device_t** device);  
  79. } hw_module_methods_t;  
  80.   
  81. /** 
  82. * Every device data structure must begin with hw_device_t 
  83. * followed by module specific public methods and attributes. 
  84. */  
  85. //每一个设备数据结构的第一个成员函数必须是hw_device_t类型,其次才是各个公共方法和属性  
  86. typedef struct hw_device_t {  
  87.     /** tag must be initialized to HARDWARE_DEVICE_TAG */  
  88.     uint32_t tag;  
  89.   
  90.     /** version number for hw_device_t */  
  91.     uint32_t version;  
  92.   
  93.     /** reference to the module this device belongs to */  
  94.     struct hw_module_t* module;  
  95.   
  96.     /** padding reserved for future use */  
  97.     uint32_t reserved[12];  
  98.   
  99.     /** Close this device */  
  100.     int (*close)(struct hw_device_t* device);  
  101.   
  102. } hw_device_t;  
  103.   
  104. /** 
  105. * Name of the hal_module_info 
  106. */  
  107. #define HAL_MODULE_INFO_SYM         HMI  
  108.   
  109. /** 
  110. * Name of the hal_module_info as a string 
  111. */  
  112. #define HAL_MODULE_INFO_SYM_AS_STR  "HMI"  
  113.   
  114. /** 
  115. * Get the module info associated with a module by id. 

  116. * @return: 0 == success, <0 == error and *module == NULL 
  117. */  
  118. int hw_get_module(const char *id, const struct hw_module_t **module);  
  119.   
  120. /** 
  121. * Get the module info associated with a module instance by class 'class_id' 
  122. * and instance 'inst'. 

  123. * Some modules types necessitate multiple instances. For example audio supports 
  124. * multiple concurrent interfaces and thus 'audio' is the module class 
  125. * and 'primary' or 'a2dp' are module interfaces. This implies that the files 
  126. * providing these modules would be named audio.primary..so and 
  127. * audio.a2dp..so 

  128. * @return: 0 == success, <0 == error and *module == NULL 
  129. */  
  130. int hw_get_module_by_class(const char *class_id, const char *inst,  
  131.                            const struct hw_module_t **module);  
  132.   
  133. __END_DECLS  
  134.   
  135. #endif  /* ANDROID_INCLUDE_HARDWARE_HARDWARE_H */  
复制代码
由以上内容可以看出(typedef struct hw_module_t ,typedef struct hw_device_t),如果我们要写一个自定义设备的驱动的HAL层时,我们得首先自定义两个数据结构:

假设我们要做的设备名为XXX:

在头文件中定义:XXX.h

  1. /*定义模块ID*/  
  2. #define XXX_HARDWARE_MODULE_ID "XXX"  
  3.   
  4. /*硬件模块结构体*/  
  5. //见hardware.h中的hw_module_t定义的说明,xxx_module_t的第一个成员必须是hw_module_t类型,其次才是模块的一此相关信息,当然也可以不定义,  
  6. //这里就没有定义模块相关信息  
  7. struct xxx_module_t {  
  8.     struct hw_module_t common;  
  9. };  
  10.   
  11. /*硬件接口结构体*/  
  12. //见hardware.h中的hw_device_t的说明,要求自定义xxx_device_t的第一个成员必须是hw_device_t类型,其次才是其它的一些接口信息.   
  13. struct xxx_device_t {  
  14.     struct hw_device_t common;  
  15.         //以下成员是HAL对上层提供的接口或一些属性  
  16.         int fd;  
  17.     int (*set_val)(struct xxx_device_t* dev, int val);  
  18.     int (*get_val)(struct xxx_device_t* dev, int* val);  
  19. };  
复制代码
注:特别注意xxx_device_t的结构定义,这个才是HAL向上层提供接口函数的数据结构,其成员就是我们想要关心的接口函数.

接下来我们在实现文件XXX.c文件中定义一个xxx_module_t的变量:

  1. /*模块实例变量*/  
  2. struct xxx_module_t HAL_MODULE_INFO_SYM = {    //变量名必须为HAL_MODULE_INFO_SYM,这是强制要求的,你要写Android的HAL就得遵循这个游戏规则,  
  3.                                                //见hardware.h中的hw_module_t的类型信息说明.  
  4.         common: {  
  5.         tag: HARDWARE_MODULE_TAG,  
  6.         version_major: 1,  
  7.         version_minor: 0,  
  8.         id: XXX_HARDWARE_MODULE_ID,    //头文件中有定义  
  9.         name: MODULE_NAME,  
  10.         author: MODULE_AUTHOR,  
  11.         methods: &xxx_module_methods,  //模块方法列表,在本地定义  
  12.     }  
  13. };  
复制代码
注意到上面有HAL_MODULE_INFO_SYM变量的成员common中包含一个函数列表xxx_module_methods,而这个成员函数列表是在本地自定义的。那么这个成员函数列是不是就是HAL向上层提供函数的地方呢?很失望,不是在这里,前面我们已经说过了,是在xxx_device_t中定义的,这个xxx_module_methods实际上只提供了一个open函数,就相当于只提供了一个模块初始化函数.其定义如下:
  1. /*模块方法表*/  
  2. static struct hw_module_methods_t xxx_module_methods = {  
  3.     open: xxx_device_open  
  4. };  
复制代码
注意到,上边的函数列表中只列出了一个xxx_device_open函数,这个函数也是需要在本地实现的一个函数。前面说过,这个函数只相当于模块初始化函数。

那么HAL又到底是怎么将xxx_device_t中定义的接口提供到上层去的呢?

且看上面这个函数列表中唯一的一个xxx_device_open的定义:

  1. static int xxx_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {  
  2.     struct xxx_device_t* dev;  
  3.     dev = (struct hello_device_t*)malloc(sizeof(struct xxx_device_t));//动态分配空间  
  4.       
  5.     if(!dev) {  
  6.         LOGE("Hello Stub: failed to alloc space");  
  7.         return -EFAULT;  
  8.     }  
  9.   
  10.     memset(dev, 0, sizeof(struct xxx_device_t));  
  11.         //对dev->common的内容赋值,  
  12.         dev->common.tag = HARDWARE_DEVICE_TAG;  
  13.     dev->common.version = 0;  
  14.     dev->common.module = (hw_module_t*)module;  
  15.     dev->common.close = xxx_device_close;  
  16.         //对dev其它成员赋值  
  17.         dev->set_val = xxx_set_val;  
  18.     dev->get_val = xxx_get_val;  
  19.   
  20.     if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {  
  21.         LOGE("Hello Stub: failed to open /dev/hello -- %s.", strerror(errno));  
  22.         free(dev);  
  23.         return -EFAULT;  
  24.     }  
  25.           
  26.         //输出&(dev->common),输出的并不是dev,而是&(dev->common)!(common内不是只包含了一个close接口吗?)  
  27.     *device = &(dev->common);  
  28.     LOGI("Hello Stub: open /dev/hello successfully.");  
  29.   
  30.     return 0;  
  31. }  
复制代码
经验告诉我们,一般在进行模块初始化的时候,模块的接口函数也会“注册”,上面是模块初始化函数,那么接口注册在哪?于是我们找到*device =&(dev->common);这行代码,可问题是,这样一来,返回给调用者不是&(dev->common)吗?而这个dev->common仅仅只包含了一个模块关闭接口!到底怎么回事?为什么不直接返回dev,dev下不是提供所有HAL向上层接口吗?

在回答上述问题之前,让我们先看一下这xxx_device_open函数原型,还是在hardware.h头文件中,找到下面几行代码:

  1. typedef struct hw_module_methods_t {  
  2.     /** Open a specific device */  
  3.     int (*open)(const struct hw_module_t* module, const char* id,  
  4.             struct hw_device_t** device);  
  5.   
  6. } hw_module_methods_t;  
复制代码
这是方法列表的定义,明确要求了方法列表中有且只一个open方法,即相当于模块初始化方法,且,这个方法的第三个参数明确指明了类型是struct hw_device_t **,而不是用户自定义的xxx_device_t,这也就是解译了在open函数实现内为什么输出的是&(dev->common)而不是dev了,原来返回的类型在hardware.h中的open函数原型中明确指出只能返回hw_device_t类型.

可是,dev->common不是只包含close接口吗?做为HAL的上层,它又是怎么"看得到"HAL提供的全部接口的呢?

接下来,让我们来看看做为HAL上层,它又是怎么来应层由HAL返回的dev->common的:

参考: 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口 这篇文章,从中可以看到这么几行代码:

  1. /*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/    
  2. static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) {    
  3.      return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);    
  4. }   
复制代码
由此可见,返回的&(dev->common)最终会返回给struce hello_device_t **类型的输出变量device,换句话说,类型为hw_device_t的dev->common在初始化函数ipen返回后,会强制转化为xxx_device_t来使用,终于明白了,原来如此!另外,在hardware.h中对xxx_device_t类型有说明,要求它的第一个成员的类型必须是hw_device_t,原来是为了HAL上层使用时的强制转化的目的,如果xxx_device_t的第一个成员类型不是hw_device_t,那么HAL上层使用中强制转化就没有意义了,这个时候,就真的“看不到”HAL提供的接口了.

此外,在hardware.h头文件中,还有明确要求定义xxx_module_t类型时,明确要求第一个成员变量类型必须为hw_module_t,这也是为了方便找到其第一个成员变量common,进而找到本地定义的方法列表,从而调用open函数进行模块初始化.



/****************************Android HAL 是如何被调用的*******************************/

Android 对硬件的调用, google 推荐使用 HAL 的方式进行调用,对于 Andriod HAL 的写法,可以参考 android 源码里的 hardware 目录下几个模块的模版。

在看 HAL 的编写方法的过程中,会发现整个模块貌似没有一个入口。一般说来模块都要有个入口,比如应用程序有 main 函数,可以为加载器进行加载执行, dll 文件有 dllmain ,而对于我们自己写的动态链接库,我们可以对库中导出的任何符号进行调用。

问题来了, Android 中的 HAL 是比较具有通用性的,需要上层的函数对其进行加载调用, Android 的 HAL 加载器是如何实现对不同的 Hardware Module 进行通用性的调用的呢?

带着这个疑问查看 Android 源码,会发现 Android 中实现调用 HAL 是通过 hw_get_module 实现的。

int hw_get_module(const char *id, const struct hw_module_t **module);

这是其函数原型, id 会指定 Hardware 的 id ,这是一个字符串,比如 sensor 的 id 是

#define SENSORS_HARDWARE_MODULE_ID "sensors" ,如果找到了对应的 hw_module_t 结构体,会将其指针放入 *module 中。看看它的实现。。。。

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

    for (i=0 ; i

        if (i < HAL_VARIANT_KEYS_COUNT) {

             // 获取 ro.hardware/ro.product.board/ro.board.platform/ro.arch 等 key 的值。

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

                continue;

            }

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

                    HAL_LIBRARY_PATH, id, prop);

              // 如果开发板叫做 mmdroid, 那么这里的 path 就是 system/lib/hw/sensor.mmdroid.so

        } else {

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

                    HAL_LIBRARY_PATH, id);// 默认会加载 /system/lib/hw/sensor.default.so

 

         }

        if (access(path, R_OK)) {

            continue;

        }

        /* we found a library matching this id/variant */

        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(id, path, module);// 调用 load 函数打开动态链接库

    } 
 
  
 
  
 

获取了动态链接库的路径之后,就会调用 load 函数打开它,下面会打开它。

 

奥秘在 load 中

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 ”

    hmi = (struct hw_module_t *)dlsym(handle, sym);// 查找“ HMI ”这个导出符号,并获取其地址

    if (hmi == NULL) {

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

        status = -EINVAL;

        goto done;

    }

 

/* Check that the id matches */

// 找到了 hw_module_t 结构!!!

    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;


 

从上面的代码中,会发现一个很奇怪的宏 HAL_MODULE_INFO_SYM_AS_STR ,它直接被定义为了 #define HAL_MODULE_INFO_SYM_AS_STR  "HMI" ,为何根据它就能从动态链接库中找到这个 hw_module_t 结构体呢?我们查看一下我们用到的 hal 对应的 so 就可以了,在 linux 中可以使用 readelf XX.so –s 查看。

Symbol table '.dynsym' contains 28 entries:

   Num:     Value  Size Type    Bind   Vis      Ndx Name

     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND

     1: 00000594     0 SECTION LOCAL  DEFAULT    7

     2: 00001104     0 SECTION LOCAL  DEFAULT   13

     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND ioctl

     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND strerror

     5: 00000b84     0 NOTYPE  GLOBAL DEFAULT  ABS __exidx_end

     6: 00000000     0 OBJECT  GLOBAL DEFAULT  UND __stack_chk_guard

     7: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_unwind_cpp_pr0

     8: 00000000     0 FUNC    GLOBAL DEFAULT  UND __errno

     9: 00001188     0 NOTYPE  GLOBAL DEFAULT  ABS _bss_end__

    10: 00000000     0 FUNC    GLOBAL DEFAULT  UND malloc

    11: 00001188     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start__

    12: 00000000     0 FUNC    GLOBAL DEFAULT  UND __android_log_print

    13: 00000b3a     0 NOTYPE  GLOBAL DEFAULT  ABS __exidx_start

    14: 00000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail

    15: 00001188     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_end__

    16: 00001188     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start

    17: 00000000     0 FUNC    GLOBAL DEFAULT  UND memset

    18: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_uidiv

    19: 00001188     0 NOTYPE  GLOBAL DEFAULT  ABS __end__

    20: 00001188     0 NOTYPE  GLOBAL DEFAULT  ABS _edata

    21: 00001188     0 NOTYPE  GLOBAL DEFAULT  ABS _end

    22: 00000000     0 FUNC    GLOBAL DEFAULT  UND open

    23: 00080000     0 NOTYPE  GLOBAL DEFAULT  ABS _stack

    24: 00001104   128 OBJECT  GLOBAL DEFAULT   13 HMI

    25: 00001104     0 NOTYPE  GLOBAL DEFAULT   13 __data_start

    26: 00000000     0 FUNC    GLOBAL DEFAULT  UND close

    27: 00000000     0 FUNC    GLOBAL DEFAULT  UND free 
 

从上面中,第 24 个符号,名字就是“ HMI ”,对应于 hw_module_t 结构体。再去对照一下 HAL 的代码。

/*

  * The COPYBIT Module

  */

struct copybit_module_t HAL_MODULE_INFO_SYM = {

    common: {

        tag: HARDWARE_MODULE_TAG,

        version_major: 1,

        version_minor: 0,

        id: COPYBIT_HARDWARE_MODULE_ID,

        name: "QCT MSM7K COPYBIT Module",

        author: "Google, Inc.",

        methods: ©bit_module_methods

    }

}; 
 

这里定义了一个名为 HAL_MODULE_INFO_SYM 的 copybit_module_t 的结构体, common 成员为 hw_module_t 类型。注意这里的 HAL_MODULE_INFO_SYM 变量必须为这个名字,这样编译器才会将这个结构体的导出符号变为“ HMI ”,这样这个结构体才能被 dlsym 函数找到!

综上,我们知道了 andriod HAL 模块也有一个通用的入口地址,这个入口地址就是 HAL_MODULE_INFO_SYM 变量,通过它,我们可以访问到 HAL 模块中的所有想要外部访问到的方法。





阅读(1805) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~