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: Android、iOS和HarmonyOS都是基于分架构设计的。这种设计方式将应用程序、系统服务和硬件分别抽象成不同的次,每一都有自己的职责和功能,以实现更好的模块化和可维护性。 在Android和HarmonyOS中,还存在一个中间,即HAL(Hardware Abstraction Layer,硬件抽象),用于将底硬件与上应用程序和系统服务解耦,以提高系统的可移植性和兼容性。 具体来说,Android和HarmonyOS的架构设计如下: 1. 应用程序:这是最上的应用程序,包括各种应用软件、游戏等,通过应用程序框架与下交互。这一主要负责应用程序的展示、交互和逻辑处理等。 2. 应用程序框架:这一主要提供应用程序所需的各种服务和功能,包括窗口管理、视图系统、通知管理、多媒体等。应用程序通过这一与下交互。 3. 系统服务:这一主要提供系统级的服务和功能,包括网络管理、安全管理、设备管理、数据存储等。应用程序和应用程序框架通过这一与底交互。 4. HAL:这一主要负责将底硬件抽象出来,提供标准的接口给上使用。不同的硬件厂商可以实现自己的HAL,以适配不同的硬件设备。 5. 驱动程序:这是最底硬件驱动程序,负责与硬件设备直接交互。 在iOS中,也采用了类似的分架构设计,但具体实现细节略有不同。 总之,分架构设计可以使系统更加模块化和可维护,同时也提高了系统的可移植性和兼容性。通过HAL抽象,不同的硬件厂商可以更方便地适配自己的硬件设备。 ### 回答2: Android、iOS和Harmony都采用了分架构设计,将应用程序、系统服务和硬件进行了抽象,以便实现平台的灵活性和可扩展性。 在Android架构中,分为四个主要次:应用、应用框架、系统运行库和Linux内核。应用是用户直接接触的次,包括了各种应用程序,如通讯、娱乐等。应用框架提供了一系列的服务和API供应用调用,例如界面管理、用户认证等。系统运行库包括了许多核心库和虚拟机,用于执行应用程序。最底是Linux内核,负责提供底硬件操作和进程管理等功能。 iOS架构也分为四个主要次:应用、应用框架、核心服务和内核。应用是用户使用的应用程序,应用框架包括了各种框架和API供应用调用。核心服务包括了各种核心服务和库,如多媒体处理、网络通信等。内核由Mach微内核和BSD组成,负责提供底硬件操作和进程管理。 Harmony架构也遵循类似的分思想,包括了应用、应用框架、系统服务和内核。应用是应用程序的集合,应用框架提供了一系列的框架和API供应用调用。系统服务包括了各种系统服务和管理功能。内核负责底硬件操作和进程管理。 在Android和Harmony的架构中,还存在硬件抽象HAL)。HAL是一个中间,用于抽象出系统和硬件之间的接口,使得应用程序和系统服务可以独立于具体的硬件平台。HAL包括了一系列的硬件抽象接口,供系统服务调用。这样一来,系统服务可以不用关心具体的硬件细节,而只需与HAL进行交互,从而实现了跨硬件平台的兼容性。 总之,Android、iOS和Harmony的架构设计都采用了分的思想,将应用程序、系统服务和硬件进行了抽象,以实现平台的灵活性和可扩展性。同时,Android和Harmony还引入了硬件抽象,使得系统服务可以与具体的硬件平台解耦,提高了跨平台的兼容性。 ### 回答3: Android、iOS和HarmonyOS(鸿蒙)是三种常见的移动操作系统,它们都采用了分架构设计。下面将详细介绍这些系统的架构及其各个次的组成。 Android架构设计分为五个主要次:应用程序、应用程序框架、系统运行库、Linux内核和硬件抽象HAL)。 应用程序是用户直接与之交互的次,包括各种应用程序,例如浏览器、短信、电话等。每个应用程序都运行在自己的进程中,并通过应用程序框架与系统进行交互。 应用程序框架提供了各种API(应用程序接口)、服务和组件,用于支持应用程序开发。例如,Activity Manager管理应用程序的生命周期,Content Provider提供数据的共享访问等。 系统运行库提供了一系列的库,用于支持Java核心库和Android运行库,并为开发者提供了丰富的功能。例如,图形库、数据库库、网络库等。 Linux内核是整个系统的核心,负责管理内存、进程、设备驱动等底功能。 硬件抽象HAL)是中间,它提供了对底硬件设备的抽象接口,为上提供统一的硬件访问方式。HAL可以兼容不同的硬件平台,并将其抽象为统一的接口,使上的应用程序可以在不同设备上运行。 与Android类似,HarmonyOS也采用了类似的分架构。它的架构由四个主要次组成:应用程序框架、核心服务、驱动能力硬件表现。 应用程序框架提供了开发应用程序所需的组件、API以及能力框架。 核心服务包括系统服务和应用程序服务,例如分布式数据管理、安全认证、分布式能力管理等。 驱动能力是与硬件相关的次,提供硬件设备的驱动和能力支持。 硬件表现是与特定硬件平台相关的次,提供底硬件的访问和控制能力。 总之,Android、iOS和HarmonyOS都采用了分架构设计,将应用程序、系统服务和硬件分别抽象出不同的次,以提供更好的开发和扩展能力。而HAL则作为一个中间,提供统一的硬件访问接口,使应用程序能够在不同的硬件平台上运行。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值