Android架构分析之硬件抽象层(HAL)

0 篇文章 0 订阅

一、分析HAL module 架构

Android硬件抽象层有三个核心数据结构,分别是hw_module_t , hw_module_methods_t, hw_device_t。定义在hardware/libhardware/include/hardware/hardware.h文件中:

1、hw_module_t:

typedef struct hw_module_t {
    /** tag must be initialized to HARDWARE_MODULE_TAG */
    uint32_t tag;
    uint16_t module_api_version;
#define version_major module_api_version
    uint16_t hal_api_version;
#define version_minor hal_api_version
    const char *id;
    const char *name;
    const char *author;
    struct hw_module_methods_t* methods;
    void* dso;
#ifdef __LP64__
    uint64_t reserved[32-7];
#else
    uint32_t reserved[32-7];
#endif

} hw_module_t;

需要注意的:

  1. 每个硬件模块都必须有一个名为 HAL_MODULE_INFO_SYM 的数据结构,该数据结构的第一个成员变量必须以hw_module_t 开头,作为模块特定的信息。
  2. HAL_MODULE_INFO_SYM 指向一个自定义硬件抽象层模块结构体
  3. tag 的值必须为 HARDWARE_MODULE_TAG,用来标志这是一个硬件抽象层模块结构体。
  4. dso 用来保存加载硬件抽象层模块后得到的句柄值。因为每一个硬件抽象层模块都对应以一个动态链接库文件。加载硬件抽象层的过程实际就是调用 dlopen 来加载对应的硬件动态链接库的过程。对应于调用 dlclose 的时候需要卸载对应的硬件抽象层需要用到这个句柄,所以要将其保存到这个成员变量中。
  5. hw_module_methods_t  定义了硬件抽象层模块的操作方法列表。

        硬件抽象层HAL由一个一个的模块组成,Android规定,每一个模块都是一个命名为HAL_MODULE_INFO_SYM的自定义结构体,并且该结构体的第一个成员必须为hw_module_t类型的变量,其它成员变量根据需要由开发者设置。

2、hw_module_methods_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;

一个指针函数用来打开硬件抽象层模块中的硬件设备。

  1. module:硬件设备所在的模块
  2. id:要打开硬件设备对应的ID。因为硬件抽象层中可能有多个设备,所以打开时候需要制定ID。
  3. device:输出参数,用来描述一个已经打开的硬件设备。

3、hw_device_t : 

typedef struct hw_device_t {
    /** tag must be initialized to HARDWARE_DEVICE_TAG */
    uint32_t tag;
    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;
  1.  每个设备对应一个自定义结构体,该结构体的第一个成员必须为 hw_device_t。
  2. tag 必须是 HARDWARE_DEVICE_TAG
  3. close 是一个指针函数,用来关闭硬件设备

Example:

sensor 模块对应的结构体定义在hardware/libhardware/include/hardware/sensors.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.
 */
struct sensors_module_t {
    struct hw_module_t common;

    /**
     * Enumerate all available sensors. The list is returned in "list".
     * @return number of sensors in the list
     */
    int (*get_sensors_list)(struct sensors_module_t* module,
            struct sensor_t const** list);

    /**
     *  Place the module in a specific mode. The following modes are defined
     *
     *  0 - Normal operation. Default state of the module.
     *  1 - Loopback mode. Data is injected for the supported
     *      sensors by the sensor service in this mode.
     * @return 0 on success
     *         -EINVAL if requested mode is not supported
     *         -EPERM if operation is not allowed
     */
    int (*set_operation_mode)(unsigned int mode);
};

sensor设备对应的结构体如下:

/*
 * sensors_poll_device_t is used with SENSORS_DEVICE_API_VERSION_0_1
 * and is present for backward binary and source compatibility.
 * See the Sensors HAL interface section for complete descriptions of the
 * following functions:
 * http://source.android.com/devices/sensors/index.html#hal
 */
struct sensors_poll_device_t {
    struct hw_device_t common;
    int (*activate)(struct sensors_poll_device_t *dev,
            int sensor_handle, int enabled);
    int (*setDelay)(struct sensors_poll_device_t *dev,
            int sensor_handle, int64_t sampling_period_ns);
    int (*poll)(struct sensors_poll_device_t *dev,
            sensors_event_t* data, int count);
};

例如三星:对应的 /device/samsung/manta/libsensors/sensors.cpp文件对其具体实现: 

static struct hw_module_methods_t sensors_module_methods = {
        open: open_sensors
};

struct sensors_module_t HAL_MODULE_INFO_SYM = {
        common: {
                tag: HARDWARE_MODULE_TAG,
                version_major: 1,
                version_minor: 0,
                id: SENSORS_HARDWARE_MODULE_ID,
                name: "Samsung Sensor module",
                author: "Samsung Electronic Company",
                methods: &sensors_module_methods,
                dso: 0,
                reserved: {},
        },
        get_sensors_list: sensors__get_sensors_list,
};

struct sensors_poll_context_t {
    struct sensors_poll_device_t device; // must be first

        sensors_poll_context_t();
        ~sensors_poll_context_t();
    int activate(int handle, int enabled);
    int setDelay(int handle, int64_t ns);
    int pollEvents(sensors_event_t* data, int count);

    // Will return true if the constructor completed
    bool isValid() { return mInitialized; };

private:
   
    ...
};

在对应的文件中实现了对应的 结构体中的成员方法。


二、Android如何使用硬件抽象层(hardware.c)

硬件抽象层的作用是对上层Application Framework屏蔽Linux底层驱动程序,那么Application Framework与硬件抽象层通信的接口是谁呢?答案是hw_get_module函数,该函数定义在hardware/libhardware/hardware.c文件中:

int hw_get_module(const char *id, const struct hw_module_t **module)
{
    return hw_get_module_by_class(id, NULL, module);
}
int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module)
{
    int status;
    int i;
    const struct hw_module_t *hmi = NULL;
    char prop[PATH_MAX];
    char path[PATH_MAX];
    char name[PATH_MAX];

    if (inst)
        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
    else
        strlcpy(name, class_id, PATH_MAX);

    /*
     * Here we rely on the fact that calling dlopen multiple times on
     * the same .so will simply increment a refcount (and not load
     * a new copy of the library).
     * We also assume that dlopen() is thread-safe.
     */

    /* Loop through the configuration variants looking for a module */
    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
        if (i < HAL_VARIANT_KEYS_COUNT) {
            if (property_get(variant_keys[i], prop, NULL) == 0) {
                continue;
            }
            snprintf(path, sizeof(path), "%s/%s.%s.so",
                     HAL_LIBRARY_PATH2, name, prop);
            if (access(path, R_OK) == 0) break;

            snprintf(path, sizeof(path), "%s/%s.%s.so",
                     HAL_LIBRARY_PATH1, name, prop);
            if (access(path, R_OK) == 0) break;
        } else {
            snprintf(path, sizeof(path), "%s/%s.default.so",
                     HAL_LIBRARY_PATH2, name);
            if (access(path, R_OK) == 0) break;

            snprintf(path, sizeof(path), "%s/%s.default.so",
                     HAL_LIBRARY_PATH1, name);
            if (access(path, R_OK) == 0) break;
        }
    }

    status = -ENOENT;
    if (i < HAL_VARIANT_KEYS_COUNT+1) {
        /* load the module, if this fails, we're doomed, and we should not try
         * to load a different variant. */
        status = load(class_id, path, module);
    }

    return status;
}

        hw_get_module 函数的作用是由第一个参数id指定的模块ID,找到模块对应的 hw_module_t 结构体,保存在第二个参数 module中。

        首先通过for循环获取模块名及路径,保存在path中。循环次数为 HAL_VARIANT_KEYS_COUNT 次,HAL_VARIANT_KEYS_COUNT 是下面要用到的 variant_keys 数组的数组元素个数。

        为了说明这个for循环是如何获得模块名及其路径,我们要先来看一下variant_keys数组的定义:

/**
 * There are a set of variant filename for modules. The form of the filename
 * is "<MODULE_ID>.variant.so" so for the led module the Dream variants 
 * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
 *
 * led.trout.so
 * led.msm7k.so
 * led.ARMV6.so
 * led.default.so
 */

static const char *variant_keys[] = {
    "ro.hardware",  /* This goes first so that it can pick up a different
                       file on the emulator. */
    "ro.product.board",
    "ro.board.platform",
    "ro.arch"
};

static const int HAL_VARIANT_KEYS_COUNT =
    (sizeof(variant_keys)/sizeof(variant_keys[0]));

上面的注释说明了 module文件的命名规则 <MODULE_ID>.variant.so 

1、MODULE_ID是模块对应的ID ,不同模块对应一个唯一固定的ID。

2、那么variant是什么呢?又怎么获得variant呢?这就跟下面的variant_keys数组有关了。

         定义的variant_keys[] 有四个值 "ro.hardware" 、"ro.product.board"、"ro.board.platform"、"ro.arch" 的四个字符串的指针,   可以把这些值理解为属性,系统会对这些值进行属性的set 。

        ro.hardware”属性的属性值是在系统启动时由init进程负责设置的。它首先会读取/proc/cmdline文件,检查里面有没有一个名为androidboot.hardware的属性,如果有,就把它的值赋值给“ro.hardware”,否则,就将/proc/cpuinfo文件的内容读取出来,并解析出Haredware字段的内容赋值给“ro.hardware”。例如在Android模拟器中,从/proc/cpuinfo文件中读取出来的Hardware字段内容为goldfish,于是,init进程就会将 “ro.hardware” 属性设置为goldfish。

        “ ro.product.board”、“ ro.board.platform”、“ ro.arch”属性是从/system/build.prop文件读取出来的。/system/build.prop文件是由编译系统中的编译脚本build/core/Makefile和shell脚本build/tools/buildinfo.sh生成的,这里不再详细分析。

3、HAL_VARIANT_KEYS_COUNT变量,它是variant_keys数组的大小。

4、通过  snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH2, name, prop)找到 对应的so 文件路径  保存在path中。

5、if (access(path, R_OK) == 0) break; 通过access 来读取动态链接库是否存在。

6、如果没有找到variant_keys[i]对应的属性,则使用<MODULE_ID>.default.so

7、最后调用 load 方法来尝试打开在上面找到的so库,并获得hw_module_t 结构体。

/**
 * Load the file defined by the variant and if successful
 * return the dlopen handle and the hmi.
 * @return 0 = success, !0 = failure.
 */
static int load(const char *id,
        const char *path,
        const struct hw_module_t **pHmi)
{
    int status;
    void *handle;
    struct hw_module_t *hmi;

    /*
     * 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
     */
    handle = dlopen(path, RTLD_NOW);
    if (handle == NULL) {
        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 = (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 */
    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);
    }

    *pHmi = hmi;

    return status;
}

        通过dlopen 方法打开so文件 得到的handle句柄,保存在 hw_module_t 的 dso 中(方便我们后面调用 close 方法来卸载对应的硬件)。最后将 hw_module_t 结构赋值给传递进来的参数 pHmi,即返回给上层调用函数。

 

        分析到这里,我们可以看出,通过hw_get_module函数,Application Framework代码可以通过指定的模块ID找到模块hw_module_t结构体。有了hw_module_t结构体,就可以调用hw_module_t-> methods->open函数,在open函数中,完成对设备对应的hw_device_t结构体的初始化,并指定设备相关的自定义函数。

总结:

硬件抽象层加载一个模块无非就是

  • 调用者传目标模块的ID参数到加载函数 。
  • 获取目标模块的存储路径。
  • 检查该路径下是否有该模块的.so文件。
  • 加载模块至内存。
  • 将句柄返回给调用者。

参考文章:Android架构分析之硬件抽象层(HAL)

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值