HAL so库加载机制---之二

4, HAL库加载

4.1 HAL库加载过程

下面以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的调用流程图如下,

HAL so库加载机制---之二_第1张图片

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的指针。

4.2 gps库加载

在第二章论述过,HAL库必须,

1,实现hw_module_t 结构体;

2,提供open方法来获取hw_device_t结构体

4.2.1 结构体

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。

4.2.2 open方法

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结构体。

4.2.3 GpsInterface

在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机制操作这些方法了。

你可能感兴趣的:(---【gps框架分析】)