分类: LINUX
2011-05-20 15:35:44
谨以此文纪念过往的岁月
一.前言
在Android中采用HAL来实现硬件的抽象,在本文中来记录鄙人的一些学习过程,如有不对之处请指教。仅仅是理解HAL的底层,并不去尝试理解JAVA层。
二.HAL的三大结构体
2.1 hw_device_t
对于每一个设备数据结构体都必须以该结构体开头,该结构体是所有设备的引导线,在该结构体之后才是每一个设备自己的数据。
typedef struct hw_device_t {
uint32_t tag; --必须初始化为HARDWARE_DEVICE_TAG
uint32_t version; --hw_device_t版本号
struct hw_module_t* module; --指向该设备所属的module
uint32_t reserved[12]; --保留
int (*close)(struct hw_device_t* device); --关闭设备
} hw_device_t;
2.2 hw_module_methods_t
该结构体仅提供一个打开特殊设备的opne函数的接口。传入参数为hw_module_t,id和hw_device_t,该函数是用于初始化hw_device_t 的各成员。
typedef struct hw_module_methods_t {
int (*open)(const struct * module, const char* id,
structhw_device_t** device);
} hw_module_methods_t;
2.3 hw_module_t
每一个硬件模块的必须申明一个HAL_MODULE_INFO_SYM的数据结构体,该结构体的第一个成员必须是以hw_module_t为数据的结构体。
typedef struct {
uint32_t tag; --必须初始化HARDWARE_MODULE_TAG
uint16_t version_major;
uint16_t version_minor;
const char *id; --模块标示符
const char *name; --该模块的name
const char *author;
struct hw_module_methods_t* methods; --模块的方法
void* dso;
uint32_t reserved[32-7]; --保留
} hw_module_t;
我们以网上很有名的led的HAL来看上面的东东。
三.HAL之Led
struct led_module_t {
struct hw_module_t common; --在此成员之后可以添加该module的其余操作
} ;
struct led_control_device_t {
struct hw_device_t
common; -- hw_device_t必须是为第一个成员。
/* supporting control APIs go here */
int ( * set_on) ( struct led_control_device_t * dev, int32_t led) ;
int ( * set_off) ( struct led_control_device_t * dev, int32_t led) ;
} ;
struct {
struct led_control_device_t device;
};
static int led_device_open( const struct hw_module_t* module, const char * name, struct hw_device_t* * device)
{
struct led_control_device_t * dev;
dev = ( struct led_control_device_t
* ) malloc ( sizeof ( * dev) ) ;
memset ( dev, 0, sizeof ( * dev) ) ;
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = module;
dev->common.close = led_device_close;
dev->set_on = led_on;
dev->set_off = led_off;
* device = &dev->common;
success:
return 0;
}
该module操作集仅有一个函数open。
static struct hw_module_methods_t led_module_methods = {
open : led_device_open
} ;
定义一个以HAL_MODULE_INFO_SYM为名的module结构体
const struct led_module_t = {
common: {
tag: HARDWARE_MODULE_TAG, --必须如此初始化,亦如1add1=2。
version_major: 1,
version_minor: 0,
id: ,
name: "Sample LED Stub" ,
author: "The Mokoid Open Source Project" ,
methods: & led_module_methods, --定义该module的操作集
}
/* supporting APIs go here */
};
也许大家会疑惑上面的函数在被制作成.so库后,如何调用。我们并不涉及JAVA层的种种,在此仅讲述如何在C++中调用使用的。
四.Led之C++调用
void led_open(const struct hw_module_t *module,struct led_control_device_t **led_device)
{
return module->methods->open(module,NULL,(strcut hw_device_t **)led_device);
}
void led_test(void)
{
Hw_module_t const module;
(LED_HARDWARE_MODULE_ID, &module);
led_control_device _t *led_device;
(module,&led_device);
led_device->set_on();
}
从上面的调用中也应该知道其实led_device仅仅提供了一个指针,其所指向的实体在open中开辟空间。其实这里实现了一个强制类型转换,将具体的设备数据结构体强制转换成hw_device_t结构体,这也可以从此来理解为什么hw_device_t必须定义为具体device结构体的第一个成员。将底层的操作通过该种方式来实现屏蔽。那我们来看动态链接库是如何被加载的。
#define HAL_MODULE_INFO_SYM HMI --还记否所有的设备的module名都必须声明为该值。
#define HAL_MODULE_INFO_SYM_AS_STR "HMI"
hw_get_module->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;
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;
}
const char *sym =;
hmi = (struct hw_module_t *)(handle, sym); --获取动态库中HMI的结构体
if (hmi == NULL) {
status = -EINVAL;
goto done;
}
if (strcmp(id, hmi->id) != 0) {
status = -EINVAL;
goto done;
}
hmi->dso = handle;
status = 0;
done:
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
}
*pHmi = hmi;
return status;
}
到此各位看官应该明白了在Android hardware中一些不同平台的硬件的HAL流程了吧。通过一个公用的HMI的结构体名来实现了对底层的屏蔽。这里面涉及一个很重要的概念就是所有的硬件设备抽象层都必须要有共同点,那就是hw_module_t和hw_device_t均是不同设备数据结构体的第一个成员,唯有第一个成员才能实现类型的强制转换。
五.总结
Nothing !!!