一、什么是HAL层? 为什么需要用到HAL层?可以参考一下连接
答:https://blog.youkuaiyun.com/shift_wwx/article/details/49000305
2、由内核底层思考,当我们写好了驱动怎么向上提供给HAL层?
首先当我写好了对应的驱动模块,生成驱动文件和设备节点文件。之后怎么去使用上安卓的HAL?
开始之前我们看看 android/hardware/libhardware/include/hardware/hardware.h 文件 谷歌给出的定义和里面所包含的说明,看看里面具体说些什么?
摘要:
/ * 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 {
…
}hw_module_t;
/** * Every device data structure must begin with hw_device_t * followed by module specific public methods and attributes. */
typedef struct hw_device_t {
…
} hw_device_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;
目前只关注里面的三个结构体
hardware.h里面包含了三个结构体。这三个结构体就是传递设备节点操作的关键。
在写HAL层的代码关键的点是,一定要按照对应的HAL指定格式。
表明三个结构体:
struct hw_module_t //模块类型
struct hw_module_methods_t //模块方法
struct hw_device_t //向上提供设备接口
原型-模块结构
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;
原型 -模块方法结构
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;
原型-设备模块(上报的设备模块)
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;
实现HAL层的大致步骤:
a、首先定义一个模块名字必须是 HAL_MODULE_INFO_SYM,它的第一个成员必须是 struct hw_module_t 类型。
b、填充 struct hw_module_t 自身的成员,特别关注其中的成员 struct hw_module_methods_t* methods;
c. 实例化 struct hw_module_methods_t* methods ,初始化里面的函数指针(服务函数)
d、自定义上层硬件接口结构,其中这个结构的第一个成员必须是struct hw_device_t 类型,在结构里面定义其他成员
e、在第c步实例的服务函数里面。 将我们d步自定义上层硬件接口的结构进行实例化,获取对应的句柄
f. 最后获取到的硬件接口结构句柄地址,给服务函数的形参hw_device_t**
①/硬件模块结构体/
在编写HAL硬件模块的时候,在hardware.h说明中要求每一个硬件模块的名字必须是 HAL_MODULE_INFO_SYM
并且这个结构体第一个成员必须是 struct hw_module_t 结构体成员,如:
/**
*** 按照规定实例化出来一个名字必须为 HAL_MODULE_INFO_SYM 的模块
*** 并且这个结构体第一个成员必须是 struct hw_module_t 结构体成员
*** struct hw_module_t commen具体成员说明在hardware.h 有说明
**/
typedef struct Test_module_t {
struct hw_module_t commen
}
typedef struct Test_module_t HAL_MODULE_INFO_SYM {
common : {
tag : HARDWARE_MODULE_TAG,
version_major : 1,
version_minor : 0,
id : "test2",
name : "test2",
author : "javalong",
methods : &Test_method /* 重点关注这个对象 */
}
}
**
②实现上面 hw_module_t commen结构里面的 methods 对象,为(struct hw_module_methods_t )类型的。 在实现methods对象的过程中发现,他还是一个结构体,里面成员为函数指针 (open)(const struct hw_module_t module, const char* id, struct hw_device_t** device),那么先将对应的函数 指针实现,在回充到methods对象,如:
**
static int Test_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {
/* .... */
/* ... */
}
struct hw_module_methods_t Test_method = {
open: Test_device_open
};
③上面的服务函数内容还没有编写,单看函数名字知道是一个模块方法。难道我们期待已久的HAL上向层提供函数的地方?失望的是并没有那么简单,
上面的 Test_device_open()函数仅仅是一个模块初始化的作用,真正要向上层提供的是一个自定义结硬件接口结构体,但是要求该硬件接口结构体
的第一个成员必须是 struct hw_device_t ,其他成员可以自定义,如:
/*硬件接口结构体*/
struct Text_device_t {
struct hw_device_t common;
//以下成员是HAL对上层提供的接口或一些属性自行定义
int fd;
int (*set_val)(struct Text_device_t* dev, int val);
int (*get_val)(struct Text_device_t* dev, int* val);
};
int Test_set_val(struct Text_device_t* dev, int val) {LOGI("hgello set_val\n"); return 0};
int Test_get_val(struct Text_device_t* dev, int* val) {LOGI("hgello get_val\n"); return 0};
④所以将上面的服务函数内容进行编写,在Test_device_open()进行模块初始化的时候将自定义硬件接口结构体实现,真正向HAL层提供一个操作
接口,让上层的函数可以真正地访问到内核驱动。
int Test_device_close(struct he_device_t * device) {LOGI("hgello Test_device_close \n"); close(dev->fd) ;return 0; }***
static int Test_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {
struct Test_device_t * dev;
dev = (struct Test_device_t*)malloc(sizeof(struct Test_device_t));
if(!dev) { LOGI("request memory founder \n"); return -1; }
memset(dev, 0, sizeof(struct Test_device_t));
dev->commen.tag = HARDWARE_DEVICE_TAG;
dev->commen.version = 0;
dev->commen.module = (hw_module_t*)module;
dev->commen.close = Test_device_close;
//用户自定义的属性
dev->set_val = Test_set_val;
dev->get_val = Test_get_val;
dev->fd = open("/dev/DevName","O_RDWR");
if(dev->fd < 0) { LOGI("open deviceFile founder :exit \n"); free(dev); return -1;}
*device = &(dev->commen);
return 0
}
问题一:在①中为什么实例出来的模块名字必须是 HAL_MODULE_INFO_SYM ?
回到hardware.h文件下查看到这是一个宏
#define HAL_MODULE_INFO_SYM HMI 其中
hmi = (structhw_module_t *)dlsym(handle, sym)
这里是查找“HMI”这个导出符号,并获取其地址。不禁要问,为什么根据“HMI”这个
导出符号, 就可以从动态链接库中找到结构体hw_module_t呢?在 UNIX下
ELF = Executable and Linkable Format,
可执行连接格式, 是UNIX系统实验室(USL)作为应用程序二进制接口 而开发和
发 布的, 扩展名为elf。一个ELF头在文件的开始,保存了路线图(road map),
描 述 了该文件的组织情况。sections 保存着object 文件的信息,从连接 角
度看:包括指令,数据,符号表,重定位信息等等。随意找到一个 xxx.default
.so 的elf文件。
可使用 < readelf xxx.default.so -s >查看随意的xxx.default.so文
件 会发现21行的符号信息是HMI。通过查找可以找到我们所做好的动态库 所以这
里HAL_MODULE_INFO_SYM变量必须为这个名字这样编译器才会将 这个结构体的导
出符号变为“HMI”。
这样这个结构体才能被dlsym 函数找到!知道了andriod HAL模块也有一个通
用的入口地址,这个入口地址就 是HAL_MODULE_INFO_SYM变量,通过它,我们可
以访问到HAL模块中的所有想要外部访问到的方法。
问题二、在③为什么第一个成员必须是hw_device_t 类型的?
在hardware.h头文件中,还有明确要求定义xxx_module_t类型时,明确要求第一个成员变量类型必须
为hw_module_t,在参考:
作者:罗升阳
原文:https://blog.youkuaiyun.com/luoshengyang/article/details/6575988
/*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/
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);
}
/*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/
static jboolean hello_init(JNIEnv* env, jclass clazz) {
hello_module_t* module;
LOGI("Hello JNI: initializing......");
if(hw_get_module(HELLO_HARDWARE_MODULE_ID, \
(const struct hw_module_t**)&module) == 0) {
LOGI("Hello JNI: hello Stub found.");
if(hello_device_open(&(module->common), &hello_device) \
== 0) {
LOGI("Hello JNI: hello device is open.");
return 0;
}
LOGE("Hello JNI: failed to open hello device.");
return -1;
}
LOGE("Hello JNI: failed to get hello stub module.");
return -1;
}
里面写了,在.h文件里面可以获取对应的自定义结构,在自定义结构里面填充的只
有&(dev->commen),明显看见在static inline int hello_device_open()
这个函数中,返回值打开第三个参数是用自定义结构体强转成struct hw_device_t
这就要求(struct hw_device_t **)必须是第一个成员,否则在上层进行强转
是找不到(dev->commen),这个强转是没有意义的,
。
问题三:在①中为什么第一个成员必须是 hw_module_t?
上面代码有这一段
if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0)
主要关注第三个形参,HAL_MODULE_INFO_SYM 成员 hw_module_t为什么强调第一
个成员是 hw_module_t ,那么在使用hw_get_module获取的时候就没有意义了。
hw_get_module是有HAL层提供给安卓框架层获取整个模块函数,通过JNI调用。
想一下在我们的④里面一个上报问题,为什么我仅仅上报
*device = &(dev->commen);
他是怎么得到我整个自定义硬件接口结构体(Text_device_t),
这里就是答案,因为他将整个模块加载了,里面自然包含了自定义硬件结构体
(Text_device_t)。