立即注册 登录
一牛网 返回首页

gygyg2008的个人空间 http://bbs.16rd.com/?8691 [收藏] [复制] [RSS]

日志

Android中HAL如何向上层提供接口总结

热度 5已有 3905 次阅读2016-5-27 17:00 |个人分类:HAL|系统分类:嵌入式论坛

1 什么是HAL

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 想要把这个问题处理好也是不容易的。

&nBSP;   总结下来,Android HAL存在的原因主要有:

    1. 并不是所有的硬件设备都有标准的linux kernel的接口

    2. KERNEL DRIVER涉及到GPL的版权。某些设备制造商并不原因公开硬件驱动,所以才去用HAL方式绕过GPL。

    3. 针对某些硬件,Android有一些特殊的需求.

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

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

这几个数据结构是在Android工作目录/hardware/libhardware/include/hardware/hardware.h文件中定义.3 解释

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
22
23
24
25
26
27
28
29
30
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*
 * Copyright (C) 2008 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.
 */
 
#ifndef ANDROID_INCLUDE_HARDWARE_HARDWARE_H
#define ANDROID_INCLUDE_HARDWARE_HARDWARE_H
 
#include <stdint.h>
#include <sys/cdefs.h>
 
#include <cutils/native_handle.h>
#include <system/graphics.h>
 
__BEGIN_DECLS
 
/*
 * Value for the hw_module_t.tag field
 */
 
#define MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D))
 
#define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T')
#define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T')
 
struct hw_module_t;
struct hw_module_methods_t;
struct hw_device_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.
 */
//每一个硬件模块都每必须有一个名为HAL_MODULE_INFO_SYM的数据结构变量,它的第一个成员的类型必须为hw_module_t
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 */
    //模块方法列表,指向hw_module_methods_t*
    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;
 
typedef struct hw_module_methods_t {                 //硬件模块方法列表的定义,这里只定义了一个open函数
    /** Open a specific device */
    int (*open)(const struct hw_module_t* module, const char* id, //注意这个open函数明确指出第三个参数的类型为struct hw_device_t**
            struct hw_device_t** device);
} hw_module_methods_t;
 
/**
 * Every device data structure must begin with hw_device_t
 * followed by module specific public methods and attributes.
 */
//每一个设备数据结构的第一个成员函数必须是hw_device_t类型,其次才是各个公共方法和属性
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;
 
/**
 * Name of the hal_module_info
 */
#define HAL_MODULE_INFO_SYM         HMI
 
/**
 * Name of the hal_module_info as a string
 */
#define HAL_MODULE_INFO_SYM_AS_STR  "HMI"
 
/**
 * Get the module info associated with a module by id.
 *
 * @return: 0 == success, <0 == error and *module == null
 */
int hw_get_module(const char *id, const struct hw_module_t **module);
 
/**
 * Get the module info associated with a module instance by class 'class_id'
 * and instance 'inst'.
 *
 * Some modules types necessitate multiple instances. For example audio supports
 * multiple concurrent interfaces and thus 'audio' is the module class
 * and 'primary' or 'a2dp' are module interfaces. This implies that the files
 * providing these modules would be named audio.primary.<variant>.so and
 * audio.a2dp.<variant>.so
 *
 * @return: 0 == success, <0 == error and *module == NULL
 */
int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module);
 
__END_DECLS
 
#endif  /* ANDROID_INCLUDE_HARDWARE_HARDWARE_H */

由以上内容可以看出(typedef struct hw_module_t ,typedef struct hw_device_t),如果我们要写一个自定义设备的驱动的HAL层时,我们得首先自定义两个数据结构: 

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

在头文件中定义:XXX.h

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

注:特别注意xxx_device_t的结构定义,这个才是HAL向上层提供接口函数的数据结构,其成员就是我们想要关心的接口函数.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
/*模块实例变量*/
struct xxx_module_t HAL_MODULE_INFO_SYM = {    //变量名必须为HAL_MODULE_INFO_SYM,这是强制要求的,你要写Android的HAL就得遵循这个游戏规则,
                                               //见hardware.h中的hw_module_t的类型信息说明.
        common: {
        tag: HARDWARE_MODULE_TAG,
        version_major: 1,
        version_minor: 0,
        id: XXX_HARDWARE_MODULE_ID,    //头文件中有定义
        name: MODULE_NAME,
        author: MODULE_AUTHOR,
        methods: &xxx_module_methods,  //模块方法列表,在本地定义
    }
};

注意到上面有HAL_MODULE_INFO_SYM变量的成员common中包含一个函数列表xxx_module_methods,而这个成员函数列表是在本地自定义的。那么这个成员函数列是不是就是HAL向上层提供函数的地方呢?很失望,不是在这里,前面我们已经说过了,是在 xxx_device_t中定义的,这个xxx_module_methods实际上只提供了一个open函数,就相当于只提供了一个模块初始化函数.其定义如下:
1
2
3
4
/*模块方法表*/
static struct hw_module_methods_t xxx_module_methods = {
    open: xxx_device_open
};

注意到,上边的函数列表中只列出了一个xxx_device_open函数,这个函数也是需要在本地实现的一个函数。前面说过,这个函数只相当于模块初始化函数。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static int xxx_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {
    struct xxx_device_t* dev;
    dev = (struct hello_device_t*)malloc(sizeof(struct xxx_device_t));//动态分配空间
     
    if(!dev) {
        LOGE("Hello Stub: failed to alloc space");
        return -EFAULT;
    }
 
    memset(dev, 0, sizeof(struct xxx_device_t));
        //对dev->common的内容赋值,
        dev->common.tag = HARDWARE_DEVICE_TAG;
    dev->common.version = 0;
    dev->common.module = (hw_module_t*)module;
    dev->common.close = xxx_device_close;
        //对dev其它成员赋值
        dev->set_val = xxx_set_val;
    dev->get_val = xxx_get_val;
 
    if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
        LOGE("Hello Stub: failed to open /dev/hello -- %s.", strerror(errno));
        free(dev);
        return -EFAULT;
    }
         
        //输出&(dev->common),输出的并不是dev,而是&(dev->common)!(common内不是只包含了一个close接口吗?)
    *device = &(dev->common);
    LOGI("Hello Stub: open /dev/hello successfully.");
 
    return 0;
}

经验告诉我们,一般在进行模块初始化的时候,模块的接口函数也会“注册”,上面是模块初始化函数,那么接口注册在哪?于是我们找到*device =&(dev->common);这行代码,可问题是,这样一来,返回给调用者不是&(dev->common)吗?而这个 dev->common仅仅只包含了一个模块关闭接口!到底怎么回事?为什么不直接返回dev,dev下不是提供所有HAL向上层接口吗?

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

1
2
3
4
5
6
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;

这是方法列表的定义,明确要求了方法列表中有且只一个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
3
4
/*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/ 
static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) { 
     return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device); 
}

由此可见,返回的&(dev->common)最终会返回给struce hello_device_t **类型的输出变量device,换句话说,类型为hw_device_t的dev->common在初始化函数open返回后,会强制转化为 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函数进行模块初始化.


综上所述,HAL是通过struct xxx_device_t这个结构体向上层提供接口的.

即:接口包含在struct xxx_device_t这个结构体内。

而具体执行是通过struct xxx_module_t HAL_MODULE_INFO_SYM这个结构体变量的函数列表成员下的open函数来返回给上层的.

文章出处:http://blog.csdn.net/flydream0/article/details/7086273

2

路过

鸡蛋
2

鲜花

握手

雷人

刚表态过的朋友 (4 人)

发表评论 评论 (2 个评论)

回复 沧浪之水163 2016-9-13 16:33
写的很详细,以前一直没细看,这次理解的透彻了
回复 Mr刘 2016-11-15 11:19
专业开发硬件,软件,手机APP,云服务,研发工程师有300多名。希望有合作的机会,联系电话:181-3829-8019  QQ:2992127947  刘经理       

facelist

您需要登录后才可以评论 登录 | 立即注册

联系我们|小黑屋