下面以GPS为例,分析上层是如何使用HAL库来访问硬件设备的。
com_android_server_location_GpsLocationProvider.cpp中加载gps.default.so库的代码如下,
hw_module_t* module;
err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
hw_get_module的调用流程图如下,
gps.h 中定义的GPS_HARDWARE_MODULE_ID 如下,
#define GPS_HARDWARE_MODULE_ID "gps"
hardware.c文件中的hw_get_module如下,
int hw_get_module(const char *id, const struct hw_module_t **module)
{
return hw_get_module_by_class(id, NULL, module);
}
字符id表示HAL库的名字,是唯一的。
hw_module_t结构体指针指向对应的HAL库信息,这样在com_android_server_location_GpsLocationProvider.cpp中才可以调用HAL gps库的方法等。
hw_get_module_by_class方法主要代码如下,
for (i=0 ; i
首先利用variant_keys逐个查找名字,如果找到并且模块名对应的so库存在,则调用load方法加载。
否则利用default名查找对应的so库是否存在,存在就调用load方法加载。
variant_keys 字符数据和hw_module_exists方法在上一章已经论述了。
Load方法如下,
static int load(const char *id,const char *path,const struct hw_module_t **pHmi)
{
int status = -EINVAL;
void *handle = NULL;
struct hw_module_t *hmi = NULL;
/*
* 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
*/
//使用dlopen打开path定义的目标共享库,得到库文件的句柄handle
handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
//出错,通过dlerror获取错误信息
char const *err_str = dlerror();
ALOGE("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"
//使用dlsym找到符号为“HMI”的地址,这里应该是hw_module_t结构体的地址;并且赋给hmi
hmi = (struct hw_module_t *)dlsym(handle, sym);
if (hmi == NULL) {
ALOGE("load: couldn't find symbol %s", sym);
status = -EINVAL;
goto done;
}
/* Check that the id matches */
//检查模块id是否匹配
if (strcmp(id, hmi->id) != 0) {
ALOGE("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 {
ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
//返回得到的hw_module_t结构体的指针
*pHmi = hmi;
return status;
}
load函数的主要工作时通过dlopen来打开目标模块共享库,打开成功后,使用dlsym来得到符号名字为"HMI"的地址。
这里的HMI应该是模块定义的hw_module_t结构体的名字,如此,就得到了模块对应的hw_module_t的指针。
在第二章论述过,HAL库必须,
1,实现hw_module_t 结构体;
2,提供open方法来获取hw_device_t结构体
hardware/qcom/gps/loc_api/libloc_api_50001/gps.c的hw_module_t结构体如下,
struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = 1,
.hal_api_version = 0,
.id = GPS_HARDWARE_MODULE_ID,
.name = "loc_api GPS Module",
.author = "Qualcomm USA, Inc.",
.methods = &gps_module_methods, //自定义的函数指针,是获取hw_device_t的入口
};
gps_module_methods定义如下,
static struct hw_module_methods_t gps_module_methods = {
.open = open_gps
};
因此,gps对应的open方法就是open_gps。
com_android_server_location_GpsLocationProvider.cpp中调用gps.default.so库的open方法代码如下,
hw_device_t* device;
err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
if (err == 0) {
gps_device_t* gps_device = (gps_device_t *)device;
sGpsInterface = gps_device->get_gps_interface(gps_device);
}
open_gps方法如下,
static int open_gps(const struct hw_module_t* module, char const* name,
struct hw_device_t** device)
{
//为gps_device_t分配内存空间
struct gps_device_t *dev = (struct gps_device_t *) malloc(sizeof(struct gps_device_t));
if(dev == NULL)
return -1;
memset(dev, 0, sizeof(*dev));
//为gps_device_t的common成员变量赋值
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (struct hw_module_t*)module;
//通过gps__get_gps_interface函数就能得到GPS模块所有interface
dev->get_gps_interface = gps__get_gps_interface;
//将gps_device_t指针强转为hw_device_t指针,赋给device
*device = (struct hw_device_t*)dev;
return 0;
}
open_gps创建了gps_device_t结构体,初始化完成后,将其转为hw_device_t。
所以得到实际上是gps_device_t结构体指针。这里我们可以理解为gps_device_t是hw_device_t的子类,
将子类对象转为父类对象返回,是很正常的使用方法,因为C中无法继承。
gps.h 中gps_device_t结构体定义如下,
struct gps_device_t {
struct hw_device_t common;
const GpsInterface* (*get_gps_interface)(struct gps_device_t* dev);
};
所以HAL上层获取的hw_device_t结构体是指上是gps_device_t结构体。
在open_gps中,get_gps_interface被赋值为gps__get_gps_interface函数指针,其主要工作就是返回GPS模块的GpsInterface结构体指针。
hardware/libhardware/include/hardware/gps.h中GpsInterface结构体定义如下,
typedef struct {
size_t size;
int (*init)( GpsCallbacks* callbacks );
int (*start)( void );
int (*stop)( void );
void (*cleanup)( void );
int (*inject_time)(GpsUtcTime time, int64_t timeReference, int uncertainty);
int (*inject_location)(double latitude, double longitude, float accuracy);
void (*delete_aiding_data)(GpsAidingData flags);
int (*set_position_mode)(GpsPositionMode mode, GpsPositionRecurrence recurrence,
uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time);
const void* (*get_extension)(const char* name);
} GpsInterface;
GpsInterface定义了操作GPS模块的基本的标准接口,得到了GpsInterface就可以通过这些接口操作GPS了,
终于可以硬件打交道了。某一个具体的GPS模块会将GpsInterface中的接口初始化为其平台相关的具体实现。
在系统的hardware中在哪儿实现的呢?
hardware/qcom/gps/loc_api/libloc_api_50001/gps.c的gps__get_gps_interface方法如下,
const GpsInterface* gps__get_gps_interface(struct gps_device_t* dev)
{
return get_gps_interface();
}
hardware\qcom\gps\loc_api\libloc_api_50001中loc.cpp的get_gps_interface方法最后会返回sLocEngInterface,
static const GpsInterface sLocEngInterface =
{
sizeof(GpsInterface),
loc_init,
loc_start,
loc_stop,
loc_cleanup,
loc_inject_time,
loc_inject_location,
loc_delete_aiding_data,
loc_set_position_mode,
loc_get_extension
};
因此, sGpsInterface最后指向sLocEngInterface,上层获取的结构体是sLocEngInterface。
这样,上层就可以利用JNI机制操作这些方法了。